iOS 布局&显示&约束

布局

layoutSubviews

不能显示调用这个方法,有许多可以在run loop 的不同时间点触发layoutSubviews的调用机制,这些触发机制比直接调用layoutSbuviews的资源消耗要小的多

可以自动调用layoutSubviews的动作有:

  • 修改view的大小
  • 新增子view
  • 用户在UIScrollView上滚动(layoutSubviews会在UIScrollView和它的父view上被调用)
  • 用户旋转设备
  • 跟新视图的constraints

这些方式都会告知系统view的位置需要被重新计算,继而会自动转化为一个最终的layoutSubviews调用。当然,也有直接触发layoutSubviews的方法。

setNeedsLaylout()

触发layoutSubviews调用的最省资源的方法就是在你的视图上调用setNeedsLaylout方法。调用这个方法代表向系统表示视图的布局需要重新计算。setNeedsLaylout方法会立即执行并返回,但在返回前不会真正更新视图。视图会在下一个update cycle中更新,就在系统调用视图们的layoutSubviews以及他们的所有子视图的layoutSubviews方法的时候。即使从setNEedsLayout返回到视图被重新绘制并布局之前有一段任意时间间隔,但是这个延迟不会对用户造成影响,因为永远不会长到对界面造成卡顿。

layoutifNeeded()

layoutifNeeded是另一个会让UIView触发layoutSubviews的方法。当视图需要更新的时候,与setNeeddsLayout()会让视图在下一周期调用layoutSubviews更新视图不同,layoutIfNeeded会立即调用layoutSubviews方法。但是如果你调用了layoutIfNeeded之后,并且没有任何操作向系统表明需要刷新视图,那么就不会调用layoutsubview。如果你在同一个run loop内调用了两次layoutIfNeeded,并且两次之间没有更新视图,第二个调用同样不会触发layoutSubviews方法。

使用layoutIFfNeeded,则布局和重绘会立即发生并在函数返回之前完成(除非有正在运行中的动画)。这个方法在你需要依赖新布局,无法等到下一次update cycle的时候会比setNeedsLayout有用。除非是这种情况,否则你更应该使用setNeedsLayout,这样在每次run loop中都只会更新一次布局。

当对希望通过修改constraint进行动画时,这个方法特别有用。你需要在animation block之前调用layoutIfNeeded,以确保在动画开始之前传播所有的布局更新,在animation block 中设置新constrait后,需要在此调用layoutIfNeeded来动画到新的状态。

当layoutSubviews完成后,在view的所有者view controller上会触发viewDidLayoutSubviews调用,因为viewDidLayoutSubviews是view布局更新后会被唯一可靠调用的方法,应该把所有依赖布局或者大小的代码放在viewDidLayoutSubviews中。这是避免使用过时的布局或者位置变量的唯一方法。

显示

一个视图的显示包含了颜色、文本、图片和Core Graphics回执等视图属性,不包括其本身和子视图的大小和位置。和布局的方法类似,显示也有触发更新的方法,它们由系统在检测到更新时被自动调用,或者我们可以手动调用直接刷新。

draw(_:)

UIView的draw方法(Objective-C的drawRect)方法对视图内容显示的操作,类似于视图布局的layoutSubviews,但是不同于layoutSubviews,draw方法不会触发后续对视图的子视图方法的调用。同样,和layoutSubviews一样,你不应该直接调用draw方法,而应该通过调用触发方法,让系统在run loop中的不同结点自动调用。

setNeedsDisplay()

这个方法类似于布局中的setNeedLayout.它会给有内容更新的视图设置一个内部标记,但在视图重绘之前就会返回。然后在下一个update cycle中,系统会遍历所有已标记的视图,并调用它们的draw方法。如果你只想在下次更新时重绘部分视图,你可以调用setNeedsDicplay(_:),并把需要重绘的矩形部分传进去(setNeedsDisplayInRect in OC)。大部分时候,在视图中跟新任何UI组建都会把相应的视图标记为“dirty”,通过设置视图“内部更新标记”,在下一次update cycle 中就会重绘,而不需要显示的setNeedsDisplay调用。然而如果你有一个属性没有绑定到UI组建,但需要在每次更新时重绘视图,你可以定义他的didSet属性,并且调用setNeedsDisplay来触发视图合适的更新。

有时候设置一个属性要求自定义绘制,这种情况下你需要重写draw方法。

视图的显示方法里没有类似布局中的layoutIfNeeded这样可以触发立即更新的方法。通常情况下等到下一个更新周期在重新绘制视图也无所谓。

约束

自动布局包含三步来布局和重绘视图。第一步是更新约束,系统会计算并给视图设置所有要求的约束。第二步是布局阶段,布局引擎计算视图和子视图的 frame 并且将它们布局。最后一步完成这一循环的是显示阶段,重绘视图的内容,如实现了 draw 方法则调用 draw。

updateConstraints()

这个方法用来在自动布局中动态改变视图约束。和布局中的layoutSubviews()方法或显示中的draw方法类似。updateConstraints() 只应该被重载,绝不要再代码中显示地调用。通常你只应该在updateConstraints方法中实现必须要更新的约束。静态的约束应该在interface builder、视图的初始化方法或者viewDidLoad()方法中指定。

通常情况下,开启或者关闭constrains、更改constrain的属性或者常量值、或者从视图层级中转移一个视图是都会设置一个内部的标记,这个标记会在下一个更新周期中触发调用updateConstrains。当然,也有手动给视图打上“update constarints”标记的方法,如下。

setNeedsUpdateConstraints()

调用 setNeedsUpdateConstraints() 会保证在下一次更新周期中更新约束。它通过标记“ constrant 已 update”来触发 updateConstraints()。这个方法和 setNeedsDisplay() 以及 setNeedsLayout() 方法的工作机制类似。

updateConstraintsIfNeeded()

对于使用自动布局的视图来说,这个方法与 layoutIfNeeded 等价。它会检查“更新 constrains”标记(可以被 setNeedsUpdateConstraints 自动设置,或者 invalidateInstrinsicContentSize)。如果它认为这些约束需要被更新,它会立即触发 updateConstraints() ,而不会等到 run loop 的末尾。

invalidateIntrinsicContentSize()

自动布局中某些视图拥有 intrinsicContentSize 属性,这是视图根据它的内容得到的自然尺寸。一个视图的 intrinsicContentSize 通常由所包含的元素的约束决定,但也可以通过重载提供自定义行为。调用 invalidateIntrinsicContentSize() 会设置一个标记表示这个视图的 intrinsicContentSize 已经过期,需要在下一个布局阶段重新计算。

串联起来

布局、显示和约束都遵循着相似的模式,例如他们更新的方式以及如何在 run loop 的不同时间点上强制更新。任意组件都有一个实际去更新的方法(layoutSubviews, draw, 和 updateConstraints),你可以重写来手动操作视图,但是任何情况下都不要显式调用。这个方法只在run loop的末端会被调用,如果视图被标记了告诉系统该视图需要被更新的标记的话。有一些操作会自动设置这个标志,但是也有一些方法允许您显式地设置它。对于布局和约束相关的更新,如果你等不到在run loop末端才更新(例如:其他行为依赖于新布局),有方法可以让你立即更新,并保证“布局需更新”标记被正确标记。下面的表格列出了任意组件会怎样更新及其对应方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容