NSLayoutConstraint inside UITableViewCell crashes app - ios

I'm trying to position an image in the center of a UITableViewCell with NSLayoutConstraints. I'm using the code below for that:
NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:self.image
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeWidth
multiplier:1
constant:200];
[self.image addConstraint: cn];
cn = [NSLayoutConstraint constraintWithItem:self.image
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeHeight
multiplier:1
constant:200];
[self.image addConstraint:cn];
Also, I would like the image to be 80% of the UITableViewCells height and have the same width - so it's square. However, currently my app crashes and throws this error: Impossible to set up layout with view hierarchy unprepared for constraint.
What am I doing wrong?

As it turns out, I had to add the image to the cell.contentView, so that it was actually available as a subview.
This is the completed working code:
NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:self.image
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeHeight
multiplier:0.7
constant:0];
[cell.contentView addConstraint: cn];
cn = [NSLayoutConstraint constraintWithItem:self.image
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeHeight
multiplier:0.7
constant:0];
[cell.contentView addConstraint:cn];
NSLayoutConstraint *constraintX = [NSLayoutConstraint constraintWithItem:self.image attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0];
NSLayoutConstraint *constraintY = [NSLayoutConstraint constraintWithItem:self.image attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0];
[cell.contentView addConstraint:constraintX];
[cell.contentView addConstraint:constraintY];

Exactly what is "self.image" referring to? Is it a property of your View Controller (this would be definitely wrong)? Seems to me it should be "cell.image" where each of your cells should have it's own reference to an image.

Related

UIButton constraints programmatically

I have created "first button" using storyboard and set its constraints for it, now I want to create another button programmatically and set it above the first button like this :
and I use this code to try and achieve this:
NSLayoutConstraint *xConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
NSLayoutConstraint *Yxonstraints = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.invoiceTextField attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:200];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:200];
[self.view addConstraints:#[xConstraint,Yxonstraints,width,height]];
However the second button ends up in the middle and on the left and also smaller than its actual size, what am I doing wrong here?
You need
self.secondBu.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.secondBu.bottomAnchor.constraint(equalTo: self.firstBu.topAnchor, constant:-15)
self.secondBu.leadingAnchor.constraint(equalTo: self.firstBu.leadingAnchor),
self.secondBu.trailingAnchor.constraint(equalTo: self.firstBu.trailingAnchor),
self.secondBu.heightAnchor.constraint(equalTo: self.firstBu.heightAnchor)
])
but the easiest way is to insert firstBu inside UIStackView in storyboard , and then use 1 line
self.stackView.insertArrangedSubview(secondBu,at:0)
you may set stackView spacing property to 15 also in IB , also avoid using addConstraints as recommended by Apple and use NSLayoutConstraint.activate
NSLayoutConstraint *Yxonstraints = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.invoiceTextField attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
You don't want that. You want secondButton.top be space to firstButton.bottom :
NSLayoutConstraint *Yxonstraints = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.invoiceTextField attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20];

IOS/Autolayout: When in lifecycle to set constraints for elements created in code

Most of my scene is in storyboard and uses autolayout. But I want to create a UITableView, label and view in code and constrain them within the overall autlayout. My question is where in the lifecycle should I create the constraints.
Right now, I create hidden versions of the elements in ViewDidLoad and then customize and display them in View Will Appear based on data. I don't think I can put the layout constraints in ViewDidLoad, because the compiler won't know where all the views from storyboard are located. On the other hand, I don't want to recreate these constraints everytime viewWillAppear fires. Most don't change and at most I might want to update one or two.
Should I place the constraints in viewWillAppear and condition creating them on some test whether they were already created? Or should I put them somewhere else such as viewDidlayoutSubviews or viewDidAppear?
Thanks for any suggestions.
This is the code that creates the constraints:
NSLayoutConstraint *contop = [NSLayoutConstraint constraintWithItem:_stepsTableView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_stepNames attribute:NSLayoutAttributeBottom multiplier:1 constant:12];
NSLayoutConstraint *contrail = [NSLayoutConstraint
constraintWithItem:_stepsTableView attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual toItem:self.scrollView
attribute:NSLayoutAttributeTrailing multiplier:1 constant:20];
NSLayoutConstraint *conlead = [NSLayoutConstraint
constraintWithItem:_stepsTableView attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual toItem:self.scrollView
attribute:NSLayoutAttributeLeading multiplier:1 constant:20];
NSLayoutConstraint *conbot = [NSLayoutConstraint
constraintWithItem:_stepsTableView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual toItem:self.scrollView
attribute:NSLayoutAttributeBottom multiplier:1 constant:20];
NSLayoutConstraint *conheight = [NSLayoutConstraint constraintWithItem:_stepsTableView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:height];
[self.scrollView removeConstraint: _bottomConstraint];
[self.scrollView addConstraints:#[contop,contrail,conlead,conbot,conheight]];
[self.view layoutIfNeeded];
Any programmatically created constraints should be placed in viewDidLayoutSubviews and wraped with once bool value as the function is being called multiple times during launch of a viewController
-(void)viewDidLayoutSubviews
{
if(once){
once = NO;
_stepsTableView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *contop = [NSLayoutConstraint constraintWithItem:_stepsTableView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_stepNames attribute:NSLayoutAttributeBottom multiplier:1 constant:12];
NSLayoutConstraint *contrail = [NSLayoutConstraint
constraintWithItem:_stepsTableView attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual toItem:self.scrollView
attribute:NSLayoutAttributeTrailing multiplier:1 constant:20];
NSLayoutConstraint *conlead = [NSLayoutConstraint
constraintWithItem:_stepsTableView attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual toItem:self.scrollView
attribute:NSLayoutAttributeLeading multiplier:1 constant:20];
NSLayoutConstraint *conbot = [NSLayoutConstraint
constraintWithItem:_stepsTableView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual toItem:self.scrollView
attribute:NSLayoutAttributeBottom multiplier:1 constant:20];
NSLayoutConstraint *conheight = [NSLayoutConstraint constraintWithItem:_stepsTableView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:height];
[self.scrollView removeConstraint: _bottomConstraint];
[self.scrollView addConstraints:#[contop,contrail,conlead,conbot,conheight]];
[self.view layoutIfNeeded];
}
}

Trailing constraint not applying when programmatically adding UIConstraints

I'm trying to add a subview to a view with constraints through code. I had some success but the trailing constraint seems to be completely ignored for whatever reason.
My code:
leading_const = 16.f;
trailing_const = 16.f;
top_const = 12.f;
bottom_const = 12.f;
insertView.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:insertView];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:insertView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:leading_const];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:insertView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:trailing_const];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:insertView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeTop
multiplier:1.f
constant:top_const];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:insertView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeHeight
multiplier:1.f
constant:130.f];
[superView addConstraints:#[leading, trailing, top, height]];
Result:
Appreciate any guidance!
Your constraint is being applied but as you have set it to 16 it is going 16 points past the trailing edge of the view. You therefore should use a negative value for the constant instead.

Autolayout issues on using KOLODA library

Am using KOLODA library,and i have issues in providing more than one xibs to display the cards,In that second view(Xib) size are not fit into the koloda's first xib.how to adjust the view layout,any solutions for that is much appreciated.
NOTE:The problem here is i need multiple xib's which have different formats.
//This is my code
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
[self addConstraints:#[width,height,top,leading]];

Cannot modify constraints for UIToolbar managed by a UINavigationController

I am adding NSContraints to a Toolbar of of UINavigationController and I get this error:
Cannot modify constraints for UIToolbar managed by a UINavigationController
In ViewDidLoad:
self.navigationController.toolbarHidden = NO;
[self.navigationController.toolbar addSubview:_labelName];
[self.navigationController.toolbar addSubview:_labelAddress];
Then I call the method below:
NSLayoutConstraint *nameRight = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeRight multiplier:1 constant:20];
NSLayoutConstraint *nameLeft = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
NSLayoutConstraint *nameTop = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *nameHeight = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeHeight multiplier:.5 constant:0];
NSLayoutConstraint *addressRight = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeRight multiplier:1 constant:20];
NSLayoutConstraint *addressLeft = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
NSLayoutConstraint *addressTop = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_labelName attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *addressHeight = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.navigationController.toolbar attribute:NSLayoutAttributeHeight multiplier:.5 constant:0];
[self.navigationController.toolbar addConstraints:#[nameRight, nameLeft, nameTop, nameHeight, addressRight, addressLeft, addressTop, addressHeight]];
Should I create a UIView inside the toolbar of the navigation controller? and put the contraints inside it?
Autolayout constraints only work with UIViews and their subclasses.
Your alternative is to roll your own toolbar with widgets based only on UIViews.
Link : iOS Autolayout and UIToolbar/UIBarButtonItems
I hope this will resolve your issue.
The issue was you need to add the toolbar items to [self using setToolBarItem] not [self.navigationController.toolbar ..] even though techinically it is the same object.
It is like self.title, sets the navigation controller title, [self setToolbarItems set the toolbar items of the navigation controller toolbar
[self setToolbarItems:#[barButtonItem] animated:NO]; <-- Answer
[self.navigationController.toolbar setItems:#[barButtonItem]]; <-- BAD
For the NSLayout you do that inside a view in the BarButtonItem:
_toolBarView = [[UIView alloc] init];
[_toolBarView addSubview:_labelName];
[_toolBarView addSubview:_labelAddress];
barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:_toolBarView];
NSLayout code:
NSLayoutConstraint *nameRight = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
NSLayoutConstraint *nameLeft = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
NSLayoutConstraint *nameTop = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *nameHeight = [NSLayoutConstraint constraintWithItem:_labelName attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0];
NSLayoutConstraint *addressRight = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
NSLayoutConstraint *addressLeft = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
NSLayoutConstraint *addressTop = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_labelName attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *addressHeight = [NSLayoutConstraint constraintWithItem:_labelAddress attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_toolBarView attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0];
[_toolBarView addConstraints:#[nameRight, nameLeft, nameTop, nameHeight, addressRight, addressLeft, addressTop, addressHeight]];
Hope this helps!

Resources