Very simple question, but I'm asking as there seems to be a lot of conflicting views and I've been completely unable to find a definitive answer, let alone a modern one.
I use Auto Layout for 99% of my handling of the user changing from portrait to landscape or vice-versa in a UIViewController. Works great. But sometimes, I have to rely on frames still for one reason or another.
How would I best handle this?
You have willAnimateRotationToInterfaceOrientation, willRotateToInterfaceOrientation, the NSNotification methods with checking status bar, and I'm sure there's some others.
If I want to change the position of a view when the user rotates, which method should I be changing the frame in? And is it best to do it with a simple setFrame: or should I be using autoresizing masks?
Since iOS6, you should not be using willRotateToInterfaceOrientation: and didRotateFromInterfaceOrientation:. These are only called on the front-most presented view controller, and will not be called on others. This means if you have a pushed view controller or a presented view controller, all others will not layout correctly.
Instead, you should always use viewWillLayout and viewDidLayout to handle rotation. They are both called inside an animation block, so anything you do which is animatable, will be animated.
For positioning views, you can either set the frames yourself, or use auto-layout constraints and adjust the constraints in viewDidLayout. If you go the auto-layout route, never remove and add constraints. Use the same constraints as much as possible and just adjust their constant values.
When I'm changing the main view frame, I typically adjust the frame in willRotateToInterfaceOrientation if I need to. Then I adjust any subviews by overriding layoutSubviews for my main view.
I don't know that this is a definitive answer, though - I don't think there really is a definitive answer - it depends on how your application is structured.
wilLRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are best used for stuff you need to do before and after rotation, respectively (for example, disabling user interaction before the rotation begins, and reenabling it after). Everything else should be done in layoutSubviews if possible.
Autoresizing masks are useful sometimes, but I usually lay everything out manually to avoid any surprises when things change between iOS releases (as they often do).
Related
I came across the project: https://github.com/monoqlo/ExpandingMenu
which adds a button to a view. when the button is added a menu appears. You can find details behind the link.
The problem is that this project doesn't support autolayout and all frames are hard coded. So it doesn't support device orientation changes.
Currently I'm trying to rebuild it with autolayout. How would you do it?
Start with a xib?
Doing everything hard coded?
Do you know some code that could be reused?
Maybe you can try these:
First: check out the code of ExpandingMenu, I know it hard code, that is to say, it use the frame of its superview or [UIScreen mainScreen]'s frame. Make clear when it set its subviews frame.
Second: first may have 2 options
one is setting its subviews frame when it init or added in view: In this case , you have to rewrite it or else, in a word , you can not use it to support device orientation.
one is setting its subviews frame when it calls layoutSubviews: In this case ,it is able to support device orientation.
That is to say: if it does not support device orientation, you can fix it by rewrite it to make it setting in layoutSubviews.
I'm new to core animation and I'm struggling with one thing - how to combine autolayout with core animation. Actually I've found only one sentence in the documentation of Core Animation which refers to Autolayout here is it
Remember to Update View Constraints as Part of Your Animation
If you are using constraint-based layout rules to manage the position of your views, you must remove any constraints that might interfere with an animation as part of configuring that animation. Constraints affect any changes you make to the position or size of a view. They also affect the relationships between the view and its child views. If you are animating changes to any of those items, you can remove the constraints, make the change, and then apply whatever new constraints are needed.
But as I've tried all is not as strait-forward as it may seem.
Here is my scenario.
I've designed a sliding menu which uses autolayout extensively. Here is the appearance of that view.
I'm using autolayout constraints to force proportional positioning of those items in the sliding menu. Actually there are a lot of constraints there and I didn't want to post all of those in my question, and even may be they are not needed for direct answer of this question, however if you need them I can update the post with those constraints.
The animation that you see in the gif was reached by only autolayout. I just added outlet to the height constraint of the sliding menu and changed it in this way: (the code is written using Xamarin Monotouch, but I believe it should be clear what is done here for pure iOS developers)
private void AnimateSlideMenuAppearance()
{
float height;
if (isSlideMenuShown) {
height = 0;
} else {
height = slideMenuHeight;
}
UIView.Animate (0.4,
delegate {
this.slideMenuHeightConstraint.Constant = height;
this.View.LayoutIfNeeded ();
},
delegate {
isSlideMenuShown = !isSlideMenuShown;
});
}
Now I want to get more sophisticated appearance transition. CLICK HERE to see the effect that I want to reach.
Just to try out I tried to implement the disappearing part of that animation with series of CABasicAnimations, but it was unsuccessful, I get strange behaviour.
Can anybody suggest what I should do here? Is that possible to use autolayout to calculate the positions of the views, but somehow override the animation between autolayout size changes? I mean in my concrete example instead of proportionally decreasing the sizes of all buttons in menu I need to add FadeOut animation to them, animate the bounds to zero and also radically increase begin time of the animations from button to button in order to get the effect that I want. Or may be I need to completely get rid of autolayout and manually calculate the sizes and animations?
What is the best practice in these kind of scenarios - when you have complex autolayouting and you need custom Core Animation transitions between autolayout changes? I hope that I described the question well.
Thank you for your answers.
This is completely feasible, while it may be complex solely because it looks like your desired cases will have multiple animations.
However, I noticed one thing in your code that's odd: you change the constant on the constraint (this.slideMenuHeightConstraint.Constant = height) in the animation block, instead of before it. For nearly all cases I can imagine, you should change the constraint before the animation block. Constraints are not visually rendered until either the next UI run loop (or by setNeedsUpdateConstraints to force it for the next run loop), or immediately by layoutIfNeeded. And since [UIView animate:...] is doing this for you, layoutIfNeeded should (generally) be the only thing in your animate block, when animating autolayout.
In your case, you will have to make the animation somewhat reactive, however - for e.g., if you want to add those buttons in like in the example and have them pop in, animate out, and grow. After calling layoutIfNeeded, you can safely check the frame size. If it's beyond your threshold (or some other metric), you can trigger then animations of the buttons. (So yes, this may be a case where I'd add more code inside the animate block -- check the threshold, begin other animation, etc).
So far in my iOS project, I've done almost everything in code. I've laid out my views by setting frames manually in the layoutSubviews and viewWillLayoutSubviews methods. However, this is a very manual way and I'm considering other methods, such as AutoLayout...my problem is that I'm inexperienced with AutoLayout (and other methods such as using Auto-resizing masks).
My problem with manually laying out is as follows:
Let's say I'm adding subviews to a view controller's view. Now let's say I'm adding subviews to those subviews, etc. My problem is that many of those child views are dynamically-sized, i.e. their size depends on data from a server.
So now it comes time to layout the child views' frames from the parent view - since parent views define the frame of their child views before the child views layout. But while the child views have been instantiated, they haven't been laid out, so they haven't set their frames, so I don't know how large they will be.
This has been extremely annoying. My solution to this problem has been to implement a method for these child views which returns how large they will be based on their data. This is extraneous, time consuming and tedious.
My question is - will switching to AutoLayout (or Auto-resizing masks) make these problems go away? Will they lend themselves to the same amount of re-usability that setting frames manually does? Am I just doing something wrong in general?
I have a VC with two imageViews. One takes up the whole screen, the other is actually slightly larger and can be moved around on the screen (Pan Gesture).
When the device changes orientation only the static view changes orientation, or just updates its frame maybe (automatically).
The second Pan UIImageView does not move. Is there a reason for this, or a switch to make this happen somewhere possibly in the storyboard properties of the UIImageView itself? I would like the view to move too.
I understand that an option would be to use the methods that confirm the orientation has completed a change and transform the imageView myself, which is not a problem... but checking I am not missing something that could do this for me automatically first.
Ensure you are using correct AutoResizeMasks on the imageview. Provide your code or storyboard description for a more complete answer.
-Additionally if you are using AutoLayout, you may need to add the appropriate constraints (making it slightly larger than the view may confuse it as to your intention without proper suggestions via constraints).
I need a view controller(on ipad) to share two modes, one in portrait and one in landscape. Actually, I pretty much want to mimic the functionality of UISplitViewController, but I want to be able to use not as the top level view controller. HIG guidelines aside, I have a general problem that I think anyone who is switching views between orientations will run into.
1) To provide for smooth transitions between views, I would like to call my view changes( and animations) inside willRotateToInterfaceOrientation:duration instead of didRotateToInterfaceOrientation method. The problem is, at this stage, the view frame and bounds have not yet changed to their new ones, so you end up having to set the frame manually, like:
subview.frame = CGRectMake(0,0,320,768);
instead of something nicer, maybe:
subview.frame = CGRectMake(0,0,320, self.view.frame.height);
2) Furthermore, even if you try such shenanigans, if the view autoresizes its subviews, you still get nowhere with it. If you disable the autoresizing of subviews, then stuff like this does not even work anymore:
subview.frame = self.view.frame; //because then you'd have to always specify the exact rect.
Does anyone have any insight to offer on this?
Thanks!
Try using willAnimateRotationToInterfaceOrientation:duration: instead.
Also, if you have your autoresizing masks set up correctly on the subviews, you shouldn't need to care whether the main view has been adjusted yet or not. Just size the subviews to fit appropriately inside the main view as it is sized now and it will just work.
If you don't (or can't) have your autoresizing masks set up correctly, you should then already have code to handle size changes in the main view's layoutSubviews method. So again, you shouldn't have to care much whether the main view has been resized yet or not.