UITableViewCell 上的自动布局问题 [英] Trouble with AutoLayout on UITableViewCell

查看:15
本文介绍了UITableViewCell 上的自动布局问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 xcode 5 项目上遇到自动布局问题.我在内部使用带有导航控制器的普通视图控制器.我在上半部分有一个 MKMapView,在下半部分有一个 UITableView.我正在使用 storyboards,并且已经配置了原型 UITableViewCell,但是我正在通过代码添加约束.我已经仔细检查了原型中的每个控件,并没有看到那里配置了任何约束.当我为 UITableViewCell 添加约束时,会出现我的问题.我在单元格中有以下代码:

-(void)updateConstraints {[超级更新约束];//首先删除旧的约束[自我删除约束:self.constraints];[self.nameLabel removeConstraints:self.nameLabel.constraints];[self.addressLabel removeConstraints:self.nameLabel.constraints];[self.rentableSquareFeetLabel removeConstraints:self.rentableSquareFeetLabel.constraints];[self.lastSaleAmountLabel removeConstraints:self.lastSaleAmountLabel.constraints];[self.lastSaleDateLabel removeConstraints:self.lastSaleAmountLabel.constraints];[self.thumbnailImageView removeConstraints:self.thumbnailImageView.constraints];//然后设置约束NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_thumbnailImageView, _nameLabel, _rentableSquareFeetLabel, _lastSaleAmountLabel, _addressLabel, _lastSaleDateLabel);[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_thumbnailImageView(60)]-[_nameLabel(<=200)]-(>=8)-[_rentableSquareFeetLabel]-(>=8)-[_lastSaleAmountLabel]|"选项:0 指标:nil 视图:viewsDictionary]];[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-(-4)-[_addressLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_lastSaleAmountLabel]-(-4)-[_lastSaleDateLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];}

我在调试控制台中得到以下信息.异常由第一行 addConstraints 触发.如果我只是继续这些,那么最终一切都会按原样显示,因为看起来 xcode 正在选择打破正确的约束:

2013-09-25 15:07:14.169 PECProperties[32381:a0b] 无法同时满足约束.以下列表中的至少一个约束可能是您不想要的.试试这个:(1)查看每个约束并尝试找出您不期望的;(2) 找到添加了一个或多个不需要的约束的代码并修复它.(注意:如果您看到不理解的 NSAutoresizingMaskLayoutConstraints,请参阅 UIView 属性 translatesAutoresizingMaskIntoConstraints 的文档)("<NSIBPrototypingLayoutConstraint:0x9d56c70 'IB 在构建时为固定框架的视图自动生成'H:|-(0)-[UIImageView:0x9d558f0](LTR) (名称:'|':UITableViewCellContentView:0x9d55620)>","<NSIBPrototypingLayoutConstraint:0x9d56d20 'IB 在构建时自动生成用于固定框架的视图'H:[UIImageView:0x9d558f0(60)]>","<NSIBPrototypingLayoutConstraint:0x9d56d80 'IB 在构建时自动生成用于具有固定框架的视图'H:|-(78)-[UILabel:0x9d559e0](LTR) (名称:'|':UITableViewCellContentView:0x9d55620)>","

第三个 NSIBPrototypingLayoutConstraint 显示了视图边缘和标签之间的 78 个点.那是原型大致定位的位置(如果我在原型中移动它,我会在调试控制台中看到约束的变化),但这与我自己的图像视图和标签之间的标准"距离约束相冲突.

我已尝试在视图控制器的 cellForRowAtIndexPath 中设置 translatesAutoresizingMaskIntoConstraints=NO,但这似乎也无济于事.如何修复布局?

解决方案

这里有几件事要介绍:

  1. 您遇到的 NSIBPrototypingLayoutConstraint 约束(并且会导致异常)由 Interface Builder 自动生成,以使您的 Storyboard 或 XIB 视图布局明确.这样做非常偷偷摸摸,但它会自动添加所需的最小约束,以便完全指定每个模棱两可的视图的位置和大小.这是对 Xcode 4 的更改,因为在 Xcode 4 中,您不能在 Interface Builder 中使用模棱两可的布局.使用 Xcode 5 及更高版本您可以,但是如果您的布局在编译时不明确,IB 会为您自动生成这些约束.

    解决此问题的方法是在 Interface Builder 中添加所需的最小约束,以便每个视图的位置 &size 已完全指定,然后选择每个不需要的约束,转到右侧边栏的属性检查器,并选中 Placeholder - Remove at build time 旁边的框.

    此复选框不仅会删除您添加的约束,而且最重要的是它会阻止自动生成的 IB 约束取而代之!(正如您可以想象的那样,当您在 IB 中有多个视图并希望在代码中管理所有约束时,这是非常乏味的.因此,您可能希望避免将 IB 完全用于您打算在其中实现 Auto 的视图层次结构以编程方式布局.)

    Placeholder 约束和 Uninstalled 约束有什么区别?这是我的了解更多详细信息,以及此答案.为了防止多次添加约束,请使用一个布尔标志(例如 hasSetupConstraints),一旦你第一次设置了你的约束,并且如果 updateConstraintscode> 再次调用,如果您没有要添加的新约束,您可以立即返回.请参阅此问题进行进一步讨论.

  2. 您用来移除约束的代码可能无法完全运行.这是因为 [view removeConstraints:view.constraints] 只会删除已添加到 view 的约束 - 请记住,可以将约束添加到视图的任何公共超级视图它们约束——并且添加到 view 的约束可能不是唯一影响 view 布局的约束!如果你需要删除一些约束,你应该在一个属性中存储对每个约束的引用(例如,一个包含 NSLayoutConstraint 实例的 NSArray 属性),然后使用 NSLayoutConstraint 或 PureLayout 开源库.您应该只停用/删除尽可能少的约束,因为这样做的计算成本很高.另一方面,更改任何约束的 constant 非常有效且值得鼓励,您无需删除或重新添加约束即可.

I'm having trouble with autolayout on an xcode 5 project. I am using a plain view controller inside with a navigation controller. I have a MKMapView on the top half and a UITableView on the bottom half. I am using storyboards, and have configured the prototype UITableViewCell, but I am adding the constraints through code. I have double-checked every control in the prototype and don't see any constraints configured there. My problem occurs when I add the constraints for the UITableViewCell. I have the following code in the cells:

-(void)updateConstraints {
    [super updateConstraints];
    //first remove old constraints
    [self removeConstraints:self.constraints];
    [self.nameLabel removeConstraints:self.nameLabel.constraints];
    [self.addressLabel removeConstraints:self.nameLabel.constraints];
    [self.rentableSquareFeetLabel removeConstraints:self.rentableSquareFeetLabel.constraints];
    [self.lastSaleAmountLabel removeConstraints:self.lastSaleAmountLabel.constraints];
    [self.lastSaleDateLabel removeConstraints:self.lastSaleAmountLabel.constraints];
    [self.thumbnailImageView removeConstraints:self.thumbnailImageView.constraints];

    //then set up constraints
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_thumbnailImageView, _nameLabel, _rentableSquareFeetLabel, _lastSaleAmountLabel, _addressLabel, _lastSaleDateLabel);
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_thumbnailImageView(60)]-[_nameLabel(<=200)]-(>=8)-[_rentableSquareFeetLabel]-(>=8)-[_lastSaleAmountLabel]|" options:0 metrics:nil views:viewsDictionary]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-(-4)-[_addressLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_lastSaleAmountLabel]-(-4)-[_lastSaleDateLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];
}

I am getting the following in the debugging console. The exception is triggered by the first addConstraints line. If I just continue through those then eventually everything shows up as it should be, as it looks like xcode is choosing to break the correct constraint:

2013-09-25 15:07:14.169 PECProperties[32381:a0b] Unable to simultaneously satisfy constraints.  Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)  (
    "<NSIBPrototypingLayoutConstraint:0x9d56c70 'IB auto generated at build time for view with fixed frame' H:|-(0)-[UIImageView:0x9d558f0](LTR)   (Names: '|':UITableViewCellContentView:0x9d55620 )>",
    "<NSIBPrototypingLayoutConstraint:0x9d56d20 'IB auto generated at build time for view with fixed frame' H:[UIImageView:0x9d558f0(60)]>",
    "<NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR)   (Names: '|':UITableViewCellContentView:0x9d55620 )>",
    "<NSLayoutConstraint:0x9d53830 H:[UIImageView:0x9d558f0]-(NSSpace(8))-[UILabel:0x9d559e0]>" )

Will attempt to recover by breaking constraint  <NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR)   (Names: '|':UITableViewCellContentView:0x9d55620 )>

Break on objc_exception_throw to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

The third NSIBPrototypingLayoutConstraint shows 78 points between the edge of the view and a label. That is where the prototype is positioned roughly (and if I move it in the prototype, I see the change in the constraint in the debugging console), but that conflicts with my own constraint of "standard" distance between the image view and the label.

I have tried setting the translatesAutoresizingMaskIntoConstraints=NO in the view controller's cellForRowAtIndexPath, but that doesn't seem to be helping either. How can I fix the layout?

解决方案

A few things to cover here:

  1. The NSIBPrototypingLayoutConstraint constraints that you're running into (and that are causing exceptions) are auto-generated by Interface Builder in order to make your Storyboard or XIB view layout non-ambiguous. It's pretty sneaky about doing this, but it's automatically adding the minimum constraints required so that the position and size of each ambiguous view becomes fully specified. This is a change from Xcode 4, because in Xcode 4 you could not have ambiguous layouts in Interface Builder. With Xcode 5 and later you can, however IB will auto-generate these constraints for you if your layout is ambiguous at compile time.

    The way to fix this issue is to add the minimum required constraints in Interface Builder so that each view's position & size is fully specified, then select each of these unwanted constraints, go to the right sidebar Attributes inspector, and check the box next to Placeholder - Remove at build time.

    Not only does this checkbox remove the constraint you added, but most importantly it will prevent the auto-generated IB constraint from taking its place! (As you can imagine, this is quite tedious when you have a number of views in IB and want to manage all your constraints in code. For this reason you may want to avoid using IB entirely for view hierarchies in which you intend to implement Auto Layout programmatically.)

    What is the difference between a Placeholder constraint and an Uninstalled constraint? Here's a slide from my Adaptive Auto Layout talk (video) (PDF slides) comparing the two:

  2. In updateConstraints, you don't want to remove constraints and re-add them like you have there. Why not? Essentially, it's terrible for performance, and I have confirmed with Apple engineers that this is not a good idea. See the question/answer I have posted here for some more details, as well as this answer. In order to prevent constraints being added more than once, use a boolean flag (e.g. hasSetupConstraints) that you set to YES once you have set up your constraints the first time, and if updateConstraints is called again you can just return immediately if you have no new constraints to add. See this question for further discussion.

  3. The code you're using to remove constraints may not work completely. This is because [view removeConstraints:view.constraints] will only remove constraints that have been added to view -- remember that constraints can be added to any common superview of the views they constrain -- and the constraints added to view may not be the only ones affecting the layout of view! If you need to remove a number of constraints, you should store a reference to each of those constraints in a property (e.g. an NSArray property containing NSLayoutConstraint instances), and then deactivate/remove those constraints using the API on NSLayoutConstraint or the PureLayout open-source library. You should only deactivate/remove as few constraints as possible because it is computationally expensive to do so. On the other hand, changing the constant of any constraint is very efficient and encouraged, and you don't need to remove or re-add the constraint to do that.

这篇关于UITableViewCell 上的自动布局问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆