使用XIB实现嵌套自定义XIB视图
在進行?iOS?開發的過程中,對于一些復雜的界面,我們可以通過?Interface Builder?這個?Xcode?集成的可視化界面編輯工具在完成,這回節省大部分時間以及代碼量。它的使用方法這里不做介紹了,這次我要介紹是使用它來實現一個嵌套的自定義視圖。解釋一下就是,我們使用?IB?自定義了一個?View?,然后又在其他的?xib?文件中使用了這個?View?,那么這就是所謂的嵌套自定義視圖。之所以要介紹它,是因為我自己在使用它的時候遇到了一些問題,一方面寫下來做個記錄供自己查看,另一方面我相信大家在使用的時候應該也會遇到這樣的問題,方便大家。?
下面使用的示例代碼我已經放到?Github?上了,?項目地址?,有需要的朋友可以去查看,?Demo?非常簡單,主要是介紹這個知識點。?
Question
首先我們創建一個?SingleView?的工程,項目使用?StoryBoard?,(使用?Xib也無所謂,因為有些老的項目可能還沒有使用到?StoryBoard?),然后創建一個?CustomView?作為我們的自定義視圖。?
有時對于復雜的界面我們可能會拆分出來對它進行單獨處理,又有可能它的界面布局很復雜,這時我們就會用?Interface Builder?對它的布局進行處理。這里的?CustomView?就是這樣一個視圖,所以我們為它創建一個?xib?文件,我們通常的作法就是把?xib?中的?View?的?custom class?更改為我們的?CustomView?。?
接下來對我們的界面進行布局,并連接輸出口,編寫響應邏輯,這里我放了一個?ImageView?和一個?Label?在這里,并把?View?的背景色設置為淺灰色。?
自定義的?View?制作完成,回到我們?ViewController?的?xib?文件,拖入兩個?View?并把他們的?custom class?更改為?CustomView?。?
這時,我們算是工作做完了,運行程序,結果悲劇了,怎么不是我們想要的結果,為什么只生成了兩個空白的視圖,我們視圖上的圖片和文字哪里去了?
在?CustomView?中的?awakeFromeNib?方法中增加斷點調試發現,在?CustomView?初始化完成后,?ImageView?和?Label?并沒有被初始化,他們仍然是?nil?。這就是在嵌套使用?xib?自定義視圖時非常容易出現的問題,我們覺得被嵌套的視圖能夠正常顯示出來,但是實際上它并沒有被按照我們在?xib?上指定的方式被初始化。?
Solution
那么如何解決這種問題,以及這種問題又是如何出現的呢?其實這主要是由于我們對?xib?文件的加載原理不熟悉所導致的,我們以為定義一個?View?,創建一個?xib文件并布局好它的子視圖,讓后將它使用在另外一個?xib?文件中,把?custom class?改成它,然后?xib?的加載系統會自動為我們做好其余的一切。其實并不是這樣的。?
這樣做?xib?加載系統只會為我們創建一個?CustomView?的對象,但這并不包括?CustomView?所對應的?xib?文件中的部分,所以只創建了一個空白的?View?。?
解決他們有兩種方式,不過最終的思路都是通過代碼強制使?CustomView?的?xib部分被加載。第一種是通過代碼創建?CustomView?的對象,然后?addSubview?到?viewController?的?view?上。第二種是在?CustomView?的實現文件里,通過重載一些方法,來完成加載?xib?文件。?
這兩種方法各有利弊,第一種使用起來方便也好理解,但是當嵌套的層級比較多的時候或者一個?View?中有多個這樣的?CustomView?時,這種方式就會顯得過于麻煩。而第二種雖然理解起來有些難度,但是當你處理好之后,直接在需要的?xib?文件中拖入?view?,改個?custom class?,就能直接生成需要的對象了,并且也能夠在?xib?中對他們進行直接布局,不再需要用代碼去布局了。?
NO 1.
先來介紹第一種方法,很簡單,就是找到?xib?文件,生成對象,設置屬性,?addsubview?到視圖上。?
NO 2.
第二種方法是通過重載?initWithCoder?方法來實現,因為通過?xib?來創建一個對象會調用到這個方法,所以我們需要在這個方法里做一些處理,把這個?CustomView?的?xib?中的內容加載進來,這時同樣是需要通過代碼來來加載,首先附上代碼?
| ? | - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { UIView *containerView = [[[UINib nibWithNibName:@"CustomView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0]; CGRect newFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); containerView.frame = newFrame; [self addSubview:containerView]; } return self; } |
此外,還要這里的輸出口以及設置?custom class?的位置跟第一種方式有所不同,這里需要取消掉?xib?中?view?的?custom class?,再將跟它連接的圖片與文字的輸出口取消掉,在這里這個?view?只是被當做一個容器來處理,它跟?Customview?沒有直接關系,它將來會被?addSubview?到?CustomView?上,除此之外還要把?xib?的?File's ower?的?custom class?改成?CustomView?,表示這個?xib?文件的持有者是?CustomView?。再把它與圖片和文字通過輸出口連接起來。?
這個時候在運行程序就看到了我們想要的結果了。?_^
其實想要實現第二種解決方案所要的效果,還有一種方式,它是通過重載?awakeAfterUsingCoder:?方法來實現的,這個方法的返回值會替換掉真正的加載對象,所以在具體的加載?CustomView?的方式又與第一種相同,所以?xib?的輸出口連接與?custom class?的設置也與第一種解決方案相同。不過這種方式是更復雜也更難于理解的,不推薦使用,因為上一個方法就能很好的解決這個問題了,這里只是貼出這個方法的代碼,有想仔細研究的請參看文章底部的參考文章。?
| ? | - (id) awakeAfterUsingCoder:(NSCoder*)aDecoder { BOOL isJustAPlaceholder = ([[self subviews] count] == 0); if (isJustAPlaceholder) { CustomView* theRealThing = [[self class] getClassObjectFromNib]; theRealThing.frame = self.frame; // make compatible with Auto Layout self.translatesAutoresizingMaskIntoConstraints = NO; theRealThing.translatesAutoresizingMaskIntoConstraints = NO; // convince ARC that we're legit, unnecessary since at least Xcode 4.5 CFRelease((__bridge const void*)self); CFRetain((__bridge const void*)theRealThing); return theRealThing; } return self; } |
原文:http://blog.wtlucky.com/blog/2014/08/10/nested-xib-views/
總結
以上是生活随笔為你收集整理的使用XIB实现嵌套自定义XIB视图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: StoryBoard之User Defi
- 下一篇: iOS: Crash文件解析