iOS開發 自定義控制項的方式及注意

2021-07-20 15:03:49 字數 4846 閱讀 3213

1. 使用純**的方式

一般來說我們的自定義類繼承自uiview,首先在initwithframe:方法中將需要的子控制項加入view中。注意,這裡只是加入到view中,並沒有設定各個子控制項的尺寸。

為什麼要在initwithframe:方法而不是在init方法?

因為使用純**的方式建立自定義類,在以後使用的時候可能使用init方法建立,也有可能使用initwithframe:方法建立,但是無論哪種方式,最後都會呼叫到initwithframe:方法。在這個方法中建立子控制項,可以保證無論哪種方式都可以成功建立。

為什麼要在initwithframe:方法裡面只是將子控制項加到view而不設定尺寸?

前面已經說過,兩種方式最後都會呼叫到initwithframe:方法。如果使用init方法建立,那麼這個view的frame有可能是不確定的:

cylview *view = [[cylview alloc] init];

view.frame = cgrectmake(0, 0, 100, 100);

...

如果是這種情況,那麼在init方法中,frame是不確定的,此時如果在initwithframe:方法中設定尺寸,那麼各個子控制項的尺寸都會是0,因為這個view的frame還沒有設定。(可以看到是在傳送完init訊息才設定的)

在layoutsubviews方法中就可以達到這個目的。第一次view__將要顯示__的時候會呼叫這個方法,之後當view的尺寸(不是位置)改變時,會呼叫這個方法

所以正常的做法應該是在initwithframe:方法中建立子控制項,注意此時子控制項有可能只是乙個區域性變數,所以想要在layoutsubviews訪問到的話,一般需要建立這個子控制項的對應屬性來指向它。

@property (nonatomic, weak) uibutton *button; // 注意這裡使用weak就可以,因為button已經被加入到self.view.subviews這個陣列裡。

...- (instancetype)initwithframe: (cgrect)frame

}

這樣我們就可以在layoutsubviews中訪問子控制項,設定子控制項的尺寸,因為此時view的frame已經確定。

- (void)layoutsubviews 

經過以上的步驟,就可以實現自定義控制項。

同時,我們還希望可以給我們的自定義控制項資料,讓其顯示

一般來說首先要將得到的資料轉換成模型資料,然後給這個自定義控制項傳入模型資料讓其顯示。

所以在這個自定義控制項的標頭檔案,需要我們設定介面以得到別人傳入的資料。比如當前我們有乙個book類,它有乙個name屬性用於顯示名稱,有乙個like屬性用於顯示多少人喜歡。現在我們需要將book的name顯示到自定義類的label子控制項上,將book的like顯示到自定義類的button子控制項上。

首先在自定義類的標頭檔案中

...

@property (nonatomic, strong) book *book;

...

在這裡我們接收乙個book作為需要顯示的資料。

然後在自定義的實現檔案中重寫book的setter方法

- (void)setbook: (book *)book 

這樣,當我們想要使用自定義類顯示資料時:

// 在控制器類的某個方法中:

book *book = self.books[index]; // 這裡指拿到books這個資料中的某個資料用於顯示

cylview *view = [[cylview alloc] initwithframe: ...];

[self.view addsubview: view]; // 將自定義類加到view中

view.book = book; // 設定book的資料,此時會呼叫setter方法給各個控制項設定資料

這樣一來就實現自定義類顯示資料的功能。而且將子控制項封裝到自定義中,控制器只需要建立自定義類和給它資料,而不需要擔心這個類內部是怎麼設計的,都有什麼控制項,資料是如何安排的,所以當需求改變時,我們的控制器有可能完全不用改動,只需改變自定義類的內部就可以。

initwithframe:中新增子控制項。

layoutsubviews中設定子控制項frame

對外設定資料介面,重寫setter方法給子控制項設定顯示資料。

在view controller裡面使用init/initwithframe:方法建立自定義類,並且給自定義類的frame賦值。

對自定義類對外暴露的資料介面進行賦值即可。

使用xib的方式可以省去initwithframe:和layoutsubviews中新增子控制項和設定子控制項尺寸的步驟,還有在view controller裡面設定view的frame,因為新增子控制項和設定子控制項的尺寸以及整個view的尺寸在xib中就已經完成。(注意整個view的位置還沒有設定,需要在控制器裡面設定。)

我們只需對外提供資料介面,重寫setter方法就可以顯示資料。

注意要將xib中的類設定為我們的自定義類,這樣建立出來的才是自定義類,而不是預設的父類。

當然,用xib這種方式是需要載入xib檔案的。載入xib檔案有兩種方法:

// 第一種方法(較為常用)

cylview *view = [[[nsbundle mainbundle] loadnibnamed:@"cylview" owner:nil options:nil] firstobject]; // cylview代表cylview.xib,代表cylview這個類對應的xib檔案。這個方法返回的是乙個nsarray,我們取第乙個object或最後乙個(因為這個陣列只有乙個cylview沒有其他物件)就是需要載入的cylview。

// 第二種方法

uinib *nib = [uinib nibwithnibname:@"cylview" bundle:nil];

nsarray *objectarray = [nib instantiatewithowner:nil options:nil];

cylview *view = [objectarray firstobject];

建立xib,在xib中拖入需要新增的控制項並設定好尺寸。並且要將這個xib的class設定為我們的自定義類。

通過iboutlet的方式,將xib中的控制項與自定義類進行關聯。

對外設定資料介面,重寫setter方法給子控制項設定顯示資料。

在view controller類裡面載入xib檔案就可以得到對應的類(這裡不需要再設定自定義類的frame,因為xib已經有了整個view的大小。只需要設定位置。),接著就可以對類對外的資料介面賦值。

如果使用**的方式建立控制項,那麼在建立時一定會呼叫initwithframe:方法;如果使用xib/storyboard方式建立控制項,那麼在建立時一定會呼叫initwithcoder:方法。

在initwithcoder:裡面訪問屬性,比如self.button,會發現它是nil的,因為此時自定義控制項正在初始化,self.button可能還未賦值(self.button是乙個iboutlet,iboutlet本質上就相當於xcode找到這個對應的屬性,然後uibutton button = … , [self.view addsubview: button]這種操作,而這一切的操作都是相當於在cylview view = [[cylview alloc] initwithcoder: nil]方法之後執行的。上面的**就相當於用**的方式實現xcode在storyboard中載入cylview),所以如果在這個方法中進行初始化操作是可能會失敗的。

所以建議在awakefromnib方法中進行初始化的額外操作。因為awakefromnib是在初始化完成後呼叫,所以在這個方法裡面訪問屬性(iboutlet)就可以保證不為nil。

3. 事實上使用xib建立自定義控制項,我們可以將載入xib的過程封裝到自定義的類中,只對外暴露乙個初始化方法,這樣外界就不知道內部是如何建立的自定義控制項了。

比如在cylview.h中提供乙個類工廠方法:

+ (instancetype)viewwithbook: (book *)book;

然後在cylview.m中實現這個方法:

+ (instancetype)viewwithbook: (book *)book

這樣外界只需用viewwithbook:方法傳入乙個book,就可以建立乙個cylview的物件,而具體是怎麼建立的,只有cylview才知道。

如果我們想,無論是通過**的方式,還是通過xib的方式,都會初始化一些值,那麼我們可以將初始化的**抽到乙個方法裡面,然後在initwithframe:方法和awakefromnib方法中分別呼叫這個方法。

關於為什麼是awakefromnib前面已經說了:

通過xib的方式建立的自定義控制項,需要設定iboutlet屬性,雖然會呼叫initwithcoder:方法,但是呼叫這個的方法的時候iboutlet屬性還未設定好,所以在這個方法中訪問屬性將會是nil。而在awakefromnib中,iboutlet已經初始化完畢,所以在這個方法中初始化不會失敗。

如果通過initwithframe:方法,說明是通過**建立的自定義控制項,它的屬性並不是iboutlet的,所以不存在未完成iboutlet的屬性未初始化完這種情況。所以在initwithframe:方法中訪問一些屬性是沒有問題的。但是應該注意,如果是通過init方法建立的自定義控制項也會呼叫initwithframe:方法,但是此時的self.frame是沒有被賦值的(在掉用這個方法的時候並沒有設定控制項的大小),如果這種情況下使用self.frame是沒有值的。注意這種情況。

iOS基礎開發 自定義控制項

自定義控制項,設定子控制項的尺寸和位置 當系統提供的控制項滿足不了我們的需求,我們可以自定義乙個控制項,繼承系統自帶的控制項,寫乙個屬於自己的控制項.自定義控制項的好處是可以把封裝控制項內部的細節,不容易被外界隨意修改.如果乙個view內部的子控制項比較多,一般會考慮自定義乙個view,把它內部子控...

IOS開發自定義CheckBox控制項

ios本身沒有系統的checkbox元件,但是實際開發中會經常用到,所以專門寫了乙個checkbox控制項,直接上 效果圖 uicheckboxbutton.h檔案如下 import import common.h inte ce uicheckboxbutton uicontrol uilabel...

開發自定義控制項

學習自定義控制項的開發不僅可以使你開發出更靈活的系統更重要的是它可以使你加深對已有伺服器控制項的理解,得以更靈活的應用。先說一下伺服器控制項的概念吧 所謂伺服器控制項,就是在伺服器上執行,並可以對映到所有瀏覽器支援的標準 html 標記的控制項,在你的web窗體中,凡是包含 runat server...