iOS: updating constraints and frame at the same time - ios

I have in my app one UITextField on the left and one UIButton on the right. The textfield is anchored on the left at the superview (a container view) and in the right to the button.
So in the left of textfield there is a
leading space = 0 in relation of container
and on the right a
trailing space = 0 in relation of button
but if I move the button on the right way, changing the x origin value, why the textfield don't enlarge its width?
(obviously the button has its constraints about width and height and for position, but not that lock the textfield)
so if I do this
self.mybutton.frame = CGRectMake(self.mybutton.frame.origin.x+100, self.mybutton.frame.origin.y, self.mybutton.frame.size.width, self.mybutton.frame.size.height);
the button moved in the right direction but the textfield seems to doesn't enlarge its width,.
Do you know why?

Working with both Auto Layout and programmatic positioning/sizing can create a lot of headaches. Part of this is because you have created constraints in Auto Layout, which are basically "rules" that your app must follow when laying out all of it's views, and when you change the frame, bounds, or center properties you may be invalidating those rules. But since Auto Layout is not constantly recalculating the layout of your views, problems may go unnoticed until a layout recalculation is triggered.
So to answer your question, changing the frame of the button does not change the text because Auto Layout has no idea that anything has changed. Plus you haven't changed the constraints on the button so if you did call - (void) setNeedsUpdateConstraints on your text field and button, the change you are looking for won't happen. The button will move back to it's initial position, the one you set with constraints.
What you may want to do is create an IBOutlet on whatever is controlling how the button gets positioned on the x-axis (i.e. its trailing space...if that is what you are using). Then instead of doing:
self.mybutton.frame = CGRectMake(self.mybutton.frame.origin.x+100, self.mybutton.frame.origin.y, self.mybutton.frame.size.width, self.mybutton.frame.size.height);
You could do something like:
self.mybuttonXconstraint.constant = self.mybuttonXconstraint.constant + 100
[self.parentView setNeedsUpdateConstraints]
The second line is to ensure that Auto Layout knows a constraint has been changed and that it should recalculate the layout for any views involved with the parent's constraints.
This may be of interest to you as well - iOS Developer Library - Auto Layout Guide

Could you try animating the button's trailing constraint?
Like so (I changed the constraint inside an animation block for illustration purposes):
UIView.animateWithDuration(
5.0,
animations: {
self.buttonTrailingMarginConstraint.constant = 0
self.view.layoutIfNeeded() // Necessary when changing constraints.
}
)
Final result:
Git clone project: https://github.com/backslash-f/animating-constraints

Related

Autolayout with a dynamic UIView in a ScrollView

I'm using a mix of Xamarin and XCode Interface Builder to build my UI.
As you can see in the screen shots the PlaceholderView at the top can have different content. The problem I'm having is trying to keep the Submit button at the bottom of the ContentView.
I'm using the ScrollView with ContentView approach and setting constraints in IB.
In ViewDidLoad() I load the contents for the PlaceholderView and then I set the height constraint of the PlaceholderView programmatically.
public override void ViewDidLoad()
{
onlineSuspectDetails = OnlineSuspectDetailsView.Create();
onlineSuspectDetails.BackgroundColor = UIColor.Gray;
SuspectDetailsPlaceholderView.AddSubview(onlineSuspectDetails);
SuspectDetailsPlaceholderView.HeightAnchor.ConstraintEqualTo(onlineSuspectDetails.HeightAnchor, 1).Active = true;
}
Now of course I had to set a Top and Bottom constraint for the Submit Button so it's working for one type
but I can't see a way to change it depending on height of the PlaceholderView in order to keep the Submit Button at the bottom.
If I could access the Bottom constraint I can calculate the new Top constraint but I can't find a way to access the Bottom constraint. How can I do this?
Are there any alternative suggestions to how I can solve this problem?
Hmmm... a bit tricky...
There may be better ways to do it, but here is one approach:
Orange = main view background
Pale Yellow = scroll view background
Gray = UIView... "label / field pairs" container; label/field pairs are in a standard UIStackView
Cyan = UIView... "details" container
Dark Green = button
Red = UIView... this is the tricky part... is a "Shim" to hold the button at the bottom
View constraints are inset by 8 so we can see them easier than if they're taking up the full screen/view.
Gray view and Details view constraints for positions / sizes are straight-forward (looks like you have no problem with that aspect).
In this method, we use a "Shim" view, and some greater-than-or-equal-to constraints to manage the Button's position.
The Shim is pinned leading and top to Zero, and its Height constraint is set to >= -30 relative to the scroll view height. Its bottom constraint is also set to >= 8 relative to the bottom of the Details view.
This tells auto-layout to put the bottom of the Shim no more than 30-pts from the bottom of the scroll view AND at least 8-pts below the bottom of the Details view.
Then the top of the Submit button is constrained to the bottom of the Shim view.
One "quirk" that I've found when working with scroll views in Interface Builder - it can be really tough (maybe impossible?) to get IB to be happy with the necessary constraints. It will show conflicts, but if you follow IB's "fixes" the desired layout then fails.
I don't actually work with IB / Storyboards, so I just focus on avoiding auto-layout / constraint conflicts at runtime.
This is probably easier to understand by seeing the actual file, so I put this up as a GitHub repo: https://github.com/DonMag/SWMoreScrollViewStuff
How are you actually adding the button to the main view? I have done something like this before by having a master UIView for everything except the button. And then I just put the button below the view and applied auto layout to everything (should just be 0,0,0,0 on the view and button). This way your button is always at the front of your view and you can do everything else in the contained view!

Button not correctly located with autolayout after updating constraints

I'm stuck with a strange bug with Auto Layout.
I have two buttons (confirm and delete) side by side, in the same view, each taking a width that's 50% of their superview, and in some situations (when test = true), I want to hide confirm button so delete button takes all the width of the screen.
Here is my autolayout:
Confirm button:
Delete button:
Proportional width is 50% (1:2) of the superview. Now here is my call, happening in viewWillAppear:
if test {
self.confirmButtonWidthConstraint.constant = 0
self.deleteButtonWidthConstraint.constant = (self.deleteButton.superview?.frame.width)!
}
However, after doing so here is the result:
And after checking the UI debugger, I can see that oddly, this delete button now has a width of... 480, with a starting x of -160. So I don't really know what's happening here. Please help!
May I suggest a different tactic?
Embed your buttons in a stack view (UIStackView) with the buttons set to fill equally.
You can then set your button to disappear with button.isHidden = true. The stack view will handle the layout for you gracefully.
You've set the width proportionally but are updating the constraints constant property. This will need lead to the result you desire as the proportional width will simply add the constant value to the multiplier result.
Instead you should set the multiplier property of your constraints. For example:
if test {
self.confirmButtonWidthConstraint.multiplier = 0.0
self.deleteButtonWidthConstraint.multiplier = 1.0
}

Remove one view's layout constraints but keep its subviews'?

Working on a project in iOS 8 using storyboard and auto layout:
In storyboard, specified constraints for this view and its subviews
In code, in response to touch events, I'm going to change this view's size by setting its frame
To make both 1 and 2 warning free, I'm doing the following when first changing its size with code:
[theView removeConstraints:theView.constraints];
theView.translatesAutoresizingMaskIntoConstraints = YES;
theView.frame = CGRectMake(0,0,width,height);
If not doing the first line, Xcode will complain a whole bunch about constraint conflicts, however adding this line will remove all its subview's constraints as well. So my question is: is there way to just remove this uiview's constraints but not its subview, say a button on it still wants to center its self relative to this view's size and position?
First, you don't need to resize the view by setting frame otherwise what is the point of keeping the constraint at first place. You could have position that by simply having a correct initial frame.
Secondly, you have a mis-conception about "TO-WHOM" a constraint has been applied to.
say a button on it still wants to center its self relative to this view's size and position?
When you apply a position related constraint to a view you normally apply it to it's superview. Means if you want to position a subview in the horizontal centre of a view then the constraint is added on view not on the subview. That's why when you called a removeContraints: message on view that position constraint was removed and now your subview isn't bound to any constraint. However the width and height constraints are applied to subviews itself.
To solve this you need to make IBOutlets for constraint that you need to modify, which in your case should be width, height, horizontal x and top constraint; and then change the constant values for them respectively.
theView.widthConstraint.constant = newValue;

iOS Swift - bottom menu like skype

I want to create bottom menu like in Skype application.
It should be at bottom with some icons and it could slide up to show more items.
What I've created is UIView with height constraint. When there is swipe gesture or dots are tapped then I change constraint of Menu view:
topMenuViewHeightConstraint.constant = 200;
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
It's working and looks good. But I am not sure if this is correct solution. If there isn't something better. Is that animation done correct way? Would it work good if there be more controls on screen? What If I want faster animation from start and then slow ending? Thanks
This is indeed the recommended way to animate constraint changes. I would suggest a slight change in how you approach the constraints.
Instead of manually assigning the view a height, you should let auto layout generate the height for you, and use a constraint pinned to the superview.bottom to perform the animation.
When user taps your UIBarButtonItem create the view and add it to the superview with a top constraint of 0 to the bottom of the superview. Now the view is positioned "below" the screen and not yet visible to the user.
Call layoutIfNeeded() on the view to trigger auto layout
Grab the height with CGRectGetHeight() and use it to set the top constraint's constant to the negative of this value (e.g. -400).
Call layoutIfNeeded() again inside your animation block and the view will slide up from the bottom of the screen.
For gestures you can use the same approach and simple use the UIGestureRecognizer method translationInView() to adjust the top constraint's constant accordingly.
An alternative method that doesn't require referencing the height in code could be to remove the top constraint and add a bottom constraint to superview.bottom.
Also -- see my other answer to a similar question:
https://stackoverflow.com/a/28484328/1451954

How to change uiview's height using auto layout?

So I created this scenario in order to understand how views increase in height according to their content. However am still not able to make it happen.
This is what I have now:
the textview is growing according to content. however the uiview containing it is disappearing. what constraints should I use so that when the uitextview becomes bigger, its parent view also increase in height?
Start by clearing all your constraints from everything so we have a fresh slate.
Step 1: Build your view hierarchy. In this example, we want something like this:
The view controller's view, which we'll call parentView has a subview, which we'll call redView. That redView has a child, a Text Field, which we'll call textField.
The hierarchy looks like this:
Step 2: Set any constraints that are specific to any individual view. In this case, we probably only want to set a width constraint on our text view. For now, I'll just set a width of 200pts.
Step 3: Set the constraints between textView and its parent, redView. Let's say we want a 10pt border all the way around. Let's add these constraints:
Once we've added these constraints we'll gets some auto layout warnings and errors. For starters, because the constraints I added for with and space to superview don't match the actual sizes, I'll get some warnings like this:
There will also be some errors describing missing X and Y positions for redView and for textView. There are really twice as many errors here as necessary. textView knows where to position itself relative to redView. We don't need more constraints to sort out textView's position. However, redView doesn't know where to position itself yet... and so ultimately, textView also sort of doesn't exactly know.
We can update the textView's frame to get rid of the warnings, but let's go ahead and fix the actual errors.
Step 5: Set up redView's constraints relative to the superView. redView already know what size to be. Notice we had no errors for redView's width. It just doesn't know where to be. In this case, I'll go simple and say we want redView to be centered. So we'll want to add these constraints:
Now we've fixed some of the problems. The only problem that remains is the height for everything.
To fix this, we must set the content sizing priorities of textView. Set these all to 1000 and change the "Intrinsic Size" property to "Placeholder".
By now, all of the auto layout errors should be gone and we should only be left with warnings because our storyboard frames don't match what our constraints say they should.
We can fix that by selecting parentView and updating all the frames:
There's one final caveat to this auto layout puzzle when it comes to autosizing based on content size: what happens if our text view has no content?
If our textview has no content, auto layout will choose a height of 0, and our users won't even be able to see that there's a text view there, much less tap in it to add content (and make it expand). When using auto layout and content-based sizing, we should almost always be sure that we've set either an explicit or minimum size for the content view.
We don't have to worry about our textView's width, as we set this explicitly to 200. So let's add a minimum height constraint. Start by adding any height constraint:
Now go to the size inspector for the textView, find this height constraint we added, and edit it into a greater than or equal to constraint:
Storyboard won't reflect the change in content in our textView and resize it appropriately, but your constraints are now set up correctly and this will behave appropriately on your device or in the simulator.
ON UITextView make your you unselected These
Scrolling Enabled
Bounces
Bounce Horizontally
Bounce Vertically
Shows Horizontal Indicator
Shows vertical indicator
Now Change your auto layout constraints like this.
On the Storyboard Page, click on your textview.
Then click on the small triangular in the lower right corner.
Click first on "Clear Constraints".
An then on "Add Missing Constraints".
Its the easiest way.

Resources