在运行时更改自动布局

Posted

技术标签:

【中文标题】在运行时更改自动布局【英文标题】:Change Autolayout at runtime 【发布时间】:2015-11-12 22:48:34 【问题描述】:

是否可以约束?

我知道您可以更改常量,但是您将如何更改不同的属性。

比如 NSLayoutAttributeTop 到 NSLayoutAttributeBottom?

这是我希望实现的一个简单示例,它将在左上角设置一个标签,然后当您按下按钮时,它将在右下角设置一个标签。

初始约束按预期工作,点击按钮无法按预期工作并抛出臭名昭著的“无法同时满足约束”。

这是我正在使用的代码:

    - (void)viewDidLoad

    [super viewDidLoad];
    [self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
    self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
                                                    attribute:NSLayoutAttributeTop
                                                    relatedBy:NSLayoutRelationEqual
                                                       toItem:self.view
                                                    attribute:NSLayoutAttributeTop
                                                   multiplier:1.0
                                                     constant:0.0];

    self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
                                                    attribute:NSLayoutAttributeLeft
                                                    relatedBy:NSLayoutRelationEqual
                                                       toItem:self.view
                                                    attribute:NSLayoutAttributeLeft
                                                   multiplier:1.0
                                                     constant:0.0];

    [self.view addConstraint:self.constraintA];
    [self.view addConstraint:self.constraintB];


- (IBAction)tappedChange:(id)sender


    [self.view removeConstraints:@[ self.constraintA, self.constraintB ]];

    self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
                                                    attribute:NSLayoutAttributeBottom
                                                    relatedBy:NSLayoutRelationEqual
                                                       toItem:self.view
                                                    attribute:NSLayoutAttributeBottom
                                                   multiplier:1.0
                                                     constant:0.0];

    self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
                                                    attribute:NSLayoutAttributeRight
                                                    relatedBy:NSLayoutRelationEqual
                                                       toItem:self.view
                                                    attribute:NSLayoutAttributeRight
                                                   multiplier:1.0
                                                     constant:0.0];
    [self.view addConstraint:self.constraintA];
    [self.view addConstraint:self.constraintB];
    [self.view setNeedsLayout];

感谢您的宝贵时间。

【问题讨论】:

【参考方案1】:

您只需要在 ios 7 及更低版本上执行 Remove/Recreate/Add 约束舞蹈。如果您正在为现代 iOS(8 及更高版本)编写代码,您可以一次创建所有约束,然后在任何给定时间将 .active 属性设置在您想要的任何 NSLayoutConstraint 实例上。

// Move to right
self.leadingConstraint.active = false;
self.trailingConstraint.active = true;

// Move to bottom
self.topConstraint.active = false;
self.bottomConstraint.active = true;

如果您使用的是 Interface Builder,您可以创建所有需要的约束(注意默认情况下未激活的灰色约束)。

然后,当按下按钮时,您可以停用旧约束并激活新约束。

如果您不确定显示的视图,您可以暂停应用执行并在调试器中使用以下私有 API 打印出完整的视图和约束列表:

po [[UIWindow keyWindow] _autolayoutTrace]

【讨论】:

这不是我在做的吗?我只是尝试创建两个新的约束,而不是在删除它们后重用现有的约束,但它仍然不起作用。错误太长,无法在此处包含,但最重要的是:“无法同时满足约束。可能以下列表中的至少一个约束是您不想要的......” Brian,这就是为什么我想知道这些视图的其他限制是什么。您必须在右侧已经有其他约束,或者这些新约束与之冲突的底部,等等。 该错误消息的内容实际上是您需要阅读和理解的内容。 这是仅有的两个约束。代码或 Interface Builder 中没有设置任何内容。我假设因为这些是 UILabel 高度和宽度是固有的,所以这将提供所需的 4 个约束。 Brian,您可以使用po [[UIWindow keyWindow] _autolayoutTrace] 查看完整的视图列表和任何歧义。【参考方案2】:

就我个人而言,我喜欢使用 Masonry 来管理代码中的约束 - 这样可以减少编写量,更容易阅读您六个月前写的内容,而且学习曲线也不那么令人头疼。

例如:

// Define some constraints, making the top one a @property:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) 
    self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
];

// Update the top constraint to match the bottom of the superview instead of the top
[view1 mas_makeConstraints:^(MASConstraintMaker *make) 
    self.topConstraint = make.top.equalTo(superview.mas_bottom).with.offset(padding.bottom);
];

【讨论】:

Apple 也更接近这一点,NSLayoutAnchor(不幸的是,仅限 iOS 9)。

以上是关于在运行时更改自动布局的主要内容,如果未能解决你的问题,请参考以下文章

使用自动布局更改按钮位置

如何使用自动调整大小和自动布局并更改其默认行为? [关闭]

自动布局:窗口调整大小时如何更改框架的原点?

如何设置自动布局约束常量通用或设备特定?

iOS - 使用自动布局在旋转时更改背景图像

如何在启用自动布局的情况下更改 iOS6 中的 UIView 大小?