【问题标题】:Is there any way to add constraint between a view and the top layout guide in a xib file?有没有办法在 xib 文件中的视图和顶部布局指南之间添加约束?
【发布时间】:2013-10-05 09:00:26
【问题描述】:

在 iOS 7 中,我们现在可以在视图和顶部布局指南之间添加一个约束,我认为这对于解决 iOS7 中的状态栏偏移问题非常有用(尤其是当视图中没有导航栏时)。

在故事板文件中,我可以轻松添加这种约束。只需按住控制键,然后将视图拖到容器中,它将显示“顶部空间到顶部布局指南”选项。

但是当我在一个 xib 文件中做同样的操作时,这个选项就消失了。

那么,有没有办法在 xib 文件中添加这种约束?还是我必须用代码添加它们?

【问题讨论】:

  • 我建议将“另一个问题”作为一个单独的问题提出。
  • 我有相反的问题——有没有办法限制容器顶部而不是故事板中的顶部布局指南?

标签: ios xcode ios7 xcode5


【解决方案1】:

Cao Huu Loc's answer with swift 4

extension NSLayoutConstraint {

    func constraintReplacing(firstItemWith newFirstItem: UILayoutSupport, withAttribute newFirstAttribute: NSLayoutAttribute) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: newFirstItem, attribute: newFirstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant)
    }

    func constraintReplacing(secondItemWith newSecondItem: UILayoutSupport, withAttribute newSecondAttribute: NSLayoutAttribute) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: firstItem as Any, attribute: firstAttribute, relatedBy: relation, toItem: newSecondItem, attribute: newSecondAttribute, multiplier: multiplier, constant: constant)
    }
}

class YourViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        for constraint in view.constraints {
            if constraint.firstItem === view && constraint.firstAttribute == .top {
                let newConstraint = constraint.constraintReplacing(firstItemWith: topLayoutGuide, withAttribute: .bottom)
                view.removeConstraint(constraint)
                view.addConstraint(newConstraint)
            }
            if constraint.secondItem === view && constraint.secondAttribute == .top {
                let newConstraint = constraint.constraintReplacing(secondItemWith: topLayoutGuide, withAttribute: .bottom)
                view.removeConstraint(constraint)
                view.addConstraint(newConstraint)
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    从 iOS 9 开始,您还可以使用 topLayoutGuide 的 anchors 更简单:

    • 斯威夫特 3

    view.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor).isActive = true

    • ObjC

    [controller.view.topAnchor constraintEqualToAnchor:controller.topLayoutGuide.bottomAnchor].active = YES;

    【讨论】:

    • 迄今为止最好的解决方案!
    【解决方案3】:

    当然,您不仅可以通过编程方式在视图和top layout guidebottom layout guide 之间添加约束,还可以在KVConstraintExtensionsMaster 库的帮助下删除和访问视图和top and bottom layout guide 之间的约束.

    // create containerView
    UIView *containerView = [UIView prepareNewViewForAutoLayout];
    [containerView setBackgroundColor:[UIColor brownColor]];
    [self.view addSubview:containerView];
    
    // To add Top and Bottom Layout Guide constraint of containerView
    [self applyTopLayoutGuideConstraintToView:containerView withPadding:0];
    [self applyBottomLayoutGuideConstraintToView:containerView withPadding:50];
    
    // To access top Layout Guide Constraint and update it's constant value.
    NSLayoutConstraint *topLayoutGuideConstraint = [self accessAppliedTopLayoutGuideConstraintFromView:containerView];
    topLayoutGuideConstraint.constant = 50;
    
    // To access bottom Layout Guide Constraint and update it's constant value with animation
    NSLayoutConstraint *bottomLayoutGuideConstraint = [self accessAppliedBottomLayoutGuideConstraintFromView:containerView];
    bottomLayoutGuideConstraint.constant = 80;
    [self.view updateModifyConstraintsWithAnimation:NULL]; // call this for animation
    
    // To remove Top and Bottom Layout Guide constraint of containerView
    [self removeAppliedTopLayoutGuideConstraintFromView:containerView];
    [self removeAppliedBottomLayoutGuideConstraintFromView:containerView ];
    

    【讨论】:

      【解决方案4】:

      这是我的替代解决方案。

      找到一个 UIView 作为枢轴,将其顶部布局约束设置为容器顶部的固定垂直空间。

      Control-拖动这个布局约束为一个IBOutlet,比如

      @property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;
      

      最后,只需像下面这样覆盖 UIViewController 的 viewWillLayoutSubviews 方法

      - (void)viewWillLayoutSubviews
      {
          [super viewWillLayoutSubviews];
      
          self.topLayoutConstraint.constant = [self.topLayoutGuide length] + YOUR_TOP_CONSTRSINT;
      }
      

      所有其他视图的顶部约束都基于此枢轴视图,全部完成:)

      【讨论】:

      • 这不能很好地处理 topLayoutGuide 移动(比如用户正在通话或使用 GPS 或其他原因导致 topLayoutGuide 增加)
      【解决方案5】:

      直到现在,即使使用 XCode 6,我也无法将控件与 .xib 文件的顶部布局指南对齐。相反,我正在使用另一种方式。

      首先,在界面生成器上,我仍然将控件对齐到 viewcontroler 视图的上边框。

      然后,在 viewDidLoad 方法中,我替换了一些约束,以便它们与顶部布局指南而不是主视图对齐:

      - (void)viewDidLoad
      {
          [super viewDidLoad];
      
          NSArray *constraints = self.view.constraints;
          for (NSLayoutConstraint *constraint in constraints) {
              if ( (constraint.firstItem == self.view) && (constraint.firstAttribute == NSLayoutAttributeTop) ) {
                  NSLayoutConstraint *newConstraint = [self constraint:constraint replaceFirstItemBy:self.topLayoutGuide attribute:NSLayoutAttributeBottom];
                  [self.view removeConstraint:constraint];
                  [self.view addConstraint:newConstraint];
              } else if ( (constraint.secondItem == self.view) && (constraint.secondAttribute == NSLayoutAttributeTop) ) {
                  NSLayoutConstraint *newConstraint = [self constraint:constraint replaceSecondItemBy:self.topLayoutGuide attribute:NSLayoutAttributeBottom];
                  [self.view removeConstraint:constraint];
                  [self.view addConstraint:newConstraint];
              }
          }
      }
      
      - (NSLayoutConstraint*)constraint:(NSLayoutConstraint*)constraint replaceFirstItemBy:(id)newItem attribute:(NSLayoutAttribute)newAttribute {
          UILayoutPriority priority = constraint.priority;
          NSLayoutRelation relation = constraint.relation;
          id secondItem = constraint.secondItem;
          NSLayoutAttribute secondAttribute = constraint.secondAttribute;
          CGFloat multiplier = constraint.multiplier;
          CGFloat constant = constraint.constant;
      
          NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:newItem attribute:newAttribute relatedBy:relation toItem:secondItem attribute:secondAttribute multiplier:multiplier constant:constant];
          newConstraint.priority = priority;
          return newConstraint;
      }
      
      - (NSLayoutConstraint*)constraint:(NSLayoutConstraint*)constraint replaceSecondItemBy:(id)newItem attribute:(NSLayoutAttribute)newAttribute {
          UILayoutPriority priority = constraint.priority;
          id firstItem = constraint.firstItem;
          NSLayoutAttribute firstAttribute = constraint.firstAttribute;
          NSLayoutRelation relation = constraint.relation;
          CGFloat multiplier = constraint.multiplier;
          CGFloat constant = constraint.constant;
      
          NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:firstItem attribute:firstAttribute relatedBy:relation toItem:newItem attribute:newAttribute multiplier:multiplier constant:constant];
          newConstraint.priority = priority;
          return newConstraint;
      }
      

      认为这不是最好的方法,因为我们替换了我们在界面生成器上定义的对象。但这可能是我们可以考虑的另一种方式。

      【讨论】:

        【解决方案6】:

        我认为它们没有出现在 XIB 中的原因是在这种情况下不了解视图控制器层次结构,而在故事板中,IB 可以从视图控制器层次结构中分辨出指南的位置在。即如果包含在一个UINavigationController中,它可以确定顶部布局指南在导航工具栏的下方。

        【讨论】:

        • 没关系。这就是这些指南的全部意义所在。您只需与它们对齐,如果该 VC 在导航控制器内,它具有一定的顶部布局指南大小。如果它显示为一个没有可见状态栏的模态 VC,那么它的大小为 0,等等。VC 可以而且应该能够相对于它的指南进行布局,而不知道它们的大小是多少(在设计时)。
        【解决方案7】:

        您应该参考以下示例,这肯定会帮助您解决问题。我是从http://developer.apple.com 那里得到的。

        [button setTranslatesAutoresizingMaskIntoConstraints: NO];
        id topGuide = myViewController.topLayoutGuide;
        NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings (button, topGuide);
        
        [myViewController.view addConstraints:
            [NSLayoutConstraint constraintsWithVisualFormat: @"V:[topGuide]-20-[button]"
            options: 0
            metrics: nil
            views: viewsDictionary]
        ];
        

        【讨论】:

        • 我在视图控制器的视图中添加了一个 UIView,我希望在 iOS7 的情况下它应该从 20 开始,对于 iOS6 它应该从 0 开始。我使用上面的代码(代替按钮我是使用我的观点)但它不工作可以知道如何解决这个问题?
        • @NuzhatZari 你可以在这里明确检查系统版本stackoverflow.com/questions/7848766/…
        • 如果这些约束也可以在 Xcode 上为 XIB 设置就好了...... Xcode 应该只提供它(不管视图控制器层次结构的知识)。
        • 有没有办法在状态栏下布局所有VC的子视图,没有约束?喜欢在故事板中使用顶部布局指南?
        猜你喜欢
        • 2013-12-11
        • 2015-04-30
        • 2015-09-25
        • 2015-06-25
        • 2021-08-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-09
        相关资源
        最近更新 更多