为UIView自定义Xib

 

一、需求

 通过Interface Builder的形式创建Xib,并将其和一个UIView的子类绑定,如何实现?

二、解决

 这个问题通过搜索,有大量的答案,大概答案的代码如下:

  

 也就是在你的子类中,在初始化方法initWithFrame、initWithCoder中主动加载一个xib对应的类,作为子view添加到当前的view中

 这种方式,会明显产生一个问题,会产生两个相同的自定义View对象

 

 

  先不说两个View带来的危害,可能导致业务代码的冲突,最关键的是,xib中拖出的reference指向只能在loadnib返回的对象中初始化好,你自定义的对象的指向为空。

  这样太坑爹了

  这也是下面这个问题产生的原因,想通过修改initwithCoder的返回值为自己主动反序列化xib产生的对象

  

 

  这种方案我也测试了,测试代码如下:

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#import "XibHackInit.h"
 
@implementation XibHackInit
 
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    static BOOL alreadyInitMark = NO;
    if(self = [super initWithCoder:aDecoder])
    {
        if(!alreadyInitMark)
        {
            NSString *className = NSStringFromClass([self class]);
            if([className containsString:@"."])
            {
                className = [[className componentsSeparatedByString:@"."] lastObject];
            }
            alreadyInitMark = YES;
            UIView *targetView = [[self class] loadFromNib:className];
            alreadyInitMark = NO;
            return (XibHackInit *)(targetView?targetView:self);
        }
    }
    return self;
}
 
+ (UIView *)loadFromNib:(NSString *)nibName
{
    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:nibName
                                                      owner:nil
                                                    options:nil];
    UIView* nibView = (UIView*)[nibViews objectAtIndex:0];
    return nibView;
}
 
@end

  注意initWithCoder中用了一个BOOL变量,这个变量主要是为了避免产生的递归,loadFromNib方法一调用、又会调用到initWithCoder中,简直就成了一个不可解的死bug

  上面的代码是OC实现的,为什么不用swift实现,主要是因为swift中init方法除了返回空值之外,不能返回其他值

  这么做的结果是什么呢?程序直接Crash

  

 

  有没有更优美的方案?

 这里的折中方案是,先创建一个容器View,在容器View中加载你想要的子View,这样可以充分考虑到Xib中复用的问题

 效果:

  

 

除了清晰的View层次之外,还可以在Interface Builder中进行可视化,实现的关键代码如下:

 

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import UIKit
 
@IBDesignable
class XibElementContainer: UIView {
 
    var elementView:UIView?
    @IBInspectable var elementNibName:String?
 
    override func awakeFromNib() {
        super.awakeFromNib()
        xibSetup()
    }
     
    func xibSetup() {
        guard let view = loadViewFromNib() else { return }
        view.frame = bounds
        view.autoresizingMask =
            [.flexibleWidth, .flexibleHeight]
        addSubview(view)
        elementView = view
    }
     
    func loadViewFromNib() -> UIView? {
 
        guard let nibName = elementNibName else { return nil }
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(
            withOwner: self,
            options: nil).first as? UIView
    }
     
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        xibSetup()
        elementView?.prepareForInterfaceBuilder()
    }
}

 

有两个关键字:

@IBDesignable  InterfaceBuilder 会尝试对这个类的对象进行可视化,会调用prepareForInterfaceBuilder方法

@IBInspectable InterfaceBuilder 会显示这个字段的输入框,可以在界面上面直接输入值

 

三、最终代码

  代码:https://github.com/liqiushui/CustomXibForUIView

posted @   兜兜有糖的博客  阅读(1060)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示