Home How do I add constraints to a subview loaded from a nib file?
Reply: 3

How do I add constraints to a subview loaded from a nib file?

Josh Hadik
1#
Josh Hadik Published in 2017-11-14 23:24:33Z

I'm trying to load a sub view on to every single page of my app from a nib file. Right now I'm using a somewhat unusual approach to loading this sub view in that I am doing it through an extension of UIStoryboard (probably not relevant to my problem, but I'm not sure). So this is how the code looks when I load the nib file:

extension UIStoryboard {
    public func appendCustomView(to viewController: UIViewController) {
        if let myCustomSubview = Bundle.main.loadNibNamed("MyCustomSubview", owner: nil, options: nil)![0] as? MyCustomSubview {
            viewController.view.addSubview(myCustomSubview)
        }
    }
}

This code does what it's supposed to do and adds "MyCustomSubview" to the view controller (I won't go in to detail on exactly how this method gets called because it works so it doesn't seem important). The problem is I can't for the life of me figure out how to add constraints that effect the size of myCustomSubview. I have tried putting code in the function I showed above as well as in the MyCustomSubview swift file to add constraints but no matter what I do the subview never changes.

Ideally the constraints would pin "MyCustomSubview" to the bottom of the ViewController, with width set to the size of the screen and a hard coded height constraint.

Here are the two main methods I tried (with about 100 minor variations for each) that did NOT work:

Method 1 - Add constraint directly from "appendCustomView"

public func appendCustomView(to viewController: UIViewController) {
    if let myCustomSubview = Bundle.main.loadNibNamed("MyCustomSubview", owner: nil, options: nil)![0] as? MyCustomSubview {

        let top = NSLayoutConstraint(item: myCustomSubview, attribute: .top, relatedBy: .equal
        , toItem: viewController.view, attribute: .top, multiplier: 1, constant: 50.0)

        viewController.view.addSubview(myCustomSubview)
        viewController.view.addConstraint(top)
    }
}

Method 2 - Add constraint outlets and setter method in MyCustomSubview

class MyCustomSubview: UIView {
    @IBOutlet weak var widthConstraint: NSLayoutConstraint!
    @IBOutlet weak var heightConstraint: NSLayoutConstraint!

    func setConstraints(){
        self.widthConstraint.constant = UIScreen.main.bounds.size.width
        self.heightConstraint.constant = 20
    }
}

And call setter method in "appendCustomView"

public func appendCustomView(to viewController: UIViewController) {
    if let myCustomSubview = Bundle.main.loadNibNamed("MyCustomSubview", owner: nil, options: nil)![0] as? MyCustomSubview {

        myCustomSubview.setConstraints()
        viewController.view.addSubview(myCustomSubview)
    }
}

(*note: the actual constraints of these examples are irrelevant and I wasn't trying to meet the specs I mentioned above, I was just trying to make any sort of change to the view to know that the constraints were updating. They weren't.)

Edit : Changed "MyCustomNib" to "MyCustomSubview" for clarity.

MQLN
2#
MQLN Reply to 2017-11-15 00:09:31Z

When you add constraints onto a view from a Nib, you have to call yourView.translatesAutoresizingMaskIntoConstraints = false, and you also need to make sure that you have all 4 (unless it's a label or a few other view types which only need 2) constraints in place:

Here's some sample code that makes a view fill it's parent view:

        parentView.addSubview(yourView)

        yourView.translatesAutoresizingMaskIntoConstraints = false

        yourView.topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true
        yourView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor).isActive = true
        yourView.bottomAnchor.constraint(equalTo: parentView.bottomAnchor).isActive = true
        yourView.trailingAnchor.constraint(equalTo: parentView.trailingAnchor).isActive = true
Josh Hadik
3#
Josh Hadik Reply to 2017-11-15 03:42:35Z

For anyone who comes across this in the future, this is the solution I came up with by tweaking this answer a little bit

Add a setConstraints(withRelationshipTo) method in the swift class that corresponds to the nib file:

class MyCustomSubview: UIView {

    func setConstraints(withRelationshipTo view: UIView){
        self.translatesAutoresizingMaskIntoConstraints = false

        // Replace with your own custom constraints
        self.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
        self.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        self.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        self.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    }
}

Then call the setConstraints method after you add the nib file to the view (probably in viewWillAppear or viewDidLoad of a view controller )

class MyViewController: UIViewController {

    override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
        if let myCustomSubview = Bundle.main.loadNibNamed("MyCustomSubview", owner: nil, options: nil)![0] as? MyCustomSubview {
            let view = self.view  // Added for clarity
            view.addSubview(myCustomSubview)
            myCustomSubview.setConstraints(withRelationshipTo: view)
        } 
    }
}
Nadeesha Lakmal
4#
Nadeesha Lakmal Reply to 2017-11-15 04:44:05Z

You can use this extension for anywhere you're going to add a subview to a existing UIView.

extension UIView {

  func setConstraintsFor(contentView: UIView, left: Bool = true, top: Bool = true, right: Bool = true, bottom: Bool = true) {

      contentView.translatesAutoresizingMaskIntoConstraints = false
      self.addSubview(contentView)

      var constraints         : [NSLayoutConstraint] = []
      if left {
          let constraintLeft      = NSLayoutConstraint(item: contentView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0)
          constraints.append(constraintLeft)
      }

      if top {
          let constraintTop       = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0)
          constraints.append(constraintTop)
      }

      if right {
          let constraintRight     = NSLayoutConstraint(item: contentView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0)
          constraints.append(constraintRight)
      }

      if bottom {
          let constraintBottom    = NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
          constraints.append(constraintBottom)
      }

      self.addConstraints(constraints)
  }
}

You can call this method like this:

containerView.setConstraintsFor(contentView: subView!, top: false)

This will add subView to the containerView and constraint to all sides except top side. You can modify this method to pass left, top, right, bottom Constant value if you want.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.38244 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO