86

I've been looking for a lot of snippets in the net and I still can't find the answer to my problem. My question is I have a scrollView(SV) and I want to add a button inside scrollView(SV) programmatically with same width and height of its superview which is scrollView(SV) so that when user rotate the device button will have the same frame of scrollView(SV). how to do the NSLayout/NSLayoutConstraint? thanks

beryllium
  • 29,669
  • 15
  • 106
  • 125
Bordz
  • 2,810
  • 4
  • 23
  • 27

11 Answers11

128

If someone is looking for a Swift solution – I would create a Swift extension for UIView which will help you each time you want to bind a subviews frame to its superviews bounds:

Swift 2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

Swift 3:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }
}

Swift 4.2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
        self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
        self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
        self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true

    }
}

Then simply call it like this:

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindFrameToSuperviewBounds()
Climbatize
  • 1,103
  • 19
  • 34
MadNik
  • 7,713
  • 2
  • 37
  • 39
  • when creating a custom UIView using a .xib, the bindFrameToSuperviewBounds should be called within 'required init?(coder aDecoder)' just after self.addSubview(self.view) – user1603721 Jul 05 '17 at 04:20
  • Worth noting that solutions using visual format are not safe area friendly. If, for example, you are calling this in a view that is inside a navigation controller showing navbars and toolbars, your view will go under the navbar and under the toolbar if it goes that far down. – Andy Ibanez Oct 21 '17 at 22:06
  • This works as solution for Swift 5 as well. I couldn't make my custom subView adapt to parentView size using AutoLayout. Using this once the subview is added worked as a charm. – toni_piu May 14 '20 at 12:35
  • The Swift 4.2 solution works well. You can even make it a bit shorter by removing the `constant: 0` part. – Yvo Jul 16 '20 at 00:27
70

I'm not sure if this is the most efficient way to do it, but it works..

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
// initialize


[coverForScrolView addSubview:button];

NSLayoutConstraint *width =[NSLayoutConstraint
                                    constraintWithItem:button
                                    attribute:NSLayoutAttributeWidth
                                    relatedBy:0
                                    toItem:coverForScrolView
                                    attribute:NSLayoutAttributeWidth
                                    multiplier:1.0
                                    constant:0];
NSLayoutConstraint *height =[NSLayoutConstraint
                                     constraintWithItem:button
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:0
                                     toItem:coverForScrolView
                                     attribute:NSLayoutAttributeHeight
                                     multiplier:1.0
                                     constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint
                                   constraintWithItem:button
                                   attribute:NSLayoutAttributeTop
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:coverForScrolView
                                   attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                   constant:0.f];
NSLayoutConstraint *leading = [NSLayoutConstraint
                                       constraintWithItem:button
                                       attribute:NSLayoutAttributeLeading
                                       relatedBy:NSLayoutRelationEqual
                                       toItem:coverForScrolView
                                       attribute:NSLayoutAttributeLeading
                                       multiplier:1.0f
                                       constant:0.f];
[coverForScrolView addConstraint:width];
[coverForScrolView addConstraint:height];
[coverForScrolView addConstraint:top];
[coverForScrolView addConstraint:leading];
Bordz
  • 2,810
  • 4
  • 23
  • 27
  • 4
    It would be much more efficient to use `NSLayoutConstraint.activateConstraints([width, height, top, leading])` – Berik Jul 06 '15 at 12:28
  • You can use `[coverForScrolView addConstraints:@[width, height, top, leading]];` – Islam Oct 16 '15 at 03:58
  • 1
    it's worth noting (years later) this code is **extremely out of date**. it's now far easier to add constraints - see any 2017 answer below – Fattie Jan 27 '17 at 14:49
49

This link can help you,follow the instructions : http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

EDIT :

use following code snippet, where subview is your subivew.

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
Uniruddh
  • 4,427
  • 3
  • 52
  • 86
  • 4
    In this case, the visual format could be also `V:|[subview]|` and `H:|[subview]|` – Gustavo Barbosa Jan 22 '15 at 21:31
  • 4
    it's worth noting (years later) this code is **extremely out of date**. it's now far easier to add constraints - see any 2017 answer below – Fattie Jan 27 '17 at 14:49
20

addConstraint and removeConstraint methods for UIView are going to be deprecated, so it's worth to use 'constraint creation conveniences':

view.topAnchor.constraint(equalTo: superView.topAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: 0).isActive = true
beryllium
  • 29,669
  • 15
  • 106
  • 125
  • This works well. You can even make it a bit shorter by removing the `constant: 0` part. – Yvo Jul 16 '20 at 00:30
8

Approach #1: Via UIView Extension

Here's a more functional approach in Swift 3+ with a precondition instead of a print (which can perish easily in the console). This one will report programmer errors as failed builds.

Add this extension to your project:

extension UIView {
    /// Adds constraints to the superview so that this view has same size and position.
    /// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
    func bindEdgesToSuperview() {
        guard let superview = superview else {
            preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
        }
        translatesAutoresizingMaskIntoConstraints = false
        ["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
            superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        }
    }
}

Now simply call it like this:

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindEdgesToSuperview()

Note that the above method is already integrated into my HandyUIKit framework which also adds some more handy UI helpers into your project.


Approach #2: Using a Framework

If you work a lot with programmatic constraints in your project then I recommend you to checkout SnapKit. It makes working with constraints a lot easier and less error-prone.

Follow the installation instructions in the docs to include SnapKit into your project. Then import it at the top of your Swift file:

import SnapKit

Now you can achieve the same thing with just this:

subview.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}
Jeehut
  • 20,202
  • 8
  • 59
  • 80
6

Swift 3:

import UIKit

extension UIView {

    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}
PLJNS
  • 451
  • 4
  • 14
  • If you only changed the code to make it Swift 3 compliant, you should rather edit the original posters answer instead of posting a new answer (as this is no change of the original posters intention). If you don't have enough points for an edit, then comment on the original post with a hint to the changes needed to comply to Swift 3. The original poster (or someone else seeing your comment) would then probably update the answer. This way we keep the thread clean from duplicate answers and deprecated code. – Jeehut Dec 30 '16 at 09:34
  • Hey @Dschee - I agree with you totally, but we are "wrong". the "consensus" view on the site for better or worse, is the opposite to what you express here. http://meta.stackoverflow.com/questions/339024/are-edits-that-insert-swift-3-code-into-existing-swift-2-answers-acceptable (I continually ignore the consensus decision, do what is sensible, and then get in trouble from the mods :) ) – Fattie Jan 27 '17 at 14:53
2

Swift 4 using NSLayoutConstraint:

footerBoardImageView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint  = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.width, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.height, multiplier: 1, constant: 0)
superview.addConstraints([widthConstraint, heightConstraint])
Masih
  • 1,633
  • 4
  • 19
  • 38
2

I've picked the best elements from the other answers:

extension UIView {
  /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
  /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
  func bindFrameToSuperviewBounds() {
    guard let superview = self.superview else {
      print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
      return
    }

    self.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
      self.topAnchor.constraint(equalTo: superview.topAnchor),
      self.bottomAnchor.constraint(equalTo: superview.bottomAnchor),
      self.leadingAnchor.constraint(equalTo: superview.leadingAnchor),
      self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
    ])
  }
}

You can use it like this, for example in your custom UIView:

let myView = UIView()
myView.backgroundColor = UIColor.red

self.addSubview(myView)
myView.bindFrameToSuperviewBounds()
Yvo
  • 18,681
  • 11
  • 71
  • 90
1

As a supplemental answer, and one for those not opposed to including third party libraries, the PureLayout library provides a method to do just this. Once the library is installed, it's as simple as

myView.autoPinEdgesToSuperviewEdges()

There are other libraries which can provide similar functionality as well depending on taste, eg. Masonry, Cartography.

Matt Pinkston
  • 1,610
  • 15
  • 18
0

As a follow up to @Dschee's solution, here is swift 3.0 syntax: (Please note: this is not my solution, I have just fixed it for Swift 3.0)

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
}
kvorobiev
  • 5,012
  • 4
  • 29
  • 35
  • 1
    If you only changed the code to make it Swift 3 compliant, you should rather edit the original posters answer instead of posting a new answer (as this is no change of the original posters intention). If you don't have enough points for an edit, then comment on the original post with a hint to the changes needed to comply to Swift 3. The original poster (or someone else seeing your comment) would then probably update the answer. This way we keep the thread clean from duplicate answers and deprecated code. – Jeehut Dec 30 '16 at 09:37
  • I agree with you completely, @Dschee, but there is (to me, absurd) comment on Meta that "we don't do that on SO" ... http://meta.stackoverflow.com/questions/339024/are-edits-that-insert-swift-3-code-into-existing-swift-2-answers-acceptable/339035#339035 – Fattie Jan 07 '17 at 17:19
  • @JoeBlow After reading the discussion behind your link I actually think that this makes sense, too. I agree with PatrickHaugh's comment (on the question) though that a new answer in combination with a comment on the original answer should be given. It is then up to the original poster to update his answer (to get the future upvotes) or not. Thank you for the link! – Jeehut Jan 08 '17 at 20:22
  • Huh, okay this is all a big part of why I have remained a faithful hit and runner in the past. I grab my answer, convert it to the current syntax and go on with my coding. The main reason I posted this way is that when I taught swift, I often was asked about how to find the solution in whatever version of swift was current because new coders were having troubles updating their function declarations. It was one of the leading sources of frustration but also an opportunity to contrast the two code styles. The student was able to predict similar changes in other code snippets as a result. – James Larcombe Jan 11 '17 at 17:24
0

I needed to cover the superview completely. The other ones wouldn't do that during orientation changes. So I wrote a new one which does - using an arbitrary size multiplier of 20. Feel free to change to your needs. Also note this one in fact makes the subview a lot bigger than the superview which might be different from requirements.

extension UIView {
    func coverSuperview() {
        guard let superview = self.superview else {
            assert(false, "Error! `superview` was nil – call `addSubview(_ view: UIView)` before calling `\(#function)` to fix this.")
            return
        }
        self.translatesAutoresizingMaskIntoConstraints = false
        let multiplier = CGFloat(20.0)
        NSLayoutConstraint.activate([
            self.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: multiplier),
            self.widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: multiplier),
            self.centerXAnchor.constraint(equalTo: superview.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: superview.centerYAnchor),
            ])
    }
}
Jonny
  • 15,955
  • 18
  • 111
  • 232