无法更改为IB中的不同大小类定义的约束IBOutlet

时间:2015-06-05 09:57:43

标签: ios autolayout interface-builder nslayoutconstraint size-classes

我为这个案子制作了一个特殊的测试应用程序。 (对不起,它已被删除)

我在Storyboard的控制器视图中添加了一个视图,在Interface Builder中设置了AutoLayout约束,并且其中一个(垂直空间)对于不同的大小类是不同的。 Screenshot from IB

因此Any height, Any width的值为100,Regular height, Regular width的值为0。 它的效果很好,iPhone上的垂直距离是100,在iPad上它是0。

此外,我为此约束设置了IBOutlet,并希望在运行时将其更改为10

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topVerticalConstraint;
似乎我无法改变它,因为它没有效果

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.topVerticalConstraint.constant = 10; // it doesn't work
}

虽然我在Regular height, Regular width中移除Interface Builder的值时会有效。

我是否想念尺寸类?

6 个答案:

答案 0 :(得分:13)

问题是,在-viewWillLayoutSubviews-viewDidLayoutSubviews之间发生布局事件之前,约束尚未完全定义,其中IB的所有参数都会发挥作用。 我的经验法则是:

  • 如果您使用框架手动定位视图,则可以在-viewDidLoad之前完成,
  • 如果您使用自动布局约束进行定位,请尽早进行调整-viewDidLayoutSubviews;

第二个陈述仅考虑对IB中的约束进行代码调整。您在-viewDidLoad中进行的调整将被布局期间在IB中设置的参数覆盖。如果使用代码添加约束,可以在-viewDidLoad中设置它们,因为没有任何内容可以覆盖它们。

我已经改变了你的代码并且它有效:

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topVerticalConstraint;
@property (weak, nonatomic) IBOutlet UIView *square;

@property (assign, nonatomic) BOOL firstLayout;

@end

@implementation ViewController

- (void)viewDidLoad

{
    [super viewDidLoad];

    self.firstLayout = YES;
}

- (void)viewDidLayoutSubviews {

    [super viewDidLayoutSubviews];

    if (self.firstLayout) {

        self.topVerticalConstraint.constant = 10;
        self.firstLayout = NO;

    }
}

@end

请注意,在ViewController的生命周期内多次调用-viewDidLayoutSubviews,因此您必须确保在初始加载时只进行一次调整。

答案 1 :(得分:4)

问题:

如果您为IB中的不同大小类设置了不同的值,请执行以下约束:

enter image description here

然后您无法在代码中更改常量值:

self.adHeightConstraint.constant = 0;  // value set to 0
[self.view layoutIfNeeded];  // value get back to IB value (44 or 36)

在这种情况下,您可能会看到您的常量值仅在视图重新计算之前一直存在。因此,在[self.view layoutIfNeeded]之后,常量重置的值回到IB中设置的任何值。

解决方案:

  1. 添加具有所需值的相同属性的第二个约束(在我的情况下,它是高度)。您可以在IB中设置此值或在代码中更改它。
  2. 为此新约束设置低优先级。由于它的优先级较低,因此不会发生任何冲突。
  3. 现在,当您需要应用新常量时,简单禁用第一个约束

    self.adHeightConstraint.active = NO;
    [self.view layoutIfNeeded];
    

答案 2 :(得分:1)

我遇到了同样的问题,但它似乎与viewDidLoad vs viewDidLayoutSubviews没有任何关系。 Interface Builder可以管理不同大小类的备用约束常量,但是当您尝试在代码中更新NSLayoutConstraint.constant时,该常量不与任何特定大小类(包括活动大小类)相关联。

来自Apple文档,Changing Constraint Constants for a Size Class(XCode 7,Interface Builder) XCode 7 Interface Builder constraints for multiple size classes

我的解决方案是从IB中删除备用常量,并在代码中管理基于大小的约束常量开关,仅针对在代码中更新/修改的特定约束。任何仅通过storyboard / IB管理的约束都可以正常使用备用大小常量。

// XCode 7.0.1, Swift 2.0
static var isCompactHeight : Bool = false;
static var heightOffset : CGFloat {
    return (isCompactHeight ? compactHeightOffset : regularHeightOffset);
}

/* applyTheme can be called as early as viewDidLoad */
func applyTheme() {
    // This part could go wherever you handle orientation changes
    let appDelegate = UIApplication.sharedApplication().delegate;
    let window = appDelegate?.window;
    let verticalSizeClass = window??.traitCollection.verticalSizeClass ?? UIUserInterfaceSizeClass.Unspecified;

    isCompactHeight = (verticalSizeClass == UIUserInterfaceSizeClass.Compact);

    // Use heightOffset
    changingConstraint.constant = heightOffset;
}

我希望稍后版本的Swift / XCode会引入getter& amp;将基于大小的备选项考虑在内的setter,反映了已经通过IB提供的功能。

答案 3 :(得分:0)

我在示例项目中查看相同的场景,它可能会忘记将NSLayoutConstraint topVerticalConstraint与故事板连接。

答案 4 :(得分:0)

在viewDidLayoutSubviews中更改约束的常量

- (void) viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    if (IS_IPHONE4) {
        self.topConstraint.constant = 10;
        self.bottomButtonTop.constant = 10;
        self.pageControlTopConstraint.constant = 5;
    }
}

答案 5 :(得分:0)

最简单的解决方案:

if (self.view.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular && self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    // for iPhone
    cnicTopConstraint.constant = -60;    
} else {
    // for iPad
    cnicTopConstraint.constant = -120;
}