UIView子类以frame.zero初始化时,调用makeRound的最佳生命周期方法
解决方案:在
layoutSubviews方法中调用makeRound 你初始化时用frame.zero,此时视图的bounds还未被设置为最终尺寸,所以直接在初始化里调用makeRound会得到cornerRadius = 0。而layoutSubviews是UIView生命周期中每次视图布局(包括尺寸调整)完成后都会被调用的方法,在这里能确保layer.bounds已经是最新的非零值。
不过要注意的是:layoutSubviews可能会被多次触发(比如父视图布局变化、设备旋转等),最好加个判断,只在尺寸确实变化时更新圆角,避免不必要的性能开销:
class YourCustomView: UIView { private var lastBoundsSize: CGSize = .zero public init() { super.init(frame: .zero) // 放置初始化逻辑,比如设置背景色、添加子视图等 } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() // 仅当bounds尺寸变化时更新圆角 if bounds.size != lastBoundsSize { makeRound() lastBoundsSize = bounds.size } } } fileprivate extension UIView { func makeRound() { layer.cornerRadius = layer.bounds.width * 0.5 clipsToBounds = true } }
为什么选layoutSubviews?
- 它是UIKit专门处理布局更新的回调,不管你用代码设置frame还是Auto Layout约束,只要视图尺寸或位置变化,这个方法都会触发,能保证拿到最新的
bounds。 - 对比
didMoveToSuperview:后者仅在视图添加到父视图时调用一次,但此时父视图可能还没完成布局,视图最终尺寸尚未确定,可靠性不足。 - 对比
draw(_ rect: CGRect):这个方法负责绘制逻辑,把布局操作放在这里不符合职责分离原则,还可能带来额外绘制开销。
另外还有一种简化方案:如果你的视图最终会是正方形,可以利用layer.cornerRadius的特性,直接设一个极大值,UIKit会自动将其转为圆形,这样就能在初始化方法里直接调用makeRound,无需等待布局完成:
fileprivate extension UIView { func makeRound() { layer.cornerRadius = CGFloat.greatestFiniteMagnitude clipsToBounds = true } }
不过这个方案的前提是视图最终必须是正方形,否则会得到圆角矩形而非圆形。
内容的提问来源于stack exchange,提问作者fingia
相关产品推荐
相关产品推荐

