UIView Awful Performance - ios

I have built a slide navigation controller which allows the sliding of UIViewController's in a fast and smooth way (Facebook style). I have since added a UIViewController with a child view controller with it's own child view controller, as well as a table view. The lowest child view controller also has a view subviews. This UIViewController has the view that is made visible when the top view is slid away.
I have tested the smoothness both with and without this view controller. Without it the slide runs at a nice 60 frames per second, but with it my device is only managing a choppy 20 to 30 fps.
I have also tried removing subviews which seems to make a slight improvement, but not as much as I would expect. Even when switched out with another fairly complex view controller (without child view controls though) I get much better performance.
I am using auto layout throughout the app, and have come to believe this is the problem. None of my code is being executed whilst this poor sliding is occurring, so I believe it has something to do with the constraints being calculated as more of the view is being shown, but can't be sure.
As to avoid including the whole class, here are the updateViewConstraint methods in the view controller and subviews:
TOP VIEW CONTROLLER
/**
* Called when the view controller’s view needs to update its constraints.
*/
- (void)updateViewConstraints
{
[super updateViewConstraints];
// remove all constraints
[self.view removeConstraints:self.view.constraints];
NSArray *constraints;
// add the table view to the top of the view and the parameters view to the bottom
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(Panel)-[tableView]|"
options:kNilOptions
metrics:#{#"Panel": #(kPanelWidth)}
views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[tableView]-|"
options:kNilOptions
metrics:nil
views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(PanelPlus)-[recipeParameters]-|"
options:kNilOptions
metrics:#{#"PanelPlus": #(kPanelWidth + 20)}
views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[recipeParameters(==Height)]|"
options:kNilOptions
metrics:#{ #"Height" : #(kParametersControllerHeight)}
views:self.viewsDictionary];
[self.view addConstraints:constraints];
}
FIRST CHILD VIEW CONTROLLER
/**
* Called when the view controller’s view needs to update its constraints.
*/
- (void)updateViewConstraints
{
[super updateViewConstraints];
// remove all constraints
[self.view removeConstraints:self.view.constraints];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[pageView]|" options:kNilOptions
metrics:nil views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[pageView]|" options:kNilOptions
metrics:nil views:self.viewsDictionary];
[self.view addConstraints:constraints];
}
SECOND CHILD VIEW CONTROLLER
- (void)updateViewConstraints
{
[super updateViewConstraints];
NSArray *constraints;
// remove all constraints
[self.view removeConstraints:self.view.constraints];
if (self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(8)-[optionLabel]-[selectionWheel]-[includeExclude(==32)]-|"
options:NSLayoutFormatAlignAllCenterX
metrics:nil
views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[selectionWheel]-|"
options:kNilOptions
metrics:nil
views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[includeExclude]-|"
options:kNilOptions
metrics:nil
views:self.viewsDictionary];
[self.view addConstraints:constraints];
}
else
{
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[selectionWheel]-|" options:kNilOptions
metrics:nil views:self.viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[optionLabel]-[selectionWheel]-(Balance)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:#{#"Balance": #(self.optionLabel.bounds.size.width + 40)}
views:self.viewsDictionary];
[self.view addConstraints:constraints];
}
}
CUSTOM UICONTROL (SUBVIEW OF SECOND CHILD VIEW CONTROLLER)
/**
* Update constraints for the view.
*/
- (void)updateConstraints
{
[super updateConstraints];
[self removeConstraints:self.constraints];
[self.excludeButton removeConstraints:self.excludeButton.constraints];
[self.includeButton removeConstraints:self.includeButton.constraints];
NSArray *constraints;
NSLayoutConstraint *constraint;
// add everything to fill the bounds
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(==6)-[excludeButton]-(==6)-|"
options:kNilOptions
metrics:nil
views:self.viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[excludeButton][optionLabel][includeButton]|"
options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom
metrics:nil
views:self.viewsDictionary];
[self addConstraints:constraints];
// make the exclude and include buttons square
constraint = [NSLayoutConstraint constraintWithItem:self.excludeButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.excludeButton attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f];
[self.excludeButton addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.includeButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.includeButton attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f];
[self.includeButton addConstraint:constraint];
}
The implementation of the top UIViewController is here.

Related

UIView programatic constraint height increase when keyboard present

I'm building a comment input control for an app. This control consists of a UITextView embedded in a UIView. All constraints are being handled programatically. What happens is when the user taps the UITextView, the keyboard will open. This calls the keyboard observer methods and I then adjust the bottom constraint for the comment input control to move up with the keyboard. However, I am also trying to increase the height of the input control at the same time so the user has more room to type. I'm having trouble achieving this.
-(void)updateViewConstraints
{
NSDictionary *views = #{
#"table" : self.commentsTableView,
#"seeMoreComments" : self.seeMoreCommentsView,
#"commentInput" : self.commentEntryInput
};
//See More Comments Constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[seeMoreComments]-0-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[seeMoreComments(45)]" options:0 metrics:nil views:views]];
//Table view constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[table]-0-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[seeMoreComments]-0-[table]-0-|" options:0 metrics:nil views:views]];
//Comment entry input
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[commentInput]-0-|" options:0 metrics:nil views:views]];
commentInputVerticalConstraint = [NSLayoutConstraint constraintWithItem:self.commentEntryInput
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:commentInputHeight];
if(commentInputBottomConstraint == nil)
{
commentInputBottomConstraint =
[NSLayoutConstraint constraintWithItem:self.commentEntryInput
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom multiplier:1.0
constant:0.0];
}
[self.view addConstraint:commentInputVerticalConstraint];
[self.view addConstraint:commentInputBottomConstraint];
[super updateViewConstraints];
}
Now I have a method that is called when keyBoardWillShow is called. This method animates the comment input control up when the keyboard appears.
(void)animateContentWithKeyboardInfo:(NSDictionary *)keyboardInfo
{
NSNumber *animationDuration = keyboardInfo[ UIKeyboardAnimationDurationUserInfoKey ];
NSValue *keyboardFrameValue = keyboardInfo[ UIKeyboardFrameEndUserInfoKey ];
CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
UIViewAnimationCurve animationCurve = [keyboardInfo[ UIKeyboardAnimationCurveUserInfoKey ] intValue];
UIViewAnimationOptions animationOptions = animationOptionWithCurve(animationCurve);
commentInputBottomConstraint.constant = (keyboardFrame.origin.y - [UIScreen mainScreen].bounds.size.height);
//Increase the veritcal height of the comment input control
commentInputVerticalConstraint.constant = 125;
//Takes into account that the Tab Bar is 50 points, and adjust for this
//value.
if(keyboardAppeared == YES)
{
commentInputBottomConstraint.constant += TAB_BAR_OFFSET;
}
else
{
commentInputBottomConstraint.constant -= TAB_BAR_OFFSET;
}
[self.view layoutIfNeeded];
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:[animationDuration floatValue] delay:0.0 options:animationOptions animations:
^{
[self.view layoutIfNeeded];
} completion:nil];
}
However, when I try to adjust the constant of the of the commentInputVerticalConstraint I receive this error message:
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the `UIView` property `translatesAutoresizingMaskIntoConstraints`)
(
"<NSLayoutConstraint:0x1899fcb0 V:[CommentEntryInput:0x176e5160(50)]>",
"<NSLayoutConstraint:0x1899e5c0 V:[CommentEntryInput:0x176e5160(125)]>"
)
I'm not sure if there is a way for me to "reset" or adjust the constraint to handle when the keyboard appears and then put it back to normal when the keyboard disappears. Any help would be appreciated.
Your problem is that -(void)updateViewConstraints is getting called more than once. So you are creating a new constraint and adding it to the view twice. Try checking if the constraint is nil or not.
I also don't think you need the first [self.view layoutIfNeeded] before the animation change of the constant. When changing constant, just set it then wrap the [self.view layoutIfNeeded] in a animation block to animate to that new value.

Autolayout "conflicting constraints"

I've created a custom UIView that I use modally to display a UIToolbar and UIPickerView. I'm trying to make it very reusable, so I'm creating all of the UI in code, including setting constraints.
Here's the method body I use to set the view up to be added to another view, and then to animate the controls up onto the view from the bottom.
My view hierarchy is as follows:
"Owner" view (view to which this view is added):<br>
|-->"Background" view (set to the full size of "Owner", but mainly used as a dimmed background)<br>
|-->"Container" view (view which holds the toolbar and picker)
|--> Toolbar
|--> Picker
Here's the code I use to set up the UI:
- (void)prepareForView:(UIView *)view {
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.containerView = containerView;
UIPickerView *picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 162.0f)];
[picker setTranslatesAutoresizingMaskIntoConstraints:NO];
self.picker = picker;
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 44.0f)];
[toolbar setTranslatesAutoresizingMaskIntoConstraints:NO];
UIBarButtonItem *done = ...;
UIBarButtonItem *flexSpace = ...;
UIBarButtonItem *cancel = ...;
toolbar.items = #[done, flexSpace, cancel];
[containerView addSubview:picker];
[containerView addSubview:toolbar];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[picker]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(picker)]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[toolbar]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(toolbar)]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[toolbar(==44)][picker(==162)]|" options:NSLayoutFormatAlignAllLeading metrics:nil views:NSDictionaryOfVariableBindings(toolbar, picker)]];
[containerView layoutIfNeeded];
[self addSubview:containerView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[containerView]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(containerView)]];
self.containerTop = [NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0f constant:self.frame.size.height];
[self addConstraint:self.containerTop];
[self layoutIfNeeded];
}
Basically, I want containerView to be sized according to its contents (which should be a static 206 points). Then, I set a vertical space constraint between its top and the top of its superview (which is self). Later, I animate changing that so that the toolbar and picker "slide up" onto the screen.
Here's the animation code (the error is always triggered BEFORE this point):
// Add the view as a subview
[view addSubview:self];
// Setup view for display (here's what triggers the message)
[self prepareForView:view];
// Animate into view
[UIView animateWithDuration:animated?0.4f:0.0f
animations:^{
self.alpha = 1.0f;
}
completion:^(BOOL finished) {
// Now, slide the container view in from the bottom of the screen
self.containerTop.constant = self.frame.size.height - self.containerView.frame.size.height;
[UIView animateWithDuration:animated?0.4f:0.0f
animations:^{
[self layoutIfNeeded];
}
completion:^(BOOL finished) {
if (postDisplay != nil) {
postDisplay();
}
}
];
}
];
This is currently displaying correctly on all simulators and devices in testing, but I hate having warnings/errors, and I'm worried that this might NOT work as desired at some point.
Here's the actual error message that's displayed (I'm pretty sure this is exactly the same error every time):
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x170283430 V:|-(0)-[UIToolbar:0x137590850] (Names: '|':UIView:0x170382b10 )>",
"<NSLayoutConstraint:0x170283480 V:[UIToolbar:0x137590850(44)]>",
"<NSLayoutConstraint:0x174084ba0 V:[UIToolbar:0x137590850]-(0)-[UIPickerView:0x137586100]>",
"<NSLayoutConstraint:0x174081fe0 V:[UIPickerView:0x137586100(162)]>",
"<NSLayoutConstraint:0x17409bbc0 V:[UIPickerView:0x137586100]-(0)-| (Names: '|':UIView:0x170382b10 )>",
"<NSLayoutConstraint:0x174095b80 V:[UIView:0x170382b10(736)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x174084ba0 V:[UIToolbar:0x137590850]-(0)-[UIPickerView:0x137586100]>
What I don't understand is that all of the constraints ARE expected, and then the displayed UI is what I intend. When I inspect the view's layout with po [self.containerView recursiveDescription] in the debugger, I see the frames being exactly what I think they ought to be. Where am I going wrong?
The "Container" view appears to have a height of 736 from the line below:
<NSLayoutConstraint:0x174095b80 V:[UIView:0x170382b10(736)]>
You have laid out the view as below:
------ Top of UIView ------- (y = 0)
- 0 Space -
UIToolBar (y = 0 to y = 44)
- 0 Space -
UIPickerView (y = 44 to y = 206) ***
- 0 Space -
------ Bottom of UIView ----- (y = 736)
*** This is where the conflict arrises. This can't be 0 space away from the bottom of the UIView with all of the other constraints set the way that they are or the height of the UIView cannot be what it is with all of the other constraints.
EDIT:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[containerView]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(containerView)]];
When you use "|" within a the visualFormat for a constraint this is the parent view. Within this line you are saying you want the parent view to 0 space to the top of the [containerView] and also 0 space from the bottom of the [containerView]. This makes the container view the same height as the parent.
Edit #2:
Look at the following. I believe this is close to what you are trying to accomplish.
[containerView addSubview:picker];
[containerView addSubview:toolbar];
[self addSubview:containerView];
// Height & Width for containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[containerView(%f)]", containerView.frame.size.height] options:nil metrics:nil views:#{#"containerView":containerView}]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:[containerView(%f)]", containerView.frame.size.width] options:nil metrics:nil views:#{#"containerView":containerView}]];
// Height & Width for picker
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[picker(%f)]", picker.frame.size.height] options:nil metrics:nil views:#{#"picker":picker}]];
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:[picker(%f)]", picker.frame.size.width] options:nil metrics:nil views:#{#"picker":picker}]];
// Height & Width for toolbar
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[toolbar(%f)]", toolbar.frame.size.height] options:nil metrics:nil views:#{#"toolbar":toolbar}]];
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:[toolbar(%f)]", toolbar.frame.size.width] options:nil metrics:nil views:#{#"toolbar":toolbar}]];
// Vertical Positioning of picker & toolbar in containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[picker]" options:nil metrics:nil views:#{#"picker":picker}]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[toolbar]-0-|" options:nil metrics:nil views:#{#"toolbar":toolbar}]];
// Horizontal Positioning of picker & toolbar in containerView
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:picker attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:toolbar attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
// Center containerView (X/Y) in parent (self)
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
Edit #3: Using 'views' and 'metrics' dictionaries.
NSDictionary *views = #{#"containerView":containerView, #"picker":picker, #"toolbar":toolbar};
NSDictionary *metrics = #{#"hCV":containerView.frame.size.height,
#"wCV":containerView.frame.size.width,
#"hP":picker.frame.size.height,
#"wP":picker.frame.size.width,
#"hT":toolbar.frame.size.height,
#"wT":toolbar.frame.size.width};
[containerView addSubview:picker];
[containerView addSubview:toolbar];
[self addSubview:containerView];
// Height & Width for containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[containerView(hCV)]" options:0 metrics:metrics views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[containerView(wCV)]" options:0 metrics:metrics views:views]];
// Height & Width for picker
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[picker(hP)]" options:0 metrics:metrics views:views]];
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[picker(wP)]" options:0 metrics:metrics views:views]];
// Height & Width for toolbar
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[toolbar(hT)]" options:0 metrics:metrics views:views]];
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[toolbar(wT)]" options:0 metrics:metrics views:views]];
// Vertical Positioning of picker & toolbar in containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[picker]" options:0 metrics:nil views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[toolbar]-0-|" options:0 metrics:nil views:views]];
// Horizontal Positioning of picker & toolbar in containerView
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:picker attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:toolbar attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
// Center containerView (X/Y) in parent (self)
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];

iOS Auto Layout When Loading Another Nib

I have a detail view in a story board. I want to load another Nib in that view when a certain condition is met. But when I do, the auto layout its all screwed you.
I am trying to display the UIView above the UIWebView like an overlay view. I want the UIView to have the same ratio of the device with a maximum height of 400 between the top and bottom layout guide.
Here is the code I used to load the Nib:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController.navigationBar setTintColor:[UIColor whiteColor]];
// Do any additional setup after loading the view, typically from a nib.
UINib *s = [UINib nibWithNibName:#"Square1" bundle:nil];
NSArray *array = [s instantiateWithOwner:self options:nil];
StopView *stopView = (StopView *)[array objectAtIndex:0];
[stopView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:stopView];
id topGuide = self.topLayoutGuide;
id bottomGuide = self.bottomLayoutGuide;
UIWebView *webView = self.detailWebView;
NSDictionary *views = NSDictionaryOfVariableBindings(stopView, topGuide, bottomGuide, webView);
// this is here to stop the auto layout from reporting that the guides has
// ambiguous layout
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topGuide]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[bottomGuide]|" options:0 metrics:nil views:views]];
// center the stop view in the super view, both lines below are needed
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-(>=12)-[stopView(<=400)]-(>=12)-|"
options: 0
metrics:nil
views:views]];
// set the height to a ratio of the width
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeWidth
relatedBy:0 toItem:stopView
attribute:NSLayoutAttributeHeight
multiplier:0.66667f constant:0];
[self.view addConstraint:con2];
// center the Stop View X,Y with the super view
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0f constant:0]];
NSLog(#"Calling configureView from viewDidLoad");
[self configureView];
}
Here are some screen shots:
As you can see in the third screen shot, my background is not showing. And you can see the T from the UILabel that placed at the top in design mode.
What am I doing incorrectly?
For a vertical constraint, you need to put a "V:" in front of the string. To get a view to be 400 max, but be as big as it can given the top and bottom spacing constraints for a smaller screen, you need to use the priority of the constraint,
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=12)-[stopView(==400#900)]-(>=12)-|"
options: 0
metrics:nil
views:views]];
The system will try to make the height of stopView as close to 400 as possible while maintaining a spacing of at least 12 to the top and bottom.

Resize subviews in Autolayout visual form

I've failed on migrating my app from Autoresize to Autolayout mechanism. All I need is a view with dynamic size and subviews that will be connected to hte left top corner with the same size as their superview. In IB I've set size for PieView (My superview) as 186x186. And let the IB to generate all needed Constraints (I will create new ones in code):
PieView.m -updateConstraints()
- (void)updateConstraints {
[super updateConstraints];
[self removeConstraintsAffectingViewAndSubviews];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
CGFloat sizeCoef = 0.7f;
CGFloat percent = [self.category getFillinPercent] / 100.0f;
percent = percent > 1.0f ? 1.0f : percent;
sizeCoef += (1.0f - sizeCoef) * percent;
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.f];//kDiameter * sizeCoef];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.0f];//kDiameter * sizeCoef];
//_imgEmptyCircle, _imgFullCircle - UIImageViews
NSDictionary *views = NSDictionaryOfVariableBindings(_imgEmptyCircle, _imgFullCircle, self);
NSDictionary *metrics = #{#"height":#100.0};
[self addConstraints:#[width, height]];
NSString *visualForm = #"H:|[_imgEmptyCircle(height)][_imgFullCircle(height)]|";
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualForm
options:0
metrics:metrics
views:views];
visualForm = #"V:|[_imgEmptyCircle(height)][_imgFullCircle(height)]|";
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualForm
options:0
metrics:metrics
views:views];
[self addConstraints:verticalConstraints];
[self addConstraints:horizontalConstraints];
}
The result really surprised me. Blue one displayed correctly as I want, but green one has strange behavior.
Off course I have error: Unable to simultaneously satisfy constraints.
First imageView:
NSIBPrototypingLayoutConstraint:0xa2795d0 'IB auto generated at build time for view with fixed frame' V:[UIImageView:0x8a37150(186)]>
NSLayoutConstraint:0xa0305c0 V:[UIImageView:0x8a37150(100)]
Second imageView: the same one
Probable for PieView:
(
NSLayoutConstraint:0x8a40750 V:[CEPieView:0x8a36e50(100)],
NSLayoutConstraint:0xa030570 V:|-(0)-[UIImageView:0x8a37150] (Names: '|':CEPieView:0x8a36e50 ),
NSLayoutConstraint:0xa0305c0 V:[UIImageView:0x8a37150(100)],
NSLayoutConstraint:0xa0305f0 V:[UIImageView:0x8a37150]-(0)-[UIImageView:0xa277cf0]>
NSLayoutConstraint:0xa030620 V:[UIImageView:0xa277cf0(100)],
NSLayoutConstraint:0xa030650 V:[UIImageView:0xa277cf0]-(0)-| (Names: '|':CEPieView:0x8a36e50 )
)
Please, give me an advise how to fix it. How to create and old autoresizing mask(UIViewAutoresizingFlexibleHeight, UIViewAutoresizingFlexibleWidth) with the help of AutoLayout mechanism. Any help will be appreciated.
The way you're setting the constraints is wrong. Basically you're asking the superview to be 100pts by 100pts, and to contains two subviews that are each 100pts by 100pts and are beside AND on top of each other. So first your superview would have to be 200pts wide and/or tall, and then you can't have two view that are both beside each other and on top.
What you really want to do (I think) is having:
NSString *visualForm = #"H:|[_imgFullCircle]|"; // No need to explicitly set the height
Then add the corresponding constraint, then:
visualForm = #"H:|[_imgEmptyCircle(height)]|";
And add another constraint for this, and do the same for vertical constraints.
Basically you'd end up with:
//_imgEmptyCircle, _imgFullCircle - UIImageViews
NSDictionary *views = NSDictionaryOfVariableBindings(_imgEmptyCircle, _imgFullCircle, self);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_imgEmptyCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_imgFullCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imgEmptyCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imgFullCircle]|"
options:0
metrics:nil
views:views]];

Using iOS Auto Layout to establish constraints between a child view controller and its parent view

Within my root view controller, I'm adding a child view controller's view as a subview, as follows:
ChildViewController *cvc = [[ChildViewController alloc] init];
[self addChildViewController:cvc];
[self.view addSubview:cvc.view];
[cvc didMoveToParentViewController:self];
and I'd now like to use a NSLayoutConstraint to position the cvc.view within the parent view (self.view), such that cvc.view is positioning 25 pts above the bottom of the parent view. My understanding is that the following should work:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSLayoutConstraint *cn =
[NSLayoutConstraint withItem:childview
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview attribute:NSLayoutAttributeBottom
multiplier: 1.0
constant: -25.0];
[superview addConstraint: cn];
But the constraint fails at runtime. I thought initially maybe the autoresizing mask in the child view was causing problems (and following the WWDC 2012 Intro video on auto layout), so I set [childview setTranslatesAutoresizingMaskIntoConstraints:NO], but then the childview simply fails to appear.
What am I doing wrong?
I'm not sure but the following should work, or something very similar:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSDictionary *constrainedViews = NSDictionaryOfVariableBindings(childview);
NSArray *constraints =
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[childview]-25-|"
options:0
metrics:nil
views:constrainedViews];
If not make sure that you are actually setting a size for the childview, something like:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSDictionary *constrainedViews = NSDictionaryOfVariableBindings(childview);
NSArray *constraints =
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[childview]|"
options:0
metrics:nil
views:constrainedViews];
Say, to make it fill the width of the view. Or:
UIView *superview = self.view;
UIView *childview = cvc.view;
NSDictionary *constrainedViews = NSDictionaryOfVariableBindings(childview);
NSArray *constraints =
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[childview(>100,<304)]-|"
options:0
metrics:nil
views:constrainedViews];
Which means something like childview should have a width bigger than 100 but less than 304 with default margins to the superview. Please note I don't know if the above constraint actually makes sense (e.g. it may just always give you 304 width childview as that would leave default margins), but it serves as an example.
To properly setup and size your child view controller with auto layout do the following:
childView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
[self.view addConstraint:left];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[self.view addConstraint:top];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
[self.view addConstraint:width];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
[self.view addConstraint:height];
If you prefer the visual format you can do the following:
childView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = #{#"childview": childview};
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-0-[childview]-0-|" options:0 metrics:nil views:views]];
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[childview]-0-|" options:0 metrics:nil views:views]];

Resources