Obtaining safeAreaInsets for interface orientations? - ios

I am using safeAreaInsets to respect iPhone X's notch and reserved menu areas.
This works great for when loading the view since the view is in the current interface orientation.
However, I need to know what the future values will be to handle animation in my willRotate method. UIViewController has a viewSafeAreaInsetsDidChange method, but that isn't updated until after the device has rotated. I need something like viewSafeAreaInsetsWillChange, but of course does not exist.
So, it seems that the only way to handle the animations in my willRotate method is by hard coding the safe area values, which is certainly far from ideal.
Is there anyway to obtain safe areas for a given orientation (portrait or landscape)?

Related

viewWillTransitionToSize: vs willTransitionToTraitCollection:

Can anyone explain the differences between these 2 methods? The docs for UIViewController explicitly state that viewWillTransitionToSize should be used for managing rotations, but clicking through to the UIContentContainer page, the willTransitionToTraitCollection method makes a confusing entrance.
I think I understand the conceptual difference between a size class change ( trait collection change ) and a size change, but I'm not sure which method to implement in which circumstances. Clarification from a UIKit wizard would be helpful!
Whenever you want to do something aa a response to a user rotating their device you should use viewWillTransitionToSize, if you do that you know for sure that your actions are executed as this is called every time your app's window changes size.
If you only want to take action when the trait collection changes, for instance if you have a certain collectionViewLayout set for a Compact size class and another you want to use for Regular you use willTransitionToTraitCollection.
If the trait collection changes then the size also changes. But it doesn't work the other way around. A portrait iPad and a landscape iPad have the same traits but are different sizes. Add multitasking to the mix and you have a whole variety of sizes that would map to just two traitCollection size classes.

Allow rotation only for one controller

I need to lock all controllers from auto rotation except one. It must rotates both portrait and landscape. I have read this topic and tried this solutions
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
but I had no luck, it didnt work. Maybe this is because I use navigation controllers, I saw some mentions of them in previous link but I didnt understand approach because author allowed orientation modes in Xcode preferences and then duplicated them in code.
Maybe some one can help with advice ?
The current method to handle rotation is viewControllerWillTransitionToSize:withTransitionCoordinator:. Documentation is at this link: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIContentContainer_Ref/index.html#//apple_ref/occ/intfm/UIContentContainer/viewWillTransitionToSize:withTransitionCoordinator:
It's a little bit different because you're working with screen sizes not orientations. This is the new way of dealing with sizes and transitions. Think of it like a responsive layout instead of distinct rotation values. To put it a different way, you're not designing for "landscape orientation" anymore, but for a screen thats wider than it is tall. It's a subtle but important difference.
You could implement this method in different ways for different view controllers. If you're using a navigation controller and want to affect child views differently, first I'd say thats terrible UX most likely. But if you still want to do it, you could handle rotation in your Nav Controller.
You should not set the device orientation like that.
you can try this method. Paste this method on the view controller to make the device support portrait and landscape except upside down orientation.
func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMaskAllButUpsideDown
}
But as chris suggested, you should use viewControllerWillTransitionToSize and handle everything regarding the orientations there.

Using size classes programmatically

I (hopefully) watched all the relevant WWDC2014 session videos and read the docs, so this question is mostly to confirm my suspicions, but please educate me.
What I want to do is animate views using Auto Layout. That in itself is not a problem. But these animations' endpoints change with different orientations. I thought I might be able to use size classes to move the views automatically on rotation, but Apple's developer guide says that animations have to be done programmatically, and from what I can gather, size classes are an Interface-Builder-only thing.
Another idea I had was using custom layout guides like the top/bottom ones IB provides, but those seem to be hardcoded.
The last thing I could do is update constraints by hand after listening to rotation events, but that is nothing new, and I feel like size classes should be useable for more than just static interfaces. Am I overestimating their purpose?
TLDR: Given two points A and B that a view can have its origin at (due to animations), how can I move both points using size classes or something similar?
After some more digging in the docs I have finally found something useable. The UIContentContainer protocol defines willTransitionToTraitCollection(:withTransitionCoordinator:), and that method's first parameter (a UITraitCollection) contains horizontal and vertical size classes as well as a UIUserInterfaceIdiom (that can be used to know whether the app is running on a iPhone or iPad, although size classes should be used for most things).
Additionally, since iOS 8 hides the status bar in landscape view, traitCollectionDidChange(previousTraitCollection:) is the corresponding method that gets called after the change happened, so the value of UIApplication.sharedApplication().statusBarHidden has changed when this method is called. Can be useful for UIScrollView's contentInset for example.
Lastly, if you need the exact screen sizes (in points, of course, but the above mentioned trait collection also knows about pixel density), there is viewWillTransitionToSize(:withTransitionCoordinator:).
Hope this helps someone else as well.

Bounds of custom UIView not correct during awakeFromNib - when are they set?

I have a custom UIView, which I have placed using Xcode (4). I need to set some default state, based on the actual bounds of the view. During awakeFromNib, bounds seems to be returning the size of the view in the storyboard layout in Xcode.
The view is in the detail side of a UISplitViewController, which in Xcode is the size of a full portrait iPad screen, but if the app loads in landscape mode then, via springs-and-struts, its size is changed, but this appears to happen after awakeFromNib.
Should I be setting this state in some other method?
It depends what sort of state you are setting - if it is dependent on the bounds then you'll need to reset it every time the device is rotated, presumably? In that case overriding setFrame: might be a better bet - be sure to call the superclass implementation before you do anything else, though.
The answer is was probably looking for, or at least the solution I have used, is to provide a public method in the UIView to be called by the parent UIViewController in viewWillAppear:

Changing iPad orientation, rotating statusbar, toolbars, but not the main view

I’m trying to set up an iPad test application, window-based, where I have a single view controller and a single view. When I rotate the iPad, I want the orientation of the toolbar to change, but not that of the view itself. For example, a sort of background view that you work in is fixed to the device, but the status bar and toolbars rotate around it. This would enable the user to work the view from all angles, but always with a correctly-oriented toolset.
A beautiful implementation of what I want can be found in the Brushes for iPad app, where the painting’s orientation is locked to the device, and the toolbars rotate around it. I think other painting apps do the same thing.
I’ve been trying to figure out how to do this, but after exhausting many many other questions here concerning orientation, I’m still at a loss.
Could anyone point me in the right direction towards a neat solution? A particular combination of autoresizes for the autoresizeMask? Countering the rotation animation with another one in the opposite direction? Using multiple concurrent view controllers, one for the rotating views and one for the non-rotating ones?
I’d very much appreciate it,
(Edit: Attempted to clarify the question, after Olie’s comment.)
To prevent rotation, you'd put this in your view controller's .m:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
But you say you still want the view frame to resize in response to the rotation. I haven't had a need to do this myself, so I'm not sure if it's sufficient to just set the autoresizingMask to have flexible width and height; you may also have to implement didRotateFromInterfaceOrientation: and use setNeedsLayout and/or resize the view manually.
I had a bug that did this a while back -- I'm pretty sure that what you're asking will get you a HIG-violation rejection from Apple. However, I'll give a shot at remembering what the problem was. I'm pretty sure it was something like this:
I had a tabbarViewController that said "I orient to any orientation."
One of the tabs was a regular-old UIViewController that said "I only do LandscapeLeft & L-Right"
When you rotated, the inside (UIVC) stayed put, but the outside (TabVC) rotated around things.
I might have some of the details backwards or otherwise convoluted, but the general ideas is: stacked VCs, not all one VC.
Good luck!
To the extent I have worked with I cannot see any simple answer to your question. What about rotating everything (tabbar, nav and status bar, your view controller) and then redrawing the content of your view controller in "old coordinates" so for the user it will look like it's not rotated?

Resources