有什么方法可以在 xib 文件中添加视图和顶部布局指南之间的约束吗?

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

在故事板文件中,我可以很容易地添加这种约束。只要按住控制键,然后将视图拖动到容器中,就会显示“ Top Space to Top Layout Guide”选项。

enter image description here

但是当我在 xib 文件中执行相同的操作时,这个选项将消失。

enter image description here

那么,有没有办法在 xib 文件中添加这种约束呢?还是要我加上代码?

47070 次浏览

你应该参考下面的例子,这肯定会对你的问题有所帮助。

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

我认为它们之所以没有出现在 XIB 中,是因为在这种情况下没有视图控制器层次结构的知识,而在故事板中,IB 可以从视图控制器层次结构中判断出视图所在的向导位置。I.e 如果它包含在 UINavigationController 中,则可以确定顶部的布局指南位于导航工具栏的下方。

直到现在,即使使用 XCode6,我也不能将控件对齐到顶部的布局指南。XIB 档案。相反,我在用另一种方式。

首先,在 Interface Builder 上,我仍然将控件对齐到视图控制器视图的顶部边框。

然后,在 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;
}

认为这不是最好的方法,因为我们替换了在 Interface Builder 上定义的对象。但这也许是我们可以考虑的另一种方式。

这是我的替代方案。

以 UIView 为轴心,将其顶部布局约束设置为容器顶部的固定垂直空间。

Control-将此布局约束拖动为 IBOutlet,如

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

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

- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];


self.topLayoutConstraint.constant = [self.topLayoutGuide length] + YOUR_TOP_CONSTRSINT;
}

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

当然,您不仅可以通过编程方式在视图和 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 ];

在 iOS9中,你也可以使用 topLayoutGuide 的 anchors:

  • Swift 3

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

  • 目的

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

曹胡洛迅速的回答了四个

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)
}
}
}
}