Adding a UIView subclass to Nib with auto layout restrictions

I am trying to create a UIView (A) containing 2 user views (B) in it

View B is installed using Autolayout restrictions and runs in Interface Builder, including restrictions. A is added to the Nib viewController

B - UIImageView (Leading = 10, Trailing = 10, AlignVertically) - UITextField (Leading = 10, Trailing = 10, AlignVertically)

ViewController A (300x300, AlignHorizontally, AlignVertically)

In ViewController, I have A that should be set to 300x300 and B1 and B2 have their own leaders, trailing, top and bottom fixed to 0. (which should make B1 and B2 equal to 300x150, forgive me if I miss something )

When loading View B, I use the following code to load my Nib:

override func awakeAfterUsingCoder(aDecoder: NSCoder!) -> AnyObject! { if self.subviews.count == 0 { let bundle = NSBundle(forClass: self.dynamicType) var view = bundle.loadNibNamed("B", owner: nil, options: nil)[0] as B view.setTranslatesAutoresizingMaskIntoConstraints(false) let constraints = self.constraints() self.removeConstraints(constraints) view.addConstraints(constraints) return view } return self } 

But when I try to run this, I get the following warning, including a failure:

 The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7f897ad1acc0 V:[TestProject.B:0x7f897af73840(300)]> When added to a view, the constraint items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug. 

I also tried to add the view as a property of View B and use the following code to add it to B

 NSBundle.mainBundle().loadNibNamed("B", owner: self, options: nil) self.addSubview(self.viewOfB); 

the result is a view added to the viewController, but it does not accept any of the AutoLayoutConstraints from its own Nib.

Right now I have no idea how to add this view to the viewController view, including the limitations. What am I doing wrong? Is there a better way to do this?

PS: View A is also used as a custom.

PPS: I use Swift to do this, but I'm sure the solutions in Objective-C also work.

+5
source share
2 answers

First of all, the error you get is that

  • you cannot change or move restrictions between your views
  • views must be added to the hierarchy before adding new constraints
  • constraints are usually added in the parent view, which contains the views (or a common ancestor).

    (see AutoLayout Guide for more details )

I would suggest having an A view (CustomView: UIView) loading content (B Views) from a nib file and adding them as subzones.

Main View with CustomView A

The trick is that you need to mark up your B views in the content view so that your nib file has only one root object.

That way, when you add a content view as a view in view A, all restrictions will be passed (similar to how UITableViewCells uses its content view).

ContentView xib

Important Note . The content view must NOT be a class type of CustomView. If you want to drag points and actions, simply declare the File Owner object of the CustomView class and snap them there.

Your final hierarchy of views should look like this:

 MainView (ViewController view) View A Content View B1 View B2 View 

In this mode, view A has a layout configured in the ViewController / xib storyboard, and you will view views B from the nib files associated with the content view.

So, the only thing to do is make sure that the content view will always be the same size as the view A.

 class CustomView: UIView { @IBOutlet var _b1Label: UILabel! @IBOutlet var _b2Button: UIButton! func loadContentView() { let contentNib = UINib(nibName: "CustomView", bundle: nil) if let contentView = contentNib.instantiateWithOwner(self, options: nil).first as? UIView { self.addSubview(contentView) // We could use autoresizing or manually setting some constraints here for the content view contentView.translatesAutoresizingMaskIntoConstraints = true contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth] contentView.frame = self.bounds } } override init(frame: CGRect) { super.init(frame: frame) loadContentView() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder); loadContentView() } } 
+5
source

The problem is this: constraints extracted from self refer to self .

Instead, you should make new restrictions referring to view and with the same other properties. eg:

 NSMutableArray *constraints = [NSMutableArray array]; for(NSLayoutConstraint *constraint in self.constraints) { id firstItem = constraint.firstItem; id secondItem = constraint.secondItem; if(firstItem == self) firstItem = view; if(secondItem == self) secondItem = view; [constraints addObject:[NSLayoutConstraint constraintWithItem:firstItem attribute:constraint.firstAttribute relatedBy:constraint.relation toItem:secondItem attribute:constraint.secondAttribute multiplier:constraint.multiplier constant:constraint.constant]]; } /* if you also have to move subviews into `view`. for(UIView *subview in self.subviews) { [view addSubview:subview]; } */ [view addConstraints:constraints]; 

And, I think you should copy some more properties from self .

 view.frame = self.frame; view.autoresizingMask = self.autoresizingMask; view.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints; 

Sorry for the Obj-C code, I copied from this answer :)

+4
source

Source: https://habr.com/ru/post/1203080/


All Articles