I have an app that I'd like to enable split view on. The UI varies slightly for iPhone vs iPad (compact vs regular width).
In compact width mode, I have a UITableView with a list of items the user can select, and when they do I push a new View Controller onto the navigation stack. However, in regular width, the UITableView list is shown on the left, and then I have some other views to the right (not implemented in a UISplitViewController). So I've implemented these 2 different screens in 2 different ViewController classes.
If my user is using an iPad with regular width and then enters split view with my app and causes the app to change to compact width, I need to change which ViewController I'm showing to the user. What is the best strategy for this?
Note: I'm not using storyboard.
Thanks!
There's 2 ways I see a solution to this. One, you can take a look at UISplitViewControllerDelegate, specifically the section on Collapsing and Expanding the Interface.
Another solution is to override your size collections so that the display is the same on iPhone and iPad, as in nothing collapses and expands, the two views are always "there". You can set the split view controller's preferredDisplayMode to Overlay which looks nice on iPhones. All you need to do is add
UITraitCollection* horizTrait = [UITraitCollection
traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
UITraitCollection* vertTrait = [UITraitCollection
traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
UITraitCollection* childTraits = [UITraitCollection
traitCollectionWithTraitsFromCollections:#[horizTrait, vertTrait]];
[self setOverrideTraitCollection:childTraits forChildViewController:self.childViewControllers[0]];
to the parent class of your UISplitViewController (If there is no parent class, you must create one. This can just be a simple navigation controller)
Edit: I should mention that the above code simply sets the traits of the device to regular-regular for all devices.
Thanks for the responses. I was thinking maybe I was supposed to be using state restoration, but then restoring a different View Controller depending on my trait collection.
Instead, I think I'll just create a view controller container and then, in willLayoutSubviews, I'll decide which view controllers to show depending on the current view's trait collections.
Thanks!
Related
To put it simply, my app has two main views: the first serves to choose some values, the second displays a report. Both are TableView based.
The app looks well on iPhone with any screen size and for any orientation, but since the app’s thought to be universal, I want to get rid of those gaps between elements when the app runs on iPad.
I decided to have the app displaying both views on iPad screen simultaneously as official YouTube app does:
(1) and (2) are the first and the second View controller.
I see it this way:
1) Some initial view controller (additional navigation view controller?) checks idiom (iPhone / iPad) and chooses the next viewController. If idiom is iPhone, the first view controller is pushed.
2) If idiom is iPad, the «new» view controller is pushed. It has two view containers containing the fist view and the second view. Containers have autolayout constraints etc.
Questions:
1) Is it a right idea in context of Apple app design philosophy? What’s the better way to create iPad view combining existing ‘iPhone’ views?
2) Where to check for device type? Shall I use the second storyboard or something? Maybe there’s a good tutorial for this case, I didn’t find one.
Thanks in advance!
For the 2nd part of your question I think you want to look into
I think you want to look into Size Classes and Auto Layout
You can make a single view but depending on the size / orientation of the device - you can enable/disable certain components of the view.
You can pin views so that in portrait you have your 1st layout and in a landscape you have your 2nd layout.
With regards to your 1st question - you do have the ability to embed a view inside of another view
I'm not sure if this is what you are looking for - but as far as what apple is pushing I believe size classes is what they "suggest". You can do a lot. The only thing is make sure you turn on the assistant editor into storyboard preview mode it will help a lot.
I am designing an iPad app using a UISplitViewController. I have configured the UISplitViewController such that the master view controller (i.e. the thinner view on the left-hand side) is always visible via splitViewController.preferredDisplayMode = .AllVisible.
The detail view controller is a UICollectionView and I would like to change the way it displays depending on whether the iPad is portrait or landscape. If it's portrait, I will make the collection view only display a single column (similar to a UITableView). If it's landscape, with more horizontal screen real estate available, then I will display multiple columns. I figured I could do this with a few conditionals within the class which implements the data source and delegate for that collection view.
I don't want to just check the orientation of the device, as I would prefer to use size classes and traits so that the app could also work on iPhone, etc. (when the same traits are encountered).
I know that each view controller has a traitCollection property, but regardless of the initial orientation of the iPad when I print out this property in the viewDidAppear method it always says that the view controller is _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Regular.
I would expect this if the iPad was portrait, but even when it's initially landscape this is printed out - although I would expect that orientation to be Regular and Regular for the horizontal and vertical size classes.
Additionally, if I implement the traitCollectionDidChange: method in the view controller it's never called on rotation or even if the expand button on the detail view controller is tapped to hide the master view controller (and give the detail view controller the full screen).
So, my question is how can I detect size class / trait changes in the view controllers within a split view controller...or am I doing this completely wrong?
For the purpose of any sample code in responses, the app is being written in Swift targeting iOS 9.
Thanks in advance for any help!
Maybe you can try to check traitCollection of self.splitViewController using the following method:
private var isCompactOrientation: Bool {
get {
return self.splitViewController?.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact
}
}
If using self.traitCollection.horizontalSizeClass, it always return .Compact.
I'm trying to find a solution I've seen implemented in some iPad apps where what appears to be a UISplitViewController does not display the master view docked to the left in landscape orientation. Instead, the behavior is exactly the same in landscape as in portrait, with a UIBarButtonItem on the left side of a UIToolbar at the top of the screen bringing up a UIPopoverController with the master view controller's view. This presents some menu options that, when selected, appear to launch new UIViewController-derived classes into the detail view.
The app I'm working on needs to take advantage of as much screen real estate as possible and having the master view with the menu options docked to the left side doesn't add much value; it actually hinders the app.
So actually what I'm trying to do is two-fold:
Suppress the docked master view in landscape orientation
Have the selection of a row (menu option) in the master view load a new UIViewController-derived class into the detail view.
I've seen examples of each by themselves, respectively:
http://vimeo.com/13054813 (Hiding the Root View of a UISplitViewController)
http://bit.ly/aypcr0 (MultipleDetailViews code example from Apple)
However, I can't seem to get both of these working together.
The reason for using this approach is that I have multiple UIViewController-derived classes that I want to display when the appropriate menu option is selected. I could just instantiate them and add their views to the existing detail view and they would display fine. The problem is that none of the UIViewController lifecycle methods ever get called besides viewDidLoad (e.g. viewWillAppear:, viewDidUnload, etc.). This also includes orientation changes, and this is a big problem for the app. It seems that the only times a UIViewController-derived class acts like a UIViewController is when it is added as a subview of the app's UIWindow, or to a container class (like UINavigationController or UISplitViewController).
Am I going down the right path with the UISplitViewController, or is there a better solution?
Thanks for all of your help in advance!
Justin
This is a good UISplitViewController replacement that has the features you want (and more). It is a direct "drop in" replacement for the real UISplitViewConroller.
http://mattgemmell.com/2010/07/31/mgsplitviewcontroller-for-ipad
I want to have multiple views in my application that I switch between.
What is the proper way to switch between UIViews that also supports UISplitViewController?
Is there a support way to switch or are Apple's controller classes designed to be root and root only?
I've tried having one root view and root controller and swap subviews in and out. One of the subviews is a UISplitViewController. It did not like the arrangement and does not display correctly. The detail view was not displayed, the master view displayed wrong orientation and wrong size.
I've then tried managing adding and removing one subview from the UIWindow in the app delegate. This works most of the time. However, the views added after the applicationDidFinishLaunching method do not appear setup correctly. They mostly look correct, however sometimes the orientation thinks its portrait when in reality its landscape. Also, when I try to display a popover, it shows up in an incorrect location. If I change the orientation, it redraws correctly. I've also have some random instances where the UISplitViewController view does not fully display, as if its frame is incorrectly sized.
Any suggestions heartily appreciated.
In applicationDidFinishLaunching, your objects haven't completed loaded from NIBs yet. Try using a viewDidLoad method instead.
What is the user-interface for switching between views? If one of these views represents a transient mode that the user enters and then exits, consider using a modal view. (See presentModalViewController:animated:.)
I would need more details about what you're doing to answer more particularly.
I have an UISplitViewController base project.
I want to have a different detail View depending the orientation of the app.
Exemple :
In landscape when I select a row in the TableView, I want the detailView to be an UIWebView.
But in portrait I want the detail view to be a complex custom view.
Is it possible ?
Thanks.
See the MultipleDetailView sample project for the basics.
The easiest way to shift views based on orientation is to use a navigation control to push an pop the each orientations custom views in response to changes to orientation. Put the nav code in willRotateToInterfaceOrientation:duration: and the view controllers will pop their views and push the other when the device rotates.
I would say, however, the using two different types of views for the same detail but different orientation will most likely confuse the user. That is not what the interface grammar teaches them to expect. In every other app, it is the same basic view with the same info just adjusted for the change in display dimensions.
You might want to think twice about whether this is a good UI design before spending the time to implement it.