为何self.view = mainView时UIView圆角异常,addSubview则正常?
self.view = mainView时UIView的圆角显示异常? 嘿,这个问题我之前也踩过坑,核心是布局时机和视图层级初始化顺序的差异在搞鬼,咱们一步步拆解清楚:
1. 直接赋值self.view = mainView时的布局bug根源
当你执行self.view = mainView时,ViewController的view属性被直接替换,但此时mainView还没被加入到窗口(window)的视图层级里,系统也还没来得及解析它的布局约束。这时候你调用self.view.layoutIfNeeded(),mainView的frame大概率还是初始的CGRect.zero或者默认的占位大小,那你创建的子视图v的约束自然没法算出正确的frame——比如v.widthAnchor对应的实际宽度可能是0,这时候v.frame.width / 2得到的圆角值完全不对,圆形效果自然就崩了。
2. 用addSubview(mainView)时为啥正常?
当你把mainView作为子视图加到原self.view上,还给它加了铺满父视图的约束,这时候mainView已经处于ViewController的视图层级中了。系统会先处理mainView的布局,当你调用layoutIfNeeded()时,mainView已经有了正确的frame,它的子视图v的约束也能正确计算出实际大小,这时候v.frame.width / 2就能得到刚好让视图变圆的圆角值,效果自然正常。
解决办法:确保布局完成后再设置圆角
要搞定直接赋值self.view = mainView的情况,关键是要等mainView和子视图v的布局都彻底完成后,再计算圆角。这里有两个靠谱的方案:
方案一:把圆角代码移到viewDidLayoutSubviews
这个方法是系统专门用来通知我们视图布局完成的回调,所有视图的frame/bounds此时都是准确的,用bounds计算圆角比frame更可靠(bounds是视图自身坐标系的大小,不受父视图变换影响):
// 先在类里把v声明为属性,方便在viewDidLayoutSubviews里访问 private let circleView = UIView() override func viewDidLoad() { super.viewDidLoad() self.view = mainView mainView.addSubview(circleView) circleView.backgroundColor = .orange circleView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ circleView.leftAnchor.constraint(equalTo: mainView.leftAnchor), circleView.rightAnchor.constraint(equalTo: mainView.centerXAnchor), circleView.topAnchor.constraint(equalTo: mainView.centerYAnchor), circleView.heightAnchor.constraint(equalTo: circleView.widthAnchor) ]) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() circleView.layer.cornerRadius = circleView.bounds.width / 2 // 可选:如果要确保完全裁剪成圆形,加上这句 circleView.layer.masksToBounds = true }
方案二:手动确保mainView布局完成后再设置圆角
如果你非要在viewDidLoad里搞定,那得先给mainView加上正确的约束(让它铺满窗口),再手动触发布局,最后再设置圆角:
override func viewDidLoad() { super.viewDidLoad() self.view = mainView mainView.translatesAutoresizingMaskIntoConstraints = false // 给mainView加约束,让它铺满窗口 guard let window = view.window else { return } NSLayoutConstraint.activate([ mainView.topAnchor.constraint(equalTo: window.topAnchor), mainView.leadingAnchor.constraint(equalTo: window.leadingAnchor), mainView.trailingAnchor.constraint(equalTo: window.trailingAnchor), mainView.bottomAnchor.constraint(equalTo: window.bottomAnchor) ]) // 先让mainView完成布局 mainView.layoutIfNeeded() // 再创建并设置子视图v let v = UIView() mainView.addSubview(v) v.backgroundColor = .orange v.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ v.leftAnchor.constraint(equalTo: mainView.leftAnchor), v.rightAnchor.constraint(equalTo: mainView.centerXAnchor), v.topAnchor.constraint(equalTo: mainView.centerYAnchor), v.heightAnchor.constraint(equalTo: v.widthAnchor) ]) // 触发v的布局 mainView.layoutIfNeeded() // 最后设置圆角 v.layer.cornerRadius = v.bounds.width / 2 v.layer.masksToBounds = true }
内容的提问来源于stack exchange,提问作者Kevin Bai

