UIButton子类初始化报错:属性初始化时机引发的两类问题
嘿,这个初始化的坑我之前踩过好几次!Swift对类初始化的顺序要求特别严格,刚好能解决你的问题~
你遇到的矛盾其实是Swift初始化规则导致的:
- Swift要求所有存储属性必须在调用
super.init()之前完成初始化,否则会报“Property not initialized at super.init call” - 但你要访问
self(比如获取self.frame.width)必须在super.init()之后,不然会触发“'self' used before super.init call”的错误
下面给你两种靠谱的解决方案,按需选择:
方案一:使用可选类型(支持纯代码+XIB/Storyboard)
把originalWidth声明为隐式解包可选或者可选类型,先让它通过初始化检查,再在super.init()之后赋值。如果是从XIB/Storyboard加载的按钮,还要注意在layoutSubviews里补全初始化,避免拿到错误的尺寸:
class AnimatedButton: UIButton { // 用隐式解包可选,确保后续使用不用频繁解包 private var originalWidth: CGFloat! // 纯代码初始化入口 override init(frame: CGRect) { super.init(frame: frame) // super.init之后self合法了,直接赋值 originalWidth = frame.width setupCustomAnimations() } // XIB/Storyboard初始化入口 required init?(coder: NSCoder) { super.init(coder: coder) // 这里的frame可能还没确定,先临时用bounds.width,后续在layoutSubviews里修正 originalWidth = bounds.width setupCustomAnimations() } // 确保布局完成后拿到正确的初始宽度 override func layoutSubviews() { super.layoutSubviews() if originalWidth == nil { originalWidth = bounds.width } } private func setupCustomAnimations() { // 这里写你的自定义动画逻辑 print("初始宽度:\(originalWidth)") } }
方案二:用临时变量提前计算(仅适合纯代码创建)
如果你的按钮只通过纯代码创建,完全不需要支持XIB,可以先把宽度存在一个临时变量里(不属于self),然后用这个变量初始化originalWidth,再调用super.init():
class AnimatedButton: UIButton { // 用不可变的let声明,更安全 private let originalWidth: CGFloat override init(frame: CGRect) { // 先把宽度存在临时变量,不属于self,所以可以在super.init之前使用 let initialWidth = frame.width originalWidth = initialWidth // 现在所有存储属性都初始化完成了,调用super.init super.init(frame: frame) setupCustomAnimations() } // 如果要支持XIB,这个方法需要额外处理,不如方案一方便 required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupCustomAnimations() { print("初始宽度:\(originalWidth)") } }
额外注意事项
- 如果你的按钮会在运行时改变尺寸,记得在尺寸变化时更新
originalWidth(或者根据需求决定是否需要更新) - 从XIB加载的控件,
init?(coder:)阶段的frame往往不是最终布局后的尺寸,所以一定要在layoutSubviews里做兜底检查,避免拿到错误的初始值
内容的提问来源于stack exchange,提问作者PruitIgoe
相关产品推荐
相关产品推荐

