用xib做公共組件

現(xiàn)狀

1、用代碼寫的公共view,用起來比較簡單,不論是代碼方式還是IB方式,都很簡單;
2、用xib寫的組件,需要調(diào)用者來加載,用代碼方式加入到原有的視圖體系中;如果調(diào)用者是用IB畫界面的,那么就要用代碼設(shè)限制或者定frame,不是很方便

改進(jìn)點(diǎn)

將加載代碼從調(diào)用者移到公共組件內(nèi)部,讓IB開發(fā)界面的調(diào)用者只要拉一個(gè)IBOutlet就可以訪問了,不需要own這個(gè)公共組件對(duì)象,和系統(tǒng)組件一樣方便使用。

xib加載過程

函數(shù)調(diào)用情況

  • 不會(huì)自動(dòng)調(diào)用的函數(shù)
  - (instancetype)init {
    self = [super init];
    
    if (self) {
        [self loadViewFromXib];
    }
    
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    if (self) {
        [self loadViewFromXib];
    }
    
    return self;
}
  • 自動(dòng)調(diào)用的函數(shù)
- (void)awakeFromNib {
    [self loadViewFromXib];
}

- initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder: aDecoder];
    
    if (self) {
        [self loadViewFromXib];
    }
    
    return self;
}

選擇的加載函數(shù)

- (void)awakeFromNib {
    [self loadViewFromXib];
}

自動(dòng)加載,通過IBOutlet的方式訪問對(duì)象,調(diào)用者不需要own這個(gè)組件

加載的代碼

- (void)loadViewFromXib {
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
    UIView *view = [views firstObject];
    
    if (nil != view) {
        UIView *superview = self;
        
        [superview addSubview:view];
        
        UIEdgeInsets padding = UIEdgeInsetsMake(0, 0, 0, 0);
        
        [view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(superview).with.insets(padding);
        }];
    }
}
  1. 需要將xib的File's Owner 設(shè)置為自定義的組件類
  2. xib中頂級(jí)的view就是load函數(shù)加載等到的view,只是個(gè)容器
  3. 將容器view以addSubview的方式添加到自定義的組件類(self)
  4. 代碼加限制,將容器view和自定義的組件類(self)完全重合,使self成了最底層的容器

加載完后執(zhí)行的函數(shù)

// View在加載完成之后,會(huì)自動(dòng)調(diào)用這個(gè)函數(shù),這里可以更新約束
- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    // 其它代碼

    // 變換約束之后,重新布局,以便取到正確的frame
    [self layoutIfNeeded]; 

    // 這個(gè)函數(shù)執(zhí)行完,一般要加這句,更新View
    [self setNeedsDisplay];
}

自動(dòng)函數(shù)加載順序

場景:ViewController作為調(diào)度者。用xib做一個(gè)自定義的view名字叫ChartView,供外部調(diào)用。xib中用代碼做一個(gè)自定義的view,叫PlotView,用來畫圖表。經(jīng)過斷點(diǎn)實(shí)驗(yàn),加載順序如下

  1. ChartView: - (void)awakeFromNib
  2. ViewController: - (void)viewDidLoad
  3. ViewController: - (void)viewWillAppear:(BOOL)animated
  4. ViewController: - (void)viewWillLayoutSubviews
  5. ViewController: - (void)viewDidLayoutSubviews
  6. ChartView: - (void)drawRect:(CGRect)rect
  7. PlotView: - (void)drawRect:(CGRect)rect
  8. ViewController: - (void)viewDidAppear:(BOOL)animated

這個(gè)調(diào)用關(guān)系就是導(dǎo)致有些函數(shù)放在- (void)viewDidLoad- (IBAction)buttonTouched:(UIButton *)sender中有不同表現(xiàn)的原因

難點(diǎn)

  1. 一般xib的File's Owner是ViewController,所以Xcode默認(rèn)讓xib對(duì)應(yīng)一個(gè)ViewController
  2. tableViewCell的File's Owner是空的(NSObject),最頂層的view容器設(shè)置為自定義的Cell類,所以需要對(duì)應(yīng)的ViewController來加載這個(gè)cell,調(diào)用者ViewController擁有own這個(gè)cell(strong引用)
  3. 一般的xib公共組件,F(xiàn)ile's Owner是空的(NSObject),所以需要調(diào)用者加載,調(diào)用這own這個(gè)組件。做法就是將最頂層view設(shè)置為自定義的組件類。這個(gè)跟tableViewCell的用法很像。
  4. 將xib的File's Owner設(shè)置為自定義的類之后,頂層的view容器的own是自定義的類自身self,所以這個(gè)view的引用是strong,雖然是IBOutlet,跟其它的不一樣
    @property (strong, nonatomic) IBOutlet UIView *containerView;
  5. 自定義類的owner是調(diào)用者的xib文件,所以自定義類本身(self)在調(diào)用者類中可以是弱引用,weak,像普通的IBOutlet一樣
    @property (weak, nonatomic) IBOutlet PAButtonWithBadgeView *buttonWithBadgeView;

注意點(diǎn)

由于自定義類本身是最底層的view容器,而xib中的View容器完全覆蓋在上面,所有擋住了,這個(gè)導(dǎo)致設(shè)置自定義的背景色不起作用。解決的方法是重寫基類的設(shè)置函數(shù)

// self是最底層容器,會(huì)被self.containerView擋住,導(dǎo)致看起來代碼設(shè)背景色不起作用,所以覆蓋基類的函數(shù)
- (void)setBackgroundColor:(UIColor *)backgroundColor {
    if (nil != backgroundColor) {
        self.backgroundColor = backgroundColor;
        [self setContainerColor:backgroundColor];
    }
}

- (void)setContainerColor:(UIColor *)color {
    if (nil != color) {
        self.containerView.backgroundColor = color;
    }
} ```
> 遺留問題:
這樣做,代碼設(shè)背景色,看上去是變了;但是,直接在調(diào)用者xib中設(shè)置自定義組件的背景色,還是不起作用的

# 備選方案
在自定義組件的xib中,頂層view容器的背景色設(shè)置為clearColor。這樣自定義組件的背景色完全由自定義組件自身來決定。調(diào)用者在xib和代碼設(shè)置背景色都能成功。但是這樣會(huì)導(dǎo)致如下不完美之處:

* 在自定義組件設(shè)計(jì)的xib中,背景色就是一片空白,失去了所見即所得的優(yōu)勢(shì)。
* 在實(shí)際使用過程中,自定義組件都是用view拉一個(gè)占位符,其它的具體內(nèi)容還是要切換到自定義組件的xib中去看具體內(nèi)容。背景色在這里,更有意義。

**正是為了發(fā)揮xib更直觀的優(yōu)勢(shì),所以采用了上面那種不完美的方案?;谝粋€(gè)假設(shè):在總的xib上的占位符上用IB設(shè)背景色,沒有什么意義。將背景色和自定義組件的xib具體內(nèi)容結(jié)合起來看更有意義。**

# 實(shí)例代碼

[zhangxusong888/PAButtonWithBadgeViewTest](https://github.com/zhangxusong888/PAButtonWithBadgeViewTest)

作為對(duì)比,下面這個(gè)例子是純代碼寫的控件

[zhangxusong888/PAProcessBarTest](https://github.com/zhangxusong888/PAProcessBarTest)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容