setneedslayoutupdateconstraints 有什么用

添加 NSLayoutConstraints 的代码放在什么位置? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
已注册用户请 &
Sponsored by
国内领先的实时后端云野狗 API 可用于开发即时聊天、网络游戏、实时定位等实时场景传输快!响应快!入门快!
Promoted by
添加 NSLayoutConstraints 的代码放在什么位置?
· 342 天前 · 841 次点击
比如一个UIViewController中。今天看到中看到是这样组织的:
在loadView 中初始化UIView并添加,最后调用setNeedsUpdateConstraints
在updateViewConstraints中设置NSLayoutConstraints,并在最后调用[super updateViewConstraints],另外,为了不重复调用,用到了BOOL didSetupConstraints。
- (void)loadView
self.view = [UIView new];
[self.view addSubview:self.blueView];
[self.view addSubview:self.redView];
[self.view addSubview:self.yellowView];
[self.view addSubview:self.greenView];
[self.view setNeedsUpdateConstraints]; // bootstrap Auto Layout
- (void)updateViewConstraints
if (!self.didSetupConstraints) {
// Apply a fixed height of 50 pt to two views at once, and a fixed height of 70 pt to another two views
[@[self.redView, self.yellowView] autoSetViewsDimension:ALDimensionHeight toSize:50.0];
[@[self.blueView, self.greenView] autoSetViewsDimension:ALDimensionHeight toSize:70.0];
self.didSetupConstraints = YES;
[super updateViewConstraints];
问题来了:
1. 如果我在loadView中添加Constraints,这样肯定是添加一次的,也省得用didSetupConstraints了,不是更好吗?这样会有什么问题吗?
[super updateViewConstraints];为什么放到了updateViewConstraints的最后面,放在最前面会有什么问题呢?
PS: 在中提到了上面的做法,很可惜并没有说明原因,所以询问一下大家的看法。
5 回复 &| &直到
14:15:30 +08:00
& &342 天前 via iPhone
简单说说我的看法:
1 在loadView中确实是只会添加一次,但是当这个view的subview调用updateViewConstraints的时候就不能更新constraint了。
2 因为update constraints的过程是bottom-up的,就是说parent view先update完成,child view再进行update,所以要把[super updateViewConstrsints]放最面。
前两天刚在stackoverflow回答过类似问题,供你参考
& &342 天前 via iPhone
@ Ps 现在项目中因为app不允许横屏或者各种尺寸变化,所以我为了简化(偷懒)也是在loadView里面放置constraint代码的。。。
& &342 天前
@ 感谢您的回复。我从您的文章「A Note On Using Auto Layout」中受益匪浅。
& &342 天前
1. 在 loadView 中添加 constraint 并把每个 size class 对应的 constraints 放到一个数组里面
比如 Compact Width | Regular Height 的放到一个数组里面,对应 iPhone 竖屏的 constraints,然后 Regular Width | Regular Height 的放到数组里面,以此类推
2. (iOS 8+ only) 在 - (void) willTransitionToTraitCollection 中用 [NSLayoutConstraint activateConstraints:] 和 [NSLayoutConstraint deactivateConstraints:],详情请看文档……
& &342 天前
@ 很高兴对你有帮助。
& · & 1119 人在线 & 最高记录 1893 & · &
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.7.3 · 50ms · UTC 15:33 · PVG 23:33 · LAX 08:33 · JFK 11:33? Do have faith in what you're doing.二次元同好交流新大陆
扫码下载App
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
附:真心感激衔接国际技术的同行们。本文翻译自:有人在上问了一个问题:如何在UITableViewCell中使用Autolayout来实现Cell的内容和子视图自动计算行高,并且能够保持平滑滚动的?这个问题得到了300+的支持和450+的收藏,答案得到了730+的支持,很详细的说明了如何在iOS7和iOS8上实现UITableView的动态行高功能,并且这个答案对实现UICollectionView的动态行高也具有参考意义。所以在这里将这个答案翻译了一下,希望对大家有所帮助。以下是答案的全文翻译:答案略长,如果你不喜欢细读,可以直接看这两个示例的代码:&- iOS8以上才支持&- iOS7+核心概念不管你是在哪个iOS版本上做开发,以下步骤中的前两个步骤都是必须的:1、设置好布局约束条件在UITableViewCell子类中,添加布局约束,使得cell子视图的边缘固定(pin)到cell的contentView的边缘(最重要的是要有顶部和底部的边距约束条件)。注意:不要将子视图的边距约束固定到cell本身上了,只能固定到cell的contentView上!&确保每个子视图垂直方向上的内容压缩阻力(compression resistance)和吸附性约束(hugging constraints)没有被你添加的更高优先级的约束条件覆盖,让这些子视图的固有内容尺寸(intrinsic content size)来驱动contentView的高度。()记住,要点是让cell的子视图与contentView之间产生垂直的连结,让它们能够对contentView“施加压力”,使contentView扩展以适合它们的尺寸。下面用一个cell和一些子视图作为示例,展示了你的一些(不是全部!)布局约束应该看起来是什么样的:可以设想,随着更多的文本被添加到上例中“Multi-line body”那个label上,它需要垂直地增高以适合文本,这将有效地迫使cell的高度增加。(当然,你需要正确地设置约束条件,以使其正常的工作!)如何设置正确的约束条件,绝对是使用Autolayout实现动态行高时最难最重要的部分。如果弄错了,它就可能无法正常工作——所以,不要着急,慢慢来!我建议你用代码来设置布局约束,这样你就完全知道每个布局约束被加到了什么地方,出问题时也更容易调试。特别如果使用一些优秀开源库,可以让用代码设置约束和用Interface Builder设置约束一样简单直观,并且功能还更强大。这里也有一个由我设计和维护的专用库:如果你用代码来设置布局约束,你应该在UITableViewCell子类的updateConstraints方法里面一次性完成。注意,updateConstraints可能不止被调用一次,因此要避免重复添加相同的布局约束。在updateConstraints中,可以将添加布局约束的代码包在一个if条件语句中(比如用一个叫didSetupConstraints的布尔属性,运行一次添加布局约束的代码后就将其设置为YES),以确保不重复添加相同的布局约束。另外,更新已有布局约束的代码(比如调整布局约束的constant属性),也应该将它们放置在updateConstraints&中,但是要在didSetupConstraints条件语句的外面,这样才可以确保每次调用的时候都会被执行。2. Cell使用具有唯一性的重用标示符在cell里面,为每一组特定的约束条件,使用一个特定的cell重用标示符。换句话说,如果cell有多种不同的布局,每一种布局应当有其对应的重用标示符。(当cell有多种不同数量的子视图的时候,或者子视图以一种独特的方式布局的时候,这些情况下你就需要使用一个新的重用标示符。)例如,要在一个cell中显示一条email消息,可能会有4种独特的布局:第一种,只有主题的消息;第二种,带主题和正文的消息;第三种,带主题和图片附件的消息;第四种,带主题、正文和图片附件的消息。每一种布局都需要完全不同的布局约束才能实现。因此,一旦cell被初始化并且布局约束被加到其中任意一种类型的cell上后,cell应当得到一个唯一的重用标示符来指定该cell类型。这就意味着,当你dequeue重用一个cell的时候,该类型cell的布局约束已经添加好了,可以直接使用。注意,由于固有内容尺寸的不同,具有相同布局约束的cell仍然可能具有不同的高度!不要混淆了布局(不同的约束)和由不同内容尺寸而计算出(通过相同的布局约束来计算)的不同视图frame这两个概念,它们根本是完全不同的两个东西。不要将拥有不同布局约束条件的cell丢到同一个重用池当中(也就是使用相同的重用标示符),然后又在每次dequeue过后将旧的约束移除,又从头开始重新添加约束。自动布局引擎内部并没有被设计来可以处理大规模的约束更改,你会看到大量的性能问题。iOS8 - Self-Sizing Cells3. 启用估算行高在iOS8上,苹果将许多在iOS8之前比较难实现的东西都内置实现了。为了让cell实现self-sizing的机制,必须先将tableView的rowHeight属性设置为常量UITableViewAutomaticDimension。然后,只需将tableView的estimatedRowHeight属性设置为非零值即可开启行高估算功能,例如:1
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // 设置为一个接近“平均”行高的值
这样做就为tableView上还没有显示在屏幕上的cell提供了一个临时的估算的行高。然后,当cell即将滚入屏幕范围内的时候,会计算出实际的高度。为了确定每一行的实际高度,tableView会自动让每个cell基于其contentView的已知固定宽度(tableView的宽度,减去其他额外的,像section index或accessoryView这些宽度)和被加到contentView及其子视图上的自动布局约束规则来计算contentView的高度。一旦真正的行高被计算出来后,旧的估算的行高会被更新为这个真实的行高(并且其他任何需要对tableView的contentSize或contentOffset的更改都自动替你完成了)。一般来说,行高估算值不需要太精确——它只是被用来修正tableView中滚动条的大小的,当你在屏幕上滑动cell的时候,即便估算值不准确,tableView还是能很好地调节滚动条。将tableView的estimatedRowHeight属性设置成(在viewDidLoad或类似的方法中)一个接近于“平均”行高的常量值即可。只有行高变化很极端的时候(比如相差一个数量级),才会在滚动时产生滚动条“跳跃”的现象。这个时候,你才应当实现tableView:estimatedHeightForRowAtIndexPath:方法,为每一行返回一个更精确的估算值。iOS7支持(需要自己实现cell尺寸自适应功能)3. 完成一个完整的布局过程 & 计算cell的高度首先,为每一个cell都初始化一个离屏(offscreen)实例,为每个重用标示符实例化一个与之对应的cell实例,这些cell完全用于高度计算。(离屏表示cell的引用被存储在view controller的一个属性或实例变量之中,并且这个cell绝对不会被用作tableView:cellForRowAtIndexPath:方法的返回值以实际呈现在屏幕上。)接着,这个cell的内容(例如,文本、图片等等)还必须和会被显示在table view中的内容完全一致。然后,强制cell立即更新子视图的布局,再用cell的contentView调用systemLayoutSizeFittingSize:方法计算出cell所需的高度是多少。使用UILayoutFittingCompressedSize参数可以得到适合cell中所有内容所需的最小尺寸。然后其高度就可以作为tableView:heightForRowAtIndexPath:方法的返回值。4. 使用估算的行高如果你的table view超过了几十行,你会发现自动布局约束的解决方式在第一次加载table view的时候会迅速地卡住主线程。因为,在第一次加载过程中,每一行都会调用tableView:heightForRowAtIndexPath:方法(为了计算滚动条的尺寸)。iOS7中,你可以(也绝对应该)使用table view的estimatedRowHeight属性。这样会为还不在屏幕范围内的cell提供一个临时估算的行高值。然后,当这些cell即将要滚入屏幕范围内的时候,真实的行高值会被计算出来(通过tableView:heightForRowAtIndexPath:方法),估算的行高就会被替换掉。一般来说,行高估算值不需要太精确——它只是被用来修正tableView中滚动条的大小的,当你在屏幕上滑动cell的时候,即便估算值不准确,tableView还是能很好地调节滚动条。将tableView的estimatedRowHeight属性设置成(在viewDidLoad或类似的方法中)一个接近于“平均”行高的常量值即可。只有行高变化很极端的时候(比如相差一个数量级),才会在滚动时产生滚动条“跳跃”的现象。这个时候,你才应当实现tableView:estimatedHeightForRowAtIndexPath:方法,为每一行返回一个更精确的估算值。5. 缓存行高(如果需要)如果上面提到的你都做了,但是tableView:heightForRowAtIndexPath:的性能仍然慢的不可接受。非常不幸,你需要给行高做一些缓存(这是苹果的工程师们给出的改进建议)。大体的思路是,第一次计算时让自动布局引擎解析约束条件,然后将计算出的行高缓存起来,以后所有对该cell的高度的请求都返回缓存值。当然,关键还要确保任何会导致cell高度变化的情况发生时你都清除了缓存的行高——这通常发生在cell的内容变化时或其他重大事件发生时(比如用户调节了动态类型文本大小(Dynamic Type text size)的滑动条)。iOS7示例代码(包含详细的注释)1
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
// 判断indexPath对应cell的重用标示符,
// 取决于特定的布局需求(可能只有一个,也或者有多个)
NSString *reuseIdentifier = ...;
// 取出重用标示符对应的cell。
// 注意,如果重用池(reuse pool)里面没有可用的cell,这个方法会初始化并返回一个全新的cell,
// 因此不管怎样,此行代码过后,你会可以得到一个布局约束已经完全准备好,可以直接使用的cell。
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// 用indexPath对应的数据内容来配置cell,例如:
// cell.textLabel.text = someTextForThisC
// 确保cell的布局约束被设置好了,因为它可能刚刚才被创建好。
// 使用下面两行代码,前提是假设你已经在cell的updateConstraints方法中设置好了约束:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// 如果你使用的是多行的UILabel,不要忘了,preferredMaxLayoutWidth需要设置正确。
// 如果你没有在cell的-[layoutSubviews]方法中设置,就在这里设置。
// cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);
return cell;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
// 判断indexPath对应cell的重用标示符,
NSString *reuseIdentifier = ...;
// 从cell字典中取出重用标示符对应的cell。如果没有,就创建一个新的然后存储在字典里面。
// 警告:不要调用table view的dequeueReusableCellWithIdentifier:方法,因为这会导致cell被创建了但是又未曾被tableView:cellForRowAtIndexPath:方法返回,会造成内存泄露!
UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
if (!cell) {
cell = [[YourTableViewCellClass alloc] init];
[self.offscreenCells setObject:cell forKey:reuseIdentifier];
// 用indexPath对应的数据内容来配置cell,例如:
// cell.textLabel.text = someTextForThisC
// 确保cell的布局约束被设置好了,因为它可能刚刚才被创建好。
// 使用下面两行代码,前提是假设你已经在cell的updateConstraints方法中设置好了约束:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// 将cell的宽度设置为和tableView的宽度一样宽。
// 这点很重要。
// 如果cell的高度取决于table view的宽度(例如,多行的UILabel通过单词换行等方式),
// 那么这使得对于不同宽度的table view,我们都可以基于其宽度而得到cell的正确高度。
// 但是,我们不需要在-[tableView:cellForRowAtIndexPath]方法中做相同的处理,
// 因为,cell被用到table view中时,这是自动完成的。
// 也要注意,一些情况下,cell的最终宽度可能不等于table view的宽度。
// 例如当table view的右边显示了section index的时候,必须要减去这个宽度。
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
// 触发cell的布局过程,会基于布局约束计算所有视图的frame。
// (注意,你必须要在cell的-[layoutSubviews]方法中给多行的UILabel设置好preferredMaxLayoutWidth值;
// 或者在下面2行代码前手动设置!)
[cell setNeedsLayout];
[cell layoutIfNeeded];
// 得到cell的contentView需要的真实高度
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
// 要为cell的分割线加上额外的1pt高度。因为分隔线是被加在cell底边和contentView底边之间的。
height += 1.0f;
return height;
// 注意:除非行高极端变化并且你已经明显的觉察到了滚动时滚动条的“跳跃”现象,你才需要实现此方法;否则,直接用tableView的estimatedRowHeight属性即可。
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
// 以必需的最小计算量,返回一个实际高度数量级之内的估算行高。
if ([self isTallCellAtIndexPath:indexPath]) {
return 350.0f;
return 40.0f;
示例项目&- iOS8以上才支持&- iOS7+
阅读(64456)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'使用Autolayout实现UITableView的Cell动态布局和高度动态改变',
blogAbstract:'摘自:/blog//shi-yong-autolayoutshi-xian-uitableviewde-celldong-tai-bu-ju-he-ke-bian-xing-gao/index.html附:真心感激衔接国际技术的同行们。本文翻译自:',
blogTag:'autolayout,cell,高度',
blogUrl:'blog/static/0',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:0,
permalink:'blog/static/0',
commentCount:2,
mainCommentCount:1,
recommendCount:1,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}查看: 1769|回复: 1
精华0在线时间8 小时帖子主题UID125980积分3295技术分1300 资源分200 分享激情775 博客好友记录相册
知县, 积分 3295, 距离下一级还需 1705 积分
UID125980积分3295
求解他们之间的关系?
也就是什么时候会更新界面,怎么更新界面,各种关系我有些混乱
精华67在线时间17366 小时帖子主题UID1积分403067技术分133280 资源分20036 分享激情125921 博客好友记录相册
首席研究员
UID1积分403067
layout是调整布局,也就是view的位置,一般是对subviews作用。
setNeedsDisplay涉及到redraw,也就是重绘,一般都是对receiver作用。
人生是一种修行
DEVDIV博主
DEVDIV人才
一级王者勋章
一级王者勋章
及时将最新信息发给我
通过Email及时将最新的模板和服务发给我。
及时了解我们最新动态。关注我们的微博
官方微博: &&&

我要回帖

更多关于 setneedslayout作用 的文章

 

随机推荐