实现自定义UIButton时为什么需要编写两个init初始化方法
为什么需要单独写自定义初始化方法?
自定义初始化方法本质是对重复配置逻辑的封装,不属于强制要求的代码。如果只保留override init(frame: CGRect)也能正常使用,但每次创建实例时都需要手动写背景色设置、标题设置、调用configure()的代码,容易产生重复冗余代码,也可能出现漏调用配置方法的问题。提供自定义初始化方法可以降低调用方的代码量,同时保证所有自定义按钮的配置逻辑统一。
为什么自定义init里要设置super.init(frame: .zero)?
.zero是CGRect(x: 0, y: 0, width: 0, height: 0)的简写,传这个值是因为当前主流iOS开发都采用AutoLayout自动布局,不需要提前给控件设定固定尺寸,后续添加完约束后系统会自动计算控件的实际frame,这里的.zero仅作为初始化时的占位值使用。
如果你需要用frame布局的场景,也可以调整自定义初始化的参数,把frame作为入参暴露给调用方传入即可。
为什么自定义init必须调用super.init?
这是Swift的两段式初始化规则强制要求的:类的初始化需要先完成父类所有存储属性的初始化,再处理子类的自定义逻辑。UIButton作为父类本身存在大量内部私有属性需要初始化,不调用super.init的话父类部分的内存空间没有完成合法初始化,整个CustomButton实例是不完整的,无法正常工作,编译器也会直接报错拦截。
能不能把所有逻辑都合并到第一个init里?
完全可以,常见的合并方式有两种:
方式1:给默认初始化方法加参数默认值
直接修改override init(frame: CGRect)的参数,给所有自定义属性加默认值,就可以适配不同的创建场景:
override init(frame: CGRect = .zero, backgroundColor: UIColor = .clear, title: String = "") { super.init(frame: frame) self.backgroundColor = backgroundColor setTitle(title, for: .normal) configure() }
调用时可以按需传参:
- 传frame创建:
CustomButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50)) - 传背景色和标题创建:
CustomButton(backgroundColor: .blue, title: "按钮")
方式2:将自定义init改为便利初始化
把你写的自定义初始化标记为convenience,直接复用已有的init(frame:)逻辑,不需要重复写父类初始化调用和配置方法调用:
convenience init(backgroundColor: UIColor, title: String) { self.init(frame: .zero) self.backgroundColor = backgroundColor setTitle(title, for: .normal) }
这种方式的优势是所有通用配置逻辑都收拢在init(frame:)中,后续修改配置规则只需要改一处即可,避免多初始化方法的逻辑不同步问题。
补充说明
代码中的required init?(coder: NSCoder)是UIKit控件遵守NSCoding协议的强制要求,如果你不用Storyboard/XIB创建这个自定义按钮,保持现有fatalError的实现即可,不会影响纯代码场景的使用。
内容的提问来源于stack exchange,提问作者Qman485

