I have a UIScrollView with some UIViews, which again contain all kinds of other views (labels, text fields, etc.). Now, when I rotate the device I would like to alter the size and position of the views within the scroll view. In order to do so I calculate a set of constraints for both portrait and landscape mode up front and switch the constraints on the scroll view according to the current interface orientation. Basically, this works as expected. The thing is that I do not want the views to be simply put, but smoothly animated into their new positions. And this is where I am completely stuck right now. The basic animation works just fine, i.e. the views within the scroll view are perfectly animated but the subviews within these views are simply repositioned and not animated. I use the following code to kick off the animation
NSMutableArray *constraints = UIInterfaceOrientationIsLandscape(interfaceOrientation) ? self.landscapeConstraints : self.portraitConstraints;
[self.scrollView removeConstraints:self.scrollView.constraints];
[self.scrollView addConstraints:constraints];
[UIView animateWithDuration:duration
animations:^{
[self.scrollView layoutIfNeeded];
}];
As far as I know (and as far as my research goes), this is just how it should be done... but obviously I am wrong and I cannot for the life of me figure out what I need to do differently. At the end of the animation everything is positioned perfectly fine, but the subviews simply refuse to animate. Any help is greatly appreciated!
For the sake of completeness it might be worth mentioning that I load the views that I position within the scroll view from a storyboard where I also set the constraints for their subviews. I think this should not be a problem though, since the layout actually works... except for the animations.
Related
I have a UIScrollView that updates its frame after each interface orientation change, keeping its height but dynamically updating its width to always fill the new width. Since it is a horizontal scroll view with paging enabled, naturally, I am updating its content according to its new size. I do this as follows (obviously it is a simplified code, but you should get the idea):
scrollView.frame = CGRectMake(0.0f, 0.0f, window.size.width, 200.0f);
// Content is being deleted here
// Then redraw with the new width in mind
scrollView.contentSize = (window.size.width * [database count]);
[scrollView setContentOffset:CGPointMake(currentPostionX, 0.0f) animated:NO];
This method works just fine; the scrollview keeps its previous position after each interface orientation change. My problem is that it is not smooth. In fact it is quite ugly, often resulting in a jittery, jumping "animation" when the contentOffset is being reset (and it doesn't matter if I set 'animated' to YES).
There is got to be a better way of doing this and make the scroll view keep its position according to its new dimensions.
Any ideas?
Update: Actually, I call the above methods in the viewWillTransitionToSize's animateAlongsideTransition section, as this creates quite a nice transition while changing between different sizes. If I put them into the completion section, everything just "pops" into place after the rotation, which is quite ugly. This way everything is smooth, except the jumping at the right position at the very end.
In this answer, I will assume that your statement:
...UIScrollView that updates its frame after each interface orientation change...
truly means after each interface orientation change.
If so, use -viewWillTransitionToSize:withTransitionCoordinator: and visit the documentation here.
Inside a sidebar menu view controller (based on SWRevealViewController), I'm using two tables to simulate two columns. So when user clicks on an option that has sub-options, the other table view controller loads them. (See Image)
Now the problem is that not all options in the first table actually has sub-options. Thus, I need to animate the first table (table with main options) so it takes full superview width when there is no sub-options to show on the other table. Only clicking on an option that has sub-options should animate the width so the two columns appear side by side. (See Images)
(Left: Intended behaviour, Right: Current behaviour)
The logic for showing sub-options and reloading tables are done and it's working perfectly. The only problem I have is with the animation. To animate the table widths I thought of setting the auto layout for the labels inside cells as follow: H:|-8-Label-8-|, V:|-8-Label-8-|, and for the both tables: H:|LeftTable(LeftTableWidth)-0-RightTable| with creating an outlet for the LeftTableWidth constraint. So when I need to animate them I do this:
[self.view layoutIfNeeded];
self.subTableWidthConstraint.constant = width;
[UITableView animateWithDuration:0.45f
animations:^{
[self.view layoutIfNeeded];
}];
Now the table widths are getting correctly animated. However, the labels inside the cells are not being animated from the initial width values to the final values. Instead, their new values are immediately set, the moment the constraint gets changed (before the tables complete their animation). I've even tried to remove everything other than:
self.subTableWidthConstraint.constant = width;
and they're still get their new values, without even call [self.view updateConstraints].
So... any clue what I did wrong?
Thanks all!
I have the same issue with animation - layoutIfNeeded for all animation-related views in animation block helps me.
self.subTableWidthConstraint.constant = width;
[self.subTable setNeedsUpdateConstraints];
[UITableView animateWithDuration:0.45f
animations:^{
[self.subTable layoutIfNeeded];
[self.mainTable layoutIfNeeded];
[self.view layoutIfNeeded];
}];
Just figured it out! Since I'm using RTL language, I needed to set the mode of the labels to Right instead of the default Left value.
Facing a really strange issue trying to dynamically add a left navigation panel to a View Controller, (should be able to support any view controller in the app, vaguely similar to the Facebook navigation) My idea seemed fairly simple, but I'm really not seeing where it's breaking down. What I've done is created a Category on UIViewController with the following method which I would think would move all the subviews to the right, and then add the new view.
-(void)addLeftView:(UIView *)newView
{
newView.frame=CGRectMake(0, 0, newView.frame.size.width, self.view.frame.size.height);
for(UIView *view in [self.view subviews])
{
view.frame=CGRectMake(view.frame.origin.x + newView.frame.size.width, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
}
[self.view addSubview:newView];
}
What actually happens, though, is that the view is added, but the subviews do not move to the right. However, if you comment the addSubview out, everything actually does move to the right exactly as expected.
To make matters even weirder, if you wrap the view movement in a [UIView animateWithDuration:completionHandler:], where the completion handler adds the subview, the animation actually happens - all the views shift to the right, but when the subview gets added, they jump back to their starting position.
I assumed this was some sort of wacky auto-layout issue, so just to see what happened, I cleared all the constraints out of that view controller, and get the same result.
Found the answer in a similar but un-related thread. Can I disable autolayout for a specific subview at runtime?
Basically it was auto-layout reverting my positioning, so disabling it by setting this user variable on just the UIViews of problem fixed my issues.
You can set the translatesAutoresizingMaskIntoConstraints type Boolean, Value to Yes in the User Defined Runtime Attributes of the UIView you want in the xib/storyboard.
I have superview called self.myMainView
In that view I have three subviews: UIView *view1 (green view), UIView *view2 (grey view), UIImageView *view3
I want to animation moving of view 3
and when view 3 is finished this animation than I want to change view hierarchy, like on example screenshots.
Here is example images:
What you are doing is the normal, standard way to change the layering order of views. So the problem is not this, but in your words "But, it violates my animation" and "crushing my animation". The problem, then, is with your animation, if it can be "violated" and "crushed" by rearranging the layers. However, you do not provide, in your question, any information about the animation! Yet that is clearly the heart of the problem.
EDIT: Now that you've posted the animation code, I was able to test, and I see what the problem is. The problem is that you are using autolayout.
view3 is positioned by constraints. When you animate the position of view3, you violate those constraints, but this does not become immediately evident. But when you exchange the layering order of view1 and view2, layout is performed! The constraints on view3 are then enforced. Since those constraints did not change, we see view3 back where you originally had it.
The simplest solution is to turn off autolayout if you don't need it. Otherwise, you will have to change the constraints, after the animation or as part of the animation. In fact, you can reposition view3 by animating its positional constraints. I describe all this in my book, here: http://www.apeth.com/iOSBook/ch17.html#_animation_and_autolayout
If all you want to do is switch which of your 2 subviews are on top of one another, can you simply use [UIView bringSubviewToFront:] rather than directly messing with the order of your subview array? That might solve the animation problems. The other thing I would try is put an if( finished ) around your call to exchangeSubviewAtIndex to make sure it doesn't interfere with the other views being animated.
The problem was in the constraints of view 3 which doesn't change after animation and when we wants to manipulate the view hierarchy, animation is stopped work properly. Matt said about that in his answer, also he provide a solution from his great book: http://www.apeth.com/iOSBook/ch17.html#_animation_and_autolayout
thank you for that Matt.
I change this solution a little and decide to post it here for anyone who will need this. But if you want to understand this better I highly recommend you to read chapter about animation from book, link is above.
First, we need to animate our movement of view 3, then change our constraints and only after this change the view hierarchy. In the book a found several ways to done this, but this one is seemed to me the best:
layout = -35.0; // new constraint constant of self.view3
NSArray *constraintsForAnimation = self.myMainView.constraints; // all constraints of superview
NSUInteger indexOfconstraintsAttribute = [constraintsForAnimation
indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){ // get index of constraint of view 3
NSLayoutConstraint *constraint = obj;
return (([constraint.firstItem isEqual: self.view3])) ? idx : NO; // return this index if we found constraint of view 3
}];
constraint.constant = layout; // changing the constraint of view3
[UIView animateWithDuration:0.3 animations:^{ // animation
[self.view3 layoutIfNeeded]; // apply new constraints to view3
}];
[self.myMainView exchangeSubviewAtIndex:3 withSubviewAtIndex:4];// toggle view 1 with view 2
We move our view 3 only with help of constraints, we don't set frame or centre view property.
Okay, we done with and this working like as I want. Hope this help you too.
I have a UIScrollView with a View inside of it. Inside of that view is a bunch of buttons, labels, etc that fit in the View when in Portrait mode...When the iPad is rotated, I want the scrollView to kick in so the user can scroll to the bottom of the view. The app runs, but when I rotate it, the scroller never works...I believe I've wired everything up correctly and I have this code in the viewDidLoad event:
[scrollview addSubview: masterView];
scrollView.contentSize = [masterView sizeThatFits:CGSizeZero];
Is there something else I am missing? Do I need to modify the size when the iPad rotates?
thanks
There may be a problem with the content size of the UIScrollView. Without the contentSize being set larger than the actual scrollView size, scroll bars won't be shown.
You can code this in with something like this:
[scrollView setContentSize:CGSizeMake(2000,2000)];
And then changing the content size to the actual content size of what you are putting in the UIScrollView (scrollView).
If you look at the results of [masterView sizeThatFits:CGSizeZero] (e.g. NSLog or set a breakpoint in your debugger, I think you will find that it's not what you expected it to be. You might find that masterView has autoResize parameters set (which is common for a view that covers the entire screen), which means that it might, itself, be getting resized too short to fit all of its controls and scrollView is simply grabbing this shortened value itself. Take a look at that CGSize and the problem will be obvious.
I faced similar situation but my case was iPhone.
Remember that content should be larger than scroll for scrollView to kick in.
"Why would you want to go down if everything is visible in front of you ?"
use the following code:
[scrollView setContentSize:CGSizeMake(intValue, intValue)];
intValue: Integer values setting width and height of scroll.
Even if it doesn't works, don't worry there are loads of other options to figure out the solution:
1. NSLog
2. Breakpoints
3. Put up errors you are getting from console on stackoverflow