为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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架