ballImage.frame = CGRectOffset( ballImage.frame, 10, 10)
This will move the ballImage object 10px down and to the right for a brief second before resetting and returning to its original location. I need it to move and stay.
Most of us use AutoLayout these days. If so you can't move the frame because the constraints move the view right back again.
In that case, what you have to do is to add horiztonal and vertical constraints to the view you want to move, connect them to outlets, and then in your code modify the constant value of those constraints and call layoutIfNeeded() to trigger the constraint changes to take effect. If you put the call to layoutIfNeeded inside a UIView animateWithDuration call, the change is animated.
Related
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
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;
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
I have a uiscrollview that has multiple subviews. They are stacked one after the next with spacing constraints. They, with auto layout, define the uiscrollview's contentsize.
Each view is my "snippet view" - a 100 px view. When a user taps a snippet view, i need to replace it with my "message view" - a much taller view. The message view has an intrinsic content size.
When I replace it, I remove all of my constraints, and then apply them again so they stack all the views on top of each other and the newly added message view gets inserted in the proper order.
This actually works fine, but its not smooth. I'd like to animate this so the snippet is removed, the views below are scooted down to make room for the taller message view, the new message view is added with the origin of the previous snippet, and I animate its frame height to fill the space.
I have code that does this without auto layout and it works well. But its a tremendous amount of layout code and I was hoping to do with auto layout.
Ive tried doing the remove / reapply constraints process, and then putting a layoutIfNeeded in an animation block. The problem is the newly added message view gets added with an origin of 0,0 and then animates down to its proper position, which is not a good effect.
You need to perform an initial layout pass to get the new view into position first.
Add it as a subview, with constraints to give it the right position (you can pin to the top of the outgoing view for this purpose). Call layoutIfNeeded, then remove and update all of your constraints and perform an animated layout as you are now.
Alternatively, before you do the animated layout, manually set the frame of the incoming view to be the same as the outgoing view. The layout pass will then animate from this instead of CGRectZero. That's probably a much neater solution.
Usually you'd add your new constraints, and then animate the application of those constraints, e.g.:
[UIView animateWithDuration:0.4 animations:^{
[self.view layoutIfNeeded];
}];
That will yield a smoother transition as the new constraints are applied.
If you want to avoid having the new subview start at 0,0 and jump down, you might create a container view (which has constraints to all the other views), and add your new view to that container. So the old "snippet view" would be in that container, you would remove it from that container, put the new one in that container, and then animate the layoutIfNeeded once all the new constraints are in place. That should avoid the effect you describe. (It should also simplify the code because you'll only be mucking about with the constraints that dictate the relationship between the container view and the subviews you add to it, and everything else should be driven from that.)
One feature of my app is something that does automatic cropping of an image.
The basic idea is that someone would take a picture of a piece of paper (think: receipt), and then the image could get cropped automatically, after the borders of the paper are determined.
I'm able to determine the paper's border by using OpenCV. So, the next thing I do is to change the "center" property of each of my guides (just 2 horizontal and 2 vertical "lines" that can get dragged around manually).
Then, sometime shortly after I make all my calls to change each of the 4 guides, something else comes along and sets the "center" again. (I've overridden "setCenter" to prove this). The center seems to be reset by this: [UIView(Geometry) _applyISEngineLayoutValues].
I can't figure out why this is happening, or how to stop it, but it probably has to do with constraints. My view is a simple UIButton. When the user taps & drags on it with their finger, an action routine gets called that just changes the center. This works.
But in another case, I'm bringing up a UIImagePickerController. After they choose the picture, I determine the paper-bounds, change the "guides" centers, and then later on "_applyISEngineLayoutValues" sets them all back.
Any idea what's going on in this case? Or how I can set the center of a view, and have it actually stay?
The first rule of AutoLayout is that you can't update the frame, bounds or center of a view directly.
You must update the constraints related to the view so that the constraints update the view.
For instance, you first vertical line will have horizontal constraints something like...
1. Leading edge to superview = some value.
2. Width = some value.
This is enough (horizontally) to place this line on the screen.
Now, if you want to move this line to the right you can't just change the center you must do this...
1. Create a property in you view controller like this...
#property (nonatomic, weak) IBOutlet NSLayoutConstraint *verticalLine1LeadingConstraint;
// or if you're coding the constraint...
#property (nonatomic, strong) NSLayoutConstraint *verticalLine1LeadingConstraint;
2. Save the constraint in to that property...
// either use IB to CTRL drag the constraint to the property like any other outlet.
// or something like...
self.verticalLine1LeadingConstraint = [NSLayotuConstraint ... // this is the code adding the constraint...
[self.view addConstraint:self.verticalLine1LeadingConstraint];
Now you have a property pointing to this constraint.
Now, when you need to "update the center" of the vertical line 1...
// Calculate the distance you want the line to be from the edge of the superview and set it on to the constraint...
float distanceFromEdgeOfSuperview = // some calculated value...
self.verticalLine1LeadingConstraint.constant = distanceFromEdgeOfSuperview;
[self.view layoutIfNeeded];
This will update the position of the view and you won't get any errors.
You're using auto layout, so Fogmeister's answer is the right one, but not everyone can use auto layout - e.g. people who have to support the iPad 1 - so I'll leave this answer here.
If you need to use a view's frame but the system is adding constraints, then there is a workaround; but it's not pretty.
_applyISEngineLayoutValues sets your view's center and bounds, but doesn't touch frame. If you override setCenter: and setBounds: to do nothing, and then always use setFrame: in your own code, then _applyISEngineLayoutValues will leave you alone.
I'm not happy with this approach, but it's the only way I've found so far to stop _applyISEngineLayoutValues from pooing all over my layout logic.