重寫UIView-讓我們隨心所欲的定製屬於自己的UI控件

回憶一下,這麼個場景。
我們在一個界面上,要排列多個相同的元素。你馬上就可以想到:
1.如果要用按鈕,可以使用來UIButton佈局。
如:

 

- (void) showSectionListbyCount:(float)totalCount everyColCount:(float)paramColCount{


    

    int curNum=1;

    

    int TotalRow= ceilf(totalCount/paramColCount);// 總行數

    

    for (int curRow=0; curRow

    

        for (int curCol=0; curCol// 列數

            

            if (totalCount

                return;

            }

            

            UIButton *btn=[UIButton buttonWithType:UIButtonTypeCustom];

            [btn setFrame:CGRectMake(10+(67+10)*curCol, (30+10)*curRow, 6730)];

            

            [btn setTitle:[NSString stringWithFormat:@"%d",curNum] forState:UIControlStateNormal];

            [btn setBackgroundImage:[UIImage imageNamed:@"btn_tv_n"]forState:UIControlStateNormal];

            [btn setBackgroundImage:[UIImage imageNamed:@"btn_tv_h"]forState:UIControlStateHighlighted];

            [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

            [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];

            [btn addTarget:self action:@selector(ClickControlAction:)forControlEvents:UIControlEventTouchUpInside];

            

            [btn setTag:curNum];

            

            [self.view addSubview:btn];


            curNum++;


            

        }

        

    }


}


如果,我們想用表格,那麼每個元素可以使用UITableViewCell來定義相近的項。


很簡單是吧,因爲,xCode,爲我們提供了這些UI控件供我們使用!所以,我們只需要用!


那麼,今天的問題也就來了!如果,我想搞一個能夠滿足自己的一些UI元素。別急,也就是這樣:

比如,我要搞這麼一個界面!




我想,首先可以肯定,蘋果不可能爲我量身定製了這麼一個控件。並且,如果,我用蘋果的默認控件來寫,那麼我肯定會崩潰的。。


也就是說,我們將每一項,寫成單獨的一個UI控件。那麼,可以根據數據進行填充!那就酷斃了!


繞了,一大圈,也就是要引用出來今天要學習的,東西!


自定義UI!!!


說起來,很牛X。其實很簡單,說白了,就是子類化(重寫)UIView


我們常常重寫,UIButton,UIImageView,UITableView 等等。因爲,可能我們用到的跟這些已有控件很相似,稍微修改一些就能夠滿足我們。


但是,出入大的時候,我們就不能簡單的繼承於一個已有控件了!

那就是完全重寫!就是子類化UIView


開始動手吧!!!


1.定義一UIView個的子類。

這個不解釋!


2.重寫drawRect方法。或者重寫layoutSubviews方法、


1. drawRect方法:


 

 

- (void)drawRect:(CGRect)rect

{

    // Drawing code

    for (int i=0; i<<span style="color: #2f2fd0">10; i++) {

        UIButton *btn=[UIButton buttonWithType:UIButtonTypeCustom];

        [btn setFrame:CGRectMake(1010+i*406730)];

        [btn setBackgroundImage:[UIImage imageNamed:@"btn_n"] forState:UIControlStateNormal];

        [btn setBackgroundImage:[UIImage imageNamed:@"btn_h"] forState:UIControlStateHighlighted];

        [btn addTarget:self action:@selector(ClickControlAction:)forControlEvents:UIControlEventTouchUpInside];

        [btn setTag:i];

        

        [self addSubview:btn];

    }

    

}


2. layoutSubviews 方法


 

- (void)layoutSubviews{

    

    [super layoutSubviews];

    

    [self setBackgroundColor:[UIColor clearColor]];


    [_imgPic setFrame:CGRectMake(7, 6, 86, 108)];

    [_imgPic setImage:[UIImage imageNamed:[self.dictData objectForKey:@"img_content"]]];

    [_imgPic.layer setShouldRasterize:NO];

    [_imgPic.layer setBorderColor: [[UIColor whiteColor] CGColor]];

    [_imgPic.layer setBorderWidth: 1.0];

    [[_imgPic layer] setShadowRadius:2];

    [[_imgPic layer] setShadowOpacity:1];

    [[_imgPic layer] setShadowColor:[UIColor blackColor].CGColor];

    [[_imgPic layer] setShadowOffset:CGSizeMake(0, 1)];

    [self addSubview:self.imgPic];

    


}


可見,這兩種方法都可以的。具體的區別,就是drawRect方法和layoutSubviews方法之間的區別了。


但是,經過測試,我發現,應該使用drawRect方法,來完成界面的繪製。因爲,如果我們使用的是layoutSubviews方法,那麼每一次改變frame的時候,layoutSubviews都將重新執行。顯然不科學。


在網上,我看到,大部分例子使用的是drawRect方法,來重繪界面。(繪製界面)

我使用了layoutSubviews方法。在frame改變時,會自動調用。(重定向子視圖)

根據需要選擇使用哪一種方法!


關於兩種方法,在網上搜了一下,總結如下:


 

layoutSubviews在以下情況下會被調用:

 

1、init初始化不會觸發layoutSubviews。

2、addSubview會觸發layoutSubviews。

3、設置view的Frame會觸發layoutSubviews,當然前提是frame的值設置前後發生了變化。

4、滾動一個UIScrollView會觸發layoutSubviews。

5、旋轉Screen會觸發父UIView上的layoutSubviews事件。

6、改變一個UIView大小的時候也會觸發父UIView上的layoutSubviews事件。

7、直接調用setLayoutSubviews。


drawRect在以下情況下會被調用:

 

1、如果在UIView初始化時沒有設置rect大小,將直接導致drawRect不被自動調用。drawRect 掉用是在Controller->loadView, Controller->viewDidLoad 兩方法之後掉用的.所以不用擔心在 控制器中,這些View的drawRect就開始畫了.這樣可以在控制器中設置一些值給View(如果這些View draw的時候需要用到某些變量 值).

2、該方法在調用sizeToFit後被調用,所以可以先調用sizeToFit計算出size。然後系統自動調用drawRect:方法。

3、通過設置contentMode屬性值爲UIViewContentModeRedraw。那麼將在每次設置或更改frame的時候自動調用drawRect:。

4、直接調用setNeedsDisplay,或者setNeedsDisplayInRect:觸發drawRect:,但是有個前提條件是rect不能爲0。

以上1,2推薦;而3,4不提倡


drawRect方法使用注意點:

 

1、 若使用UIView繪圖,只能在drawRect:方法中獲取相應的contextRef並繪圖。如果在其他方法中獲取將獲取到一個invalidate 的ref並且不能用於畫圖。drawRect:方法不能手動顯示調用,必須通過調用setNeedsDisplay 或 者 setNeedsDisplayInRect,讓系統自動調該方法。

2、若使用calayer繪圖,只能在drawInContext: 中(類似於drawRect)繪製,或者在delegate中的相應方法繪製。同樣也是調用setNeedDisplay等間接調用以上方法

3、若要實時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來掉用setNeedsDisplay實時刷新屏幕

 


3.自定義UIView的初始化方法。


我們常常在UIViewController中,看到initWithNibName方法:


 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // coding

    }

    return self;

}


這是xCode給我們定義好的初始化方法。


既然,我們要自定義UIView,那麼最好,根據自己的業務邏輯,來完成子類化UIView的初始化方法:


 

#pragma mark - Initialization 根據業務邏輯,傳入數據,初始化自己


- (id)initItemWithFram:(CGRect)frame andData:(NSMutableDictionary *)paramData{

    

    self = [super initWithFrame:frame];

    if (self) {

        // Initialization code

        _dictData=[[NSMutableDictionary alloc] init];

        _imgPic=[[UIImageView allocinit];

        _imgCover=[[UIImageView allocinit];

        _btnClick=[[UIButton allocinit];

        _btnDel=[[UIButton allocinit];

        _lblTips=[[UILabel allocinit];

        _lblName=[[UILabel allocinit];

        

    }

    self.dictData=paramData;

    return self;

    

    

}


+ (id) initItemWithFrame:(CGRect)frame andData:(NSMutableDictionary *)paramData{


    WTVTVCollectionItem *tmpInstance=[[WTVTVCollectionItem allocinitItemWithFram:frameandData:paramData];

    return tmpInstance;

}


這樣,就可以初始化:


 

NSMutableArray *items = [NSMutableArray array];

    

    [items addObject:[WTVTVCollectionItem initItemWithFrame:CGRectMake(1010100150)andData:dict]];

    [items addObject:[WTVTVCollectionItem initItemWithFrame:CGRectMake(1010100150)andData:dict]];

    [items addObject:[WTVTVCollectionItem initItemWithFrame:CGRectMake(1010100150)andData:dict]];


4.爲UIView加入一些動畫


動畫是UI操作的靈魂!給予動畫效果,就看上去有神了。


添加動畫,一般也用2種方法:


1.重寫UIView的方法,在調用時,進行動畫操作。


 

# pragma mark - 重寫父類的removeFromSuperview方法,在刪除時,使用相應的動畫效果


- (void) removeFromSuperview {

    

    [UIView animateWithDuration:0.2 animations:^{

        self.alpha = 0.0;

        [self setFrame:CGRectMake(self.frame.origin.x+50self.frame.origin.y+5000)];

        [_btnDel setFrame:CGRectMake(0000)];

    }completion:^(BOOL finished) {

        [super removeFromSuperview];

    }];

}


2.直接在UIView元素上,進行動畫對象的添加


 

 

- (void) enableEditing {

    

    if (self.isInEditingMode == YES)

        return;

    

    // 設置爲編輯狀態

    self.isInEditingMode = YES;

    [_btnDel setHidden:NO];

    

    // 開始晃動動畫

    CATransform3D transform;

    // 獲取0x-1之間的整數

    if (arc4random() % 2 == 1)

        transform = CATransform3DMakeRotation(-0.08001.0);

    else

        transform = CATransform3DMakeRotation(0.08001.0);

    

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];

    animation.toValue = [NSValue valueWithCATransform3D:transform];

    animation.autoreverses = YES;

    animation.duration = 0.1;

    animation.repeatCount = 10000;

    animation.delegate = self;

    [[self layeraddAnimation:animation forKey:@"wiggleAnimation"];


    [(WTVTVSprintBoard*)self.delegate enableEditingMode];

    

}


5.子類化UIView的交互


如何將操作鏈接起來!?就是說,在控件外,對我們自定義的控件進行操作?!


也有2種方法:


1.消息

2.代理


總結:


我想,我過多的將思路就可以了,因爲業務需求不同,控件的自定義需求也不同,所以,不能用具體來固定!但是,思路是想通的,知道了原理,我們就可以發揮我們的聰明智慧,創造出千變萬化的控件來!



希望對你有所幫助!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章