美文网首页
iOS layoutSubviews

iOS layoutSubviews

作者: 言霏 | 来源:发表于2020-04-02 16:05 被阅读0次

触发时机(参考):

    1. 自身被init(frame: CGRect)创建出来且CGRect不会zero时。
    1. addSubview的时候。
    1. 当自身或子视图size发生改变的时候。
    1. 滑动UIScrollView的时候。
    1. 旋转Screen会触发父UIView上的layoutSubviews事件。

着重讨论下比较复杂的第二条情况(子视图size发生改变的时候)。
建立如下白板Demo

image.png
ViewController中添加橙色父视图,其中含有Change按钮、紫色子视图,且给紫色视图添加autolayout。
image.png

完整代码:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}

class OrangeView: UIView {
    
    @IBOutlet weak var purpleView: UIView!
    @IBOutlet weak var purpleViewH: NSLayoutConstraint!
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        print(#function)
    }
    
    @IBAction func clickBtn(_ sender: Any) {
        
        changeConstraint()
    }
    
    func changeConstraint() {
        if purpleViewH.constant == 30.0 {
            purpleViewH.constant = 160.0
        } else {
            purpleViewH.constant = 30.0
        }
        
        print(purpleView.frame.size.height)
    }
}

此时点击Change按钮,紫色视图变为160高度,打印:

image.png
可以看到即使更改了purpleViewH.constant = 160.0但是purpleView.frame.size.height仍为30.0,这种情况是在purpleViewH.constant = 160.0时橙色视图被标记为“需要刷新”,在当前runloop结束时才会调用layoutSubviews方法进行真正刷新,故打印仍为30.0
更改changeConstraint ()为:
func changeConstraint() {
        if purpleViewH.constant == 30.0 {
            purpleViewH.constant = 160.0
        } else {
            purpleViewH.constant = 30.0
        }
        
        self.layoutIfNeeded()
        print(purpleView.frame.size.height)
    }

紫色视图变为160高度。打印:

image.png
purpleViewH.constant = 160.0将橙色视图被标记为“需要刷新”,layoutIfNeeded()会把“需要刷新”的视图立即去调用layoutSubviews()purpleView.frame.size.height也被更新为160.0
更改changeConstraint ()为:
func changeConstraint() {
    self.layoutIfNeeded()
    print(purpleView.frame.size.height)
}

打印:

image.png
此时layoutSubviews()不会被调用,紫色视图依旧也给30.0高度。
更改changeConstraint ()为:
func changeConstraint() {
        self.setNeedsLayout()
        self.layoutIfNeeded()
        print(purpleView.frame.size.height)
    }

打印:

image.png
setNeedsLayout()的作用就是标记当前视图为“需要刷新”(即使其实没必要刷新的),layoutIfNeeded ()才会立即去调用了layoutSubviews()

将紫色视图的autoLayout删掉,同时更改代码为:

@IBAction func clickBtn(_ sender: Any) {
        
        changeFrame()
    }
    
    func changeFrame() {
        if purpleView.bounds.height == 30.0 {
            purpleView.bounds = CGRect.init(x: 0, y: 0, width: purpleView.bounds.width, height: 160.0)
        } else {
            purpleView.bounds = CGRect.init(x: 0, y: 0, width: purpleView.bounds.width, height: 30.0)
        }
        
        print(purpleView.frame.size.height)
    }

紫色视图变为高度160.0,打印:

image.png
用bounds、frame进行布局,会立即改变,在当前runloop结束时调用layoutSubviews ()
更改changeFrame ()为:
    func changeFrame() {
        if purpleView.bounds.height == 30.0 {
            purpleView.bounds = CGRect.init(x: 0, y: 0, width: purpleView.bounds.width, height: 160.0)
        } else {
            purpleView.bounds = CGRect.init(x: 0, y: 0, width: purpleView.bounds.width, height: 30.0)
        }
        self.layoutIfNeeded()
        print(purpleView.frame.size.height)
    }

紫色视图变为高度160.0,打印:

image.png
如果自身已经被标记了“需要刷新”(可能是由于改变autolayout、bounds等的值被系统自动标记,也可能是setNeedsLayout强制标记)layoutIfNeeded ()就会立即调用layoutSubviews()
更改changeFrame ()为:
    func changeFrame() {
        self.setNeedsLayout()
        print(purpleView.frame.size.height)
    }

打印:

image.png
setNeedsLayout()将当前视图强制标记为“需要刷新”,在runloop当前周期结束前调用layoutSubviews()

可以得出结论setNeedsLayout()是将当前视图强制标记为“需要刷新”,在runloop当前周期结束前调用layoutSubviews()
如果视图已经被标记了“需要刷新”,layoutIfNeeded()就会立即调用layoutSubviews(),同时将视图标记为“不需要刷新”,以免在runloop当前周期结束前又调用次layoutSubviews()
能将视图标记为“需要刷新”的方式就是开头说的那几种。

相关文章

网友评论

      本文标题:iOS layoutSubviews

      本文链接:https://www.haomeiwen.com/subject/josxphtx.html