53

I am displaying a view in the bottom of the universal application and adding this view dynamically in my view. I want to show this view in bottom every time like iAd. in both orientation. How can I add constraints for this. Please suggest.

Thanks

Mitesh Khatri
  • 3,935
  • 4
  • 44
  • 67
  • Do you want to increase the height of the View as per device? – iphonic Jul 27 '15 at 10:49
  • No. Fix height same as iAd. – Mitesh Khatri Jul 27 '15 at 10:51
  • Make sure you see [On iOS, what are the differences between margins, edge insets, content insets, alignment rects, layout margins, anchors](https://stackoverflow.com/questions/37796884/on-ios-what-are-the-differences-between-margins-edge-insets-content-insets-a/47614397#47614397). It will improve your understanding of margins, anchors, layoutguides... – mfaani Dec 08 '17 at 22:50

5 Answers5

106

To fix a view to the bottom of the screen you need following constraints to set.

  1. Leading Constraint with respect of Parent View for - X
  2. Trailing Constraint with respect of Parent View for - Width
  3. Bottom Constraint with respect of Parent View for - Y
  4. Height Constraint attached to self for - Height.

Lets add.

UIView *subView=bottomView;
UIView *parent=self.view;

subView.translatesAutoresizingMaskIntoConstraints = NO;

//Trailing    
NSLayoutConstraint *trailing =[NSLayoutConstraint
                                constraintWithItem:subView
                                attribute:NSLayoutAttributeTrailing
                                relatedBy:NSLayoutRelationEqual
                                toItem:parent   
                                attribute:NSLayoutAttributeTrailing
                                multiplier:1.0f
                                constant:0.f];

//Leading

NSLayoutConstraint *leading = [NSLayoutConstraint
                                   constraintWithItem:subView
                                   attribute:NSLayoutAttributeLeading
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:parent
                                   attribute:NSLayoutAttributeLeading
                                   multiplier:1.0f
                                   constant:0.f];

//Bottom
NSLayoutConstraint *bottom =[NSLayoutConstraint
                                 constraintWithItem:subView
                                 attribute:NSLayoutAttributeBottom
                                 relatedBy:NSLayoutRelationEqual
                                 toItem:parent
                                 attribute:NSLayoutAttributeBottom
                                 multiplier:1.0f
                                 constant:0.f];

//Height to be fixed for SubView same as AdHeight
NSLayoutConstraint *height = [NSLayoutConstraint
                               constraintWithItem:subView
                               attribute:NSLayoutAttributeHeight
                               relatedBy:NSLayoutRelationEqual
                               toItem:nil
                               attribute:NSLayoutAttributeNotAnAttribute
                               multiplier:0
                               constant:ADHeight];
    
    //Add constraints to the Parent
    [parent addConstraint:trailing];
    [parent addConstraint:bottom];
    [parent addConstraint:leading];

    //Add height constraint to the subview, as subview owns it.
    [subView addConstraint:height];
starball
  • 20,030
  • 7
  • 43
  • 238
iphonic
  • 12,615
  • 7
  • 60
  • 107
  • The `attribute` value on `*height` should actually be `NSLayoutAttributeNotAnAttribute`, not `0`. – slider May 29 '16 at 03:46
  • @dperk NSLayoutAttributeNotAnAttribute = 0: NSLayoutAttribute is enum values where it is defined as 0, so both are same no difference it's about choice what you want to use. – iphonic May 29 '16 at 04:00
  • 3
    True, although it's best practice to keep consistent with enum names. For 0, I concede; it's no big deal. But it's always best to be consistent. – slider May 29 '16 at 04:30
  • 1
    should this setup be done in awakeFromNib or viewDidload? why? – mfaani Aug 02 '16 at 14:06
  • 1
    @Honey It should be added only once, choose `viewDidLoad` as it gets called once. – iphonic Aug 02 '16 at 14:11
  • Correct me if I am wrong: For all your constraints there is a `constraintWithItem` and `toItem` attribute. Meaning the constraint already specifics it's between what and what and how they relate to one another with all the other attributes. My question is: when you message `[parent addConstraint:trailing]` couldn't you instead have done `[subView addConstraint:trailing]` – mfaani Aug 02 '16 at 15:45
  • @Honey No you can't do it with subview because the trailing, leading, top, bottom should be added with respect of superview where as a fixed width and height should be with respect to subView only. – iphonic Aug 02 '16 at 15:48
  • Do you mean when `[someView addConstraint:aConstraint]`, the `NSLayoutAttributeLeading` or `NSLayoutAttributeTrailing` will find out how it should related based on `someView` and not based on the *value* of `toItem`? – mfaani Aug 02 '16 at 15:55
  • @iphonic I Want the subview height to change with respect to superview height (like 1/3 rd of superview). How to add it ? – subin272 Apr 10 '18 at 02:24
  • How about all set in one shot - [self addConstraints:@[leading, trailing, bottom]]; – byJeevan Dec 16 '19 at 10:14
12

Small extension for previous answer because addConstraint will be deprecated in future. Here is an extension for UI view. Use these functions after you added view to hierarchy.

OBJC

@implementation UIView (Constraints)

-(void)addConstaintsToSuperviewWithLeftOffset:(CGFloat)leftOffset topOffset:(CGFloat)topOffset {

    self.translatesAutoresizingMaskIntoConstraints = false;

    [[NSLayoutConstraint constraintWithItem: self
                                  attribute: NSLayoutAttributeLeading
                                  relatedBy: NSLayoutRelationEqual
                                     toItem: self.superview
                                  attribute: NSLayoutAttributeLeading
                                 multiplier: 1
                                   constant: leftOffset] setActive:true];

    [[NSLayoutConstraint constraintWithItem: self
                                  attribute: NSLayoutAttributeTop
                                  relatedBy: NSLayoutRelationEqual
                                     toItem: self.superview
                                  attribute: NSLayoutAttributeTop
                                 multiplier: 1
                                   constant: topOffset] setActive:true];
}

-(void)addConstaintsWithWidth:(CGFloat)width height:(CGFloat)height {

    self.translatesAutoresizingMaskIntoConstraints = false;


    [[NSLayoutConstraint constraintWithItem: self
                                  attribute: NSLayoutAttributeWidth
                                  relatedBy: NSLayoutRelationEqual
                                     toItem: nil
                                  attribute: NSLayoutAttributeNotAnAttribute
                                 multiplier: 1
                                   constant: width] setActive:true];

    [[NSLayoutConstraint constraintWithItem: self
                                  attribute: NSLayoutAttributeHeight
                                  relatedBy: NSLayoutRelationEqual
                                     toItem: nil
                                  attribute: NSLayoutAttributeNotAnAttribute
                                 multiplier: 1
                                   constant: height] setActive:true];
}

@end

Swift 3

extension UIView {

    public func addConstaintsToSuperview(leftOffset: CGFloat, topOffset: CGFloat) {

        self.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint(item: self,
                           attribute: .leading,
                           relatedBy: .equal,
                           toItem: self.superview,
                           attribute: .leading,
                           multiplier: 1,
                           constant: leftOffset).isActive = true

        NSLayoutConstraint(item: self,
                           attribute: .top,
                           relatedBy: .equal,
                           toItem: self.superview,
                           attribute: .top,
                           multiplier: 1,
                           constant: topOffset).isActive = true
    }

    public func addConstaints(height: CGFloat, width: CGFloat) {

        self.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint(item: self,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: nil,
                           attribute: .notAnAttribute,
                           multiplier: 1,
                           constant: height).isActive = true


        NSLayoutConstraint(item: self,
                           attribute: .width,
                           relatedBy: .equal,
                           toItem: nil,
                           attribute: .notAnAttribute,
                           multiplier: 1,
                           constant: width).isActive = true
    }
}
Alex Shubin
  • 3,549
  • 1
  • 27
  • 32
11

Also since iOS 9 it could be done super simple with anchors:

Swift 3

extension UIView {

    func addConstaintsToSuperview(leadingOffset: CGFloat, topOffset: CGFloat) {

        guard superview != nil else {
            return
        }

        translatesAutoresizingMaskIntoConstraints = false

        leadingAnchor.constraint(equalTo: superview!.leadingAnchor,
                                 constant: leadingOffset).isActive = true

        topAnchor.constraint(equalTo: superview!.topAnchor,
                             constant: topOffset).isActive = true
    }

    func addConstaints(height: CGFloat, width: CGFloat) {

        heightAnchor.constraint(equalToConstant: height).isActive = true
        widthAnchor.constraint(equalToConstant: width).isActive = true
    }

}

OBJC category

@implementation UIView (Constraints)

-(void)addConstaintsToSuperviewWithLeadingOffset:(CGFloat)leadingOffset topOffset:(CGFloat)topOffset
{
    if (self.superview == nil) {
        return;
    }

    self.translatesAutoresizingMaskIntoConstraints = false;

    [[self.leadingAnchor constraintEqualToAnchor:self.superview.leadingAnchor
                                        constant:leadingOffset] setActive:true];

    [[self.topAnchor constraintEqualToAnchor:self.superview.topAnchor
                                   constant:topOffset] setActive:true];
}

-(void)addConstaintsWithHeight:(CGFloat)height width:(CGFloat)width
{
    [[self.heightAnchor constraintEqualToConstant:height] setActive:true];
    [[self.widthAnchor constraintEqualToConstant:width] setActive:true];
}

@end
Alex Shubin
  • 3,549
  • 1
  • 27
  • 32
7

You can use autolayout constraints programmatically like below

fileprivate func setupName(){
        let height = CGFloat(50)

        lblName.text = "Hello world"
        lblName.backgroundColor = .lightGray

        //Step 1
        lblName.translatesAutoresizingMaskIntoConstraints = false

        //Step 2
        self.view.addSubview(lblName)

        //Step 3
        NSLayoutConstraint.activate([

            lblName.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
            lblName.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            lblName.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor,constant: -height),
            lblName.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
            ])

    }

Output screenshot

Avinash Reddy
  • 451
  • 5
  • 7
0

Extending @Alex Shubin solution in Swift 4, I do the following:

Sometimes, I need to add a variable number of constraints, in this case 3, because the height will be calculated latter.

keyboard.addConstaints(top: nil, right: 0.0, bottom: 0.0, left: 0.0, width: nil, height: nil)

You have to be careful adding all required, and making sure you don't have any conflicting constraints.

extension UIView {
    func addConstaints(top: CGFloat?, right: CGFloat?, bottom: CGFloat?, left: CGFloat?, width: CGFloat?, height: CGFloat?) {
        translatesAutoresizingMaskIntoConstraints = false
        if top != nil { self.addConstaint(top: top!) }
        if right != nil { self.addConstaint(right: right!) }
        // Add lines for bottom, left, width an heigh
        // ...
    }
    func addConstaint(top offset: CGFloat) {
        guard superview != nil else { return }
        topAnchor.constraint(equalTo: superview!.topAnchor, constant: offset).isActive = true
    }
}

To verify that all views were laid-out correctly, you can inspect the .constraints property of the superView or you can check the frame in:

override func viewDidLayoutSubviews() {
    print(keyboard.frame)
}
eharo2
  • 2,553
  • 1
  • 29
  • 39