Is there any way to add constraint between a view and the top layout guide in a xib file?

后端 未结 7 2318
时光说笑
时光说笑 2020-12-07 15:44

In iOS 7, we can now add a constraint between a view and the top layout guide, which I think is very useful to solve the status bar offset issue in iOS7(especially when ther

相关标签:
7条回答
  • 2020-12-07 15:53

    I think the reason they don't show up in the XIB is that there is no knowledge of the view controller hierarchy in that situation, whereas in a story board IB can tell where the guides are from the view controller hierarchy the view is in. I.e If it is contained in a UINavigationController, it can determine that the top layout guide is below the navigation toolbar.

    0 讨论(0)
  • 2020-12-07 15:57

    Until now even using XCode 6 I cannot align controls to top layout guide on .xib files. Instead, I am using an alternative way.

    First, on interface builder, I still align controls to top border of viewcontroler's view.

    Then, in viewDidLoad method, I replace some constraints so they will align to top layout guide instead of main view:

    - (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;
    }
    

    Think this is not the best way because we replace objects that we define on interface builder. But it may be an alternative way that we can think about.

    0 讨论(0)
  • 2020-12-07 15:59

    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)
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-07 16:09

    You should refer the following example, this will definitely help you for your problem. I got this from 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]
    ];
    
    0 讨论(0)
  • 2020-12-07 16:09

    Of course, You can not only add constraint between a view and the top layout guide or bottom layout guide by programmatically but also you can remove and access constraint between a view and the top and bottom layout guide with the help of KVConstraintExtensionsMaster library.

    // 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 ];
    
    0 讨论(0)
  • 2020-12-07 16:10

    From iOS 9 you can also do it more simple with topLayoutGuide's anchors:

    • Swift 3

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

    • ObjC

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

    0 讨论(0)
提交回复
热议问题