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


75

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

在情节提要文件中,我可以轻松添加此类约束。只需按住Control键,然后将视图拖到容器中,它将显示“顶部空间到顶部布局指南”选项。

在此处输入图片说明

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

在此处输入图片说明

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


我建议将“另一个问题”作为一个单独的问题提出。
hpique 2013年

1
我有一个相反的问题–是否有任何方法可以限制到情节提要中的容器顶部而不是顶部布局指南?
Pavel Alexeev 2015年

Answers:


77

您应该参考以下示例,这绝对可以帮助您解决问题。我是从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开始。工作可以知道如何解决这个问题?
Nuzhat Zari

@NuzhatZari您可以在此处显式检查系统版本stackoverflow.com/questions/7848766/…–
joslinm

14
如果也可以在Xcode上为XIB设置这些约束,那就太好了……Xcode应该只提供它(不管对视图控制器层次结构的了解如何)。
里卡多·桑切斯·塞兹

1
没有约束,是否可以在状态栏下布置所有VC的子视图?喜欢在情节提要中使用顶部布局指南吗?
User123335511231 2015年

14

这是我的替代解决方案。

找到一个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增大)
匿名

9

在iOS 9中,您还可以使用topLayoutGuide的操作更加简单anchors

  • 迅捷3

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

  • 对象

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


迄今为止最好的解决方案!
卡佩

6

到目前为止,即使使用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

当然,你不仅可以添加约束视图和之间top layout guidebottom layout guide通过编程方式,也可以将视图和之间移除和存取限制top and bottom layout guide的帮助下KVConstraintExtensionsMaster库。

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

5

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


3
没关系 这就是这些指南的重点。您只需将它们对齐,并且如果该VC在导航控制器内部,则它具有一定的顶部布局指南大小。如果将其表示为模式VC,且没有可见的状态栏,则其大小为0,依此类推。VC可以并且应该能够相对于其布局进行布局,而无需知道其尺寸(在设计时)。
CIFilter

0

曹Hu禄的答案迅速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)
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.