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

Can't change Constraint IBOutlet that is defined for different size classes in IB

我专门针对这个案例做了一个测试app。 (很抱歉,它已经被删除了)

我在 Storyboard 的控制器视图上添加了一个视图,在 Interface Builder 中设置了 AutoLayout 约束并使其中一个(垂直 space)对于不同的尺寸是不同的 类。 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
}

尽管当我删除 Interface BuilderRegular height, Regular width 的值时它起作用了。

我是不是漏掉了尺码 类?

问题是在 -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

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

我在示例项目中检查了它正在运行的相同场景,可能是您忘记将 NSLayoutConstraint topVerticalConstraint 与故事板连接。

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

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

我的解决方案是从 IB 中删除备用常量,并在代码中管理基于大小的约束常量开关,仅针对代码中 updated/modified 的那些特定约束。任何仅通过 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 和 setter,反映 IB 已经提供的功能。

问题:

如果您在 IB 中为不同的大小设置不同的值 类,如下所示:

那么您不能像这样在代码中更改常量值:

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];
    

更改 viewDidLayoutSubviews 中的约束常量

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

最简单的解决方案:

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