【问题标题】:Can't change Constraint IBOutlet that is defined for different size classes in IB无法更改为 IB 中不同大小类别定义的约束 IBOutlet
【发布时间】:2015-08-20 05:35:31
【问题描述】:

我为此案例制作了一个特殊的测试应用程序。 (很抱歉它已经被删除了)

我在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
}

虽然当我在Interface Builder 中删除Regular height, Regular width 的值时它会起作用。

我错过了一些关于尺码等级的事情吗?

【问题讨论】:

  • 你是否在界面构建器中正确绑定了 NSLayoutConstraint 和顶部约束?
  • 非常确定约束尚未在 -viewDidLoad 中设置。您是否尝试过更改 -viewDidLayoutSubviews 中的约束?
  • @UtsavParikh 当然。相同的项目示例效果很好,当我在 Interface Builder 中删除 w R h R 的约束时。
  • @NKorotkov 是的,替换为 -viewDidLayoutSubviews 有帮助。非常感谢。您的意思是,该属性为空还是尚未应用于查看?
  • @Sander 属性不为零,但您在 -viewDidLoad 中对其所做的更改将被 IB 中设置的参数覆盖。

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


【解决方案1】:

问题是直到布局事件发生在-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 的生命周期内被多次调用,因此您必须确保您的调整仅在初始加载时发生一次。

【讨论】:

    【解决方案2】:

    问题:

    如果你在 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];
      

    【讨论】:

    • 这是一个完美的答案!我将优先级 999 用于第二个约束。
    【解决方案3】:

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

    来自 Apple 文档,Changing Constraint Constants for a Size Class(XCode 7,界面生成器)

    我的解决方案是从 IB 中删除备用常量,并在代码中管理基于大小的约束常量切换,仅适用于在代码中更新/修改的那些特定约束。任何仅通过情节提要/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 已经可用的功能。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        在 viewDidLayoutSubviews 中改变约束的常量

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

        【讨论】:

          【解决方案6】:

          最简单的解决方案:

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

          【讨论】:

            猜你喜欢
            • 2016-10-29
            • 2020-12-09
            • 1970-01-01
            • 1970-01-01
            • 2016-05-15
            • 1970-01-01
            • 1970-01-01
            • 2016-02-09
            • 2018-02-22
            相关资源
            最近更新 更多