1.自定義控件的兩種方法

轉(zhuǎn)(https://www.cnblogs.com/ihojin/p/tableHeaderView-resizeheight.html)

使用純代碼的方式

  1. 一般來說我們的自定義類繼承自UIView激蹲,首先在initWithFrame:方法中將需要的子控件加入view中。注意涩馆,這里只是加入到view中详民,并沒有設(shè)置各個(gè)子控件的尺寸柒室。

    為什么要在initWithFrame:方法而不是在init方法?

    因?yàn)槭褂眉兇a的方式創(chuàng)建自定義類,在以后使用的時(shí)候可能使用init方法創(chuàng)建,也有可能使用initWithFrame:方法創(chuàng)建肴颊,但是無論哪種方式,最后都會調(diào)用到initWithFrame:方法渣磷。在這個(gè)方法中創(chuàng)建子控件婿着,可以保證無論哪種方式都可以成功創(chuàng)建。

    為什么要在initWithFrame:方法里面只是將子控件加到view而不設(shè)置尺寸醋界?

    前面已經(jīng)說過竟宋,兩種方式最后都會調(diào)用到initWithFrame:方法。如果使用init方法創(chuàng)建物独,那么這個(gè)view的frame有可能是不確定的:

    CYLView *view = [[CYLView alloc] init];
    view.frame = CGRectMake(0, 0, 100, 100);
    ...
    

    如果是這種情況袜硫,那么在init方法中氯葬,frame是不確定的挡篓,此時(shí)如果在initWithFrame:方法中設(shè)置尺寸,那么各個(gè)子控件的尺寸都會是0帚称,因?yàn)檫@個(gè)view的frame還沒有設(shè)置官研。(可以看到是在發(fā)送完init消息才設(shè)置的)

    所以我們應(yīng)該保證view的frame設(shè)置完才會設(shè)置它的子控件的尺寸。

  2. layoutSubviews方法中就可以達(dá)到這個(gè)目的闯睹。第一次view__將要顯示__的時(shí)候會調(diào)用這個(gè)方法戏羽,之后當(dāng)view的尺寸(不是位置)改變時(shí),會調(diào)用這個(gè)方法楼吃。

    所以正常的做法應(yīng)該是在initWithFrame:方法中創(chuàng)建子控件始花,注意此時(shí)子控件有可能只是一個(gè)局部變量,所以想要在layoutSubviews訪問到的話孩锡,一般需要創(chuàng)建這個(gè)子控件的對應(yīng)屬性來指向它酷宵。

    @property (nonatomic, weak) UIButton *button; // 注意這里使用weak就可以,因?yàn)閎utton已經(jīng)被加入到self.view.subviews這個(gè)數(shù)組里躬窜。
    ...
    
    - (instancetype)initWithFrame: (CGRect)frame
    {
     if (self = [super initWithFrame: frame]) {
         UIButton *button = ... // 創(chuàng)建一個(gè)button
            [button setTitle: ...] // 設(shè)置button的屬性
            [self.view addSubview: button]; // 將button加到view中浇垦,并不設(shè)置尺寸
            self.button = button; //將self.button指向這個(gè)button保證在layoutSubviews中可以訪問
             
             UILabel *label = ... // 其他的子控件同理
        }
    }
    

    這樣我們就可以在layoutSubviews中訪問子控件,設(shè)置子控件的尺寸荣挨,因?yàn)榇藭r(shí)view的frame已經(jīng)確定男韧。

    - (void)layoutSubviews 
    {
     [super layoutSubviews]; // 注意,一定不要忘記調(diào)用父類的layoutSubviews方法默垄!
    
         self.button.frame = ... // 設(shè)置button的frame
        self.label.frame = ...  // 設(shè)置label的frame
    }
    

    經(jīng)過以上的步驟此虑,就可以實(shí)現(xiàn)自定義控件。

  3. 同時(shí)口锭,我們還希望可以給我們的自定義控件數(shù)據(jù)寡壮,讓其顯示。

    一般來說首先要將得到的數(shù)據(jù)轉(zhuǎn)換成模型數(shù)據(jù),然后給這個(gè)自定義控件傳入模型數(shù)據(jù)讓其顯示况既。

    所以在這個(gè)自定義控件的頭文件这溅,需要我們設(shè)置接口以得到別人傳入的數(shù)據(jù)。比如當(dāng)前我們有一個(gè)Book類棒仍,它有一個(gè)name屬性用于顯示名稱悲靴,有一個(gè)like屬性用于顯示多少人喜歡。現(xiàn)在我們需要將Book的name顯示到自定義類的label子控件上莫其,將Book的like顯示到自定義類的button子控件上癞尚。

    首先在自定義類的頭文件中:

    ...
    @property (nonatomic, strong) Book *book;
    ...
    

    在這里我們接收一個(gè)book作為需要顯示的數(shù)據(jù)。

    然后在自定義的實(shí)現(xiàn)文件中重寫book的setter方法:

    - (void)setBook: (Book *)book 
    {
     _book = book; // 注意在這個(gè)方法中乱陡,不寫這句也是沒有問題的浇揩,因?yàn)樵谙旅娴恼Z句使用的是book而非self.book或_book,但是如果在其他的方法中也想要訪問book這個(gè)屬性憨颠,那么就需要寫上胳徽,否則self.book或_book會一直是nil(因?yàn)槌隽诉@個(gè)方法的作用域,book就銷毀了爽彤,如果再想訪問需要有其他的引用指向它)养盗。所以建議,要寫上這句适篙。
    
        [self.button setTitle: book.like forState...];
        self.label = book.name;
    }
    

    這樣往核,當(dāng)我們想要使用自定義類顯示數(shù)據(jù)時(shí):

    // 在控制器類的某個(gè)方法中:
    Book *book = self.books[index]; // 這里指拿到books這個(gè)數(shù)據(jù)中的某個(gè)數(shù)據(jù)用于顯示
    CYLView *view = [[CYLView alloc] initWithFrame: ...];
    [self.view addSubview: view]; // 將自定義類加到view中
    view.book = book; // 設(shè)置book的數(shù)據(jù),此時(shí)會調(diào)用setter方法給各個(gè)控件設(shè)置數(shù)據(jù)
    

    這樣一來就實(shí)現(xiàn)自定義類顯示數(shù)據(jù)的功能嚷节。而且將子控件封裝到自定義中聂儒,控制器只需要創(chuàng)建自定義類和給它數(shù)據(jù),而不需要擔(dān)心這個(gè)類內(nèi)部是怎么設(shè)計(jì)的硫痰,都有什么控件衩婚,數(shù)據(jù)是如何安排的,所以當(dāng)需求改變時(shí)碍论,我們的控制器有可能完全不用改動谅猾,只需改變自定義類的內(nèi)部就可以。

    ?

總結(jié):

  1. initWithFrame:中添加子控件鳍悠。

  2. layoutSubviews中設(shè)置子控件frame税娜。

  3. 對外設(shè)置數(shù)據(jù)接口,重寫setter方法給子控件設(shè)置顯示數(shù)據(jù)藏研。

  4. 在view controller里面使用init/initWithFrame:方法創(chuàng)建自定義類敬矩,并且給自定義類的frame賦值。

  5. 對自定義類對外暴露的數(shù)據(jù)接口進(jìn)行賦值即可蠢挡。

    ?

使用xib方式

  1. 使用xib的方式可以省去initWithFrame:layoutSubviews中添加子控件和設(shè)置子控件尺寸的步驟弧岳,還有在view controller里面設(shè)置view的frame凳忙,因?yàn)樘砑幼涌丶驮O(shè)置子控件的尺寸以及整個(gè)view的尺寸在xib中就已經(jīng)完成。(注意整個(gè)view的位置還沒有設(shè)置禽炬,需要在控制器里面設(shè)置涧卵。)

  2. 我們只需對外提供數(shù)據(jù)接口,重寫setter方法就可以顯示數(shù)據(jù)腹尖。

  3. 注意要將xib中的類設(shè)置為我們的自定義類柳恐,這樣創(chuàng)建出來的才是自定義類,而不是默認(rèn)的父類热幔。

  4. 當(dāng)然乐设,用xib這種方式是需要加載xib文件的。加載xib文件有兩種方法:

    // 第一種方法(較為常用)
    CYLView *view = [[[NSBundle mainBundle] loadNibNamed:@"CYLView" owner:nil options:nil] firstObject]; // CYLView代表CYLView.xib绎巨,代表CYLView這個(gè)類對應(yīng)的xib文件近尚。這個(gè)方法返回的是一個(gè)NSArray,我們?nèi)〉谝粋€(gè)Object或最后一個(gè)(因?yàn)檫@個(gè)數(shù)組只有一個(gè)CYLView沒有其他對象)就是需要加載的CYLView场勤。
    
    // 第二種方法
    UINib *nib = [UINib nibWithNibName:@"CYLView" bundle:nil];
    NSArray *objectArray = [nib instantiateWithOwner:nil options:nil];
    CYLView *view = [objectArray firstObject];
    
  5. xib文件中的控件可以通過Control-Drag的方式在CYLView中進(jìn)行連線戈锻,這樣CYLView是就可以訪問這些控件。(可以在setter方法中給這些控件賦值以顯示數(shù)據(jù))

總結(jié):

  1. 創(chuàng)建xib却嗡,在xib中拖入需要添加的控件并設(shè)置好尺寸舶沛。并且要將這個(gè)xib的Class設(shè)置為我們的自定義類嘹承。

  2. 通過IBOutlet的方式窗价,將xib中的控件與自定義類進(jìn)行關(guān)聯(lián)。

  3. 對外設(shè)置數(shù)據(jù)接口叹卷,重寫setter方法給子控件設(shè)置顯示數(shù)據(jù)撼港。

  4. 在view controller類里面加載xib文件就可以得到對應(yīng)的類(這里不需要再設(shè)置自定義類的frame,因?yàn)閤ib已經(jīng)有了整個(gè)view的大小骤竹。只需要設(shè)置位置帝牡。),接著就可以對類對外的數(shù)據(jù)接口賦值蒙揣。

    ?

補(bǔ)充

  1. 如果使用代碼的方式創(chuàng)建控件靶溜,那么在初始化時(shí)一定會調(diào)用initWithFrame:方法;如果使用xib/storyboard方式創(chuàng)建控件懒震,那么在初始化時(shí)一定會調(diào)用initWithCoder:方法罩息。

  2. initWithCoder:里面訪問屬性,比如self.button个扰,會發(fā)現(xiàn)它是nil的瓷炮,因?yàn)榇藭r(shí)自定義控件正在初始化,self.button可能還未賦值(self.button是一個(gè)IBOutlet递宅,IBOutlet本質(zhì)上就相當(dāng)于Xcode找到這個(gè)對應(yīng)的屬性娘香,然后UIButton *button = … , [self.view addSubview: button]這種操作苍狰,而這一切的操作都是相當(dāng)于在CYLView *view = [[CYLView alloc] initWithCoder: nil]方法之后執(zhí)行的。上面的代碼就相當(dāng)于用代碼的方式實(shí)現(xiàn)Xcode在storyboard中加載CYLView)烘绽,所以如果在這個(gè)方法中進(jìn)行初始化操作是可能會失敗的淋昭。

    所以建議在awakeFromNib方法中進(jìn)行初始化的額外操作。因?yàn)?code>awakeFromNib是在初始化完成后調(diào)用安接,所以在這個(gè)方法里面訪問屬性(IBOutlet)就可以保證不為nil响牛。

  3. 事實(shí)上使用xib創(chuàng)建自定義控件,我們可以將加載xib的過程封裝到自定義的類中赫段,只對外暴露一個(gè)初始化方法呀打,這樣外界就不知道內(nèi)部是如何創(chuàng)建的自定義控件了。

    比如在CYLView.h中提供一個(gè)類工廠方法:

    + (instancetype)viewWithBook: (Book *)book;
    

    然后在CYLView.m中實(shí)現(xiàn)這個(gè)方法:

    + (instancetype)viewWithBook: (Book *)book
    {
     CYLView *view = [[[NSBundle mainBundle] loadNibNamed: NSStringFromClass(self) owner: nil opetions: nil] firstObject];
     view.book = book;
         return view;
    }
    

    這樣外界只需用viewWithBook:方法傳入一個(gè)book糯笙,就可以創(chuàng)建一個(gè)CYLView的對象贬丛,而具體是怎么創(chuàng)建的,只有CYLView才知道给涕。

  4. 如果我們想豺憔,無論是通過代碼的方式,還是通過xib的方式够庙,都會初始化一些值恭应,那么我們可以將初始化的代碼抽到一個(gè)方法里面,然后在initWithFrame:方法和awakeFromNib方法中分別調(diào)用這個(gè)方法耘眨。

    關(guān)于為什么是awakeFromNib前面已經(jīng)說了:

    通過xib的方式創(chuàng)建的自定義控件昼榛,需要設(shè)置IBOutlet屬性,雖然會調(diào)用initWithCoder:方法剔难,但是調(diào)用這個(gè)的方法的時(shí)候IBOutlet屬性還未設(shè)置好胆屿,所以在這個(gè)方法中訪問屬性將會是nil。而在awakeFromNib中偶宫,IBOutlet已經(jīng)初始化完畢非迹,所以在這個(gè)方法中初始化不會失敗。

    如果通過initWithFrame:方法纯趋,說明是通過代碼創(chuàng)建的自定義控件憎兽,它的屬性并不是IBOutlet的,所以不存在未完成IBOutlet的屬性未初始化完這種情況吵冒。所以在initWithFrame:方法中訪問一些屬性是沒有問題的纯命。但是應(yīng)該注意,如果是通過init方法創(chuàng)建的自定義控件也會調(diào)用initWithFrame:方法桦锄,但是此時(shí)的self.frame是沒有被賦值的(在掉用這個(gè)方法的時(shí)候并沒有設(shè)置控件的大性健),如果這種情況下使用self.frame是沒有值的结耀。注意這種情況留夜。

    ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匙铡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碍粥,更是在濱河造成了極大的恐慌鳖眼,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚼摩,死亡現(xiàn)場離奇詭異钦讳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)枕面,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門愿卒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人潮秘,你說我怎么就攤上這事琼开。” “怎么了枕荞?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵柜候,是天一觀的道長。 經(jīng)常有香客問我躏精,道長渣刷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任矗烛,我火速辦了婚禮辅柴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘高诺。我一直安慰自己碌识,他們只是感情好碾篡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布虱而。 她就那樣靜靜地躺著,像睡著了一般开泽。 火紅的嫁衣襯著肌膚如雪牡拇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天穆律,我揣著相機(jī)與錄音惠呼,去河邊找鬼。 笑死峦耘,一個(gè)胖子當(dāng)著我的面吹牛剔蹋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辅髓,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼泣崩,長吁一口氣:“原來是場噩夢啊……” “哼少梁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起矫付,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤凯沪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后买优,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妨马,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年杀赢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烘跺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脂崔,死狀恐怖液荸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脱篙,我是刑警寧澤娇钱,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站绊困,受9級特大地震影響文搂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秤朗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一煤蹭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧取视,春花似錦硝皂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至折欠,卻和暖如春贝或,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锐秦。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工咪奖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酱床。 一個(gè)月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓羊赵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扇谣。 傳聞我的和親對象是個(gè)殘疾皇子昧捷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容