Using Xcode Storyboards for rich rotating UI's - ios

I am working on an iPad app that relies on a rather complex layout that seems to be beyond the abilities to the auto-resizing masks to rotate cleanly from portrait to landscape.
I can easily enough hand-tweak a layout in Interface Builder for each orientation, but I am puzzling over the most elegant and maintainable way to handle making the transition between the two different layouts.
Is there any way that this can be done with segues?
Is there a way I can easily snapshot two different layouts and use code to morph between them?
Am I better off trying to use HTML5 to do the page layout and not UILabels?
Are there other better techniques that I haven't even thought of yet?
Help is much appreciated - it seems like this shouldn't be so hard.

All you scenes may or may not need to have their own subclass (depending on inheritance). Assign the subclass to the respective scene.
You then need to set the supported rotation values in the subclass. eg.:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
EDIT:
I suggest loading a respective NIB by overriding willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation:.

Related

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.

Who should manage the UI Elements, the view or the view controller?

I am currently developing an iOS app and I was wondering how to manage the UI elements. I am using a Storyboard to place my views and Autolayout to make everything resolution independent. If it helps, here's a bit of background on how I came up with this question.
Some Backgroud
I have buttons the represent piano keys and I later in code add a subview to these that draws the actual keys on them. I did this by creating #IBOutlets on the ViewController and susbscribed to the UIDeviceOrientationDidChangeNotification. This called a method that adds the subviews based on the current button frames to get the appropriate sized keys.
This worked fine when running in iOS 8.1, but if I ran the app on iOS 7, the frames of the buttons weren't updated to the new orientation by the time I got the UIDeviceOrientationDidChangeNotification, so everything was messed up in landscape. I did a little research (UIInterfaceOrientation not yet updated when UIDeviceOrientationDidChangeNotification caught in UIView) and it seems that a good solution would be to override the layoutSubviews method. Problem is, layoutSubviewsis a UIView method, and I am managing my buttons and UI in my UIViewController.
Impulsively I wanted to subclass my UIView, override layoutSubviews and from there call a method on my Controller to add the keys to the buttons correctly, but that doesn't sound right.
The Questions
My UI is managed by the UIViewController. But I need to update my views based on a method proper of a UIView. I assume, to respect the MVC principles, that my view should't now a thing about my controller, but then this brings up a few questions:
Who should be managing the UI?
Is my strategy wrong and should the
UIView hold the #IBOutlets to the buttons so that it can later
apply the subviews I need them to have?
If you read the background, do you have a suggestion for this particular situation?
I assume the complexity of this problem is relatively small and I don't need to setup notifications in NSNotificationCenter, but I may be wrong. I would really like to hear this is not the only solution.
Thanks in advance. I hope I was clear enough, but if you want/need any additional details on my particular situation, let me know and I will gladly elaborate.
if I understood the question correctly, your ViewController should be laying out the keys within it's view. A good, but not the only, place to do this is in viewDidLayoutSubviews which a method you can override on UIViewController.
Now if your views should maintain their internal layout. i.e if you buttons/keys have any subviews, you should update those in layoutSubviews on UIView as you mentioned.
Both methods will be called in response to changes in the bounds or center of your parent view.
As far as rotation changes are concerned, in iOS 8, you should use viewWillTransitionToSize:withTransitionCoordinator: or willTransitionToTraitCollection:withTransitionCoordinator:
depending on your needs.
I highly recommend that you write your layout independent of orientation. You should just use the containing view's bounds for reference and never hard code in any frames. For example perhaps one of your keys should be 1/56 the width of it's superview instead of a magical number for portrait and landscape.

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.

iOS 7 status bar overlaps with view - did they have to make it like this?

I know this has been asked before, but none of these solutions work, and that's the reason of my posting. Please do not close before considering my case.
My plist already has UIViewControllerBasedStatusBarAppearance = false.
I have already tried applying deltas, but to no result.
Changing the top level view frame in ViewWillAppear (like self.view.frame) did not succeed.
I thought of increasing the view height (storyboard attribute inspector), in combination with deltas, but my top level view X, Y are disabled in storyboard attribute inspector.
My main view doesn't have any children views because I load them into main view either dynamically or load them from XIBs which are again shared by more than view controllers. These XIBs provide layout for both Portrait and Landscape. I don't know what approach is ideal for this kind of configuration, but I would like it better if solution lies along these lines.
This approach worked partially, but gave me inconsistent results.
What makes the solution tricky is the fact that I have to support all 4 orientations - this is something I handle in code via didRotate and willRotate delegates for my other views, but failing to do it for statusbar.
Please help...
Could this link be of any help?
You might have to use the new setEdgesForExtendedLayout: method to get this working consistently?
Also, have a look at these official docs if you haven't already done so.
I ended up writing my own function to shift my all subviews (remember, not top level views whose frame is fixated by IB).
It didn't spoil my work but imagine if this was the case for a very big project with so many screens, the limitations would have made it a nightmare.

Autolayout and Device Orientation

I am developing an application which supports portrait and landscape modes. I am using auto layout to arrange my views. As i have been reading many posts and i have realized that developers commonly use one among the following approaches.
1. First approach:
Implement the UIViewController:updateConstraints method and update constraints according to device orientation.
2. Second approach:
Implement the UIViewController:viewWillLayoutSubviews method and update constraints according to device orientation.
Could anyone please tell me what is the best approach to use ? I have been searching for a best practice to combine autorotation and auto layout and nothing yet. Thanks.
I would use this UIViewController's method:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
that is called upon rotation, and call -setNeedsUpdateConstraints from there.
If you need to do additional calculations or manage the contrainsts add
- (void)updateViewConstraints
{
[super updateViewConstraints];
// do calculations if needed, like set constants
}
The best approach is to use neither. Instead, configure your constraints correctly so that no programmatic changes are required on rotation. The Auto Layout runtime will maintain the views in position as you already have specified.
Updating constraints other than changing the value of .constant is a real performance hit and should be avoided.
Using viewWillLayoutSubviews is not necessary. Auto Layout methods are updateViewConstraints (for the view controller), and updateConstraints (in the views).
I believe that the best approach is to update the constraints in -(void)updateViewConstraints by checking the device orientation. There is no need to call setNeedsUpdateConstraints in willAnimateRotationToInterfaceOrientation because it is automatically called by the iOs when the device orientation changes. Thank you all for the great effort.
for ios8+
From the documentation for:
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
A standard view controller might use this method to change the
constraints on the views it manages.

Resources