Showing both Master and Detail View in portrait mode in MGSplitViewController - ipad

I am currently integrating MGSplitViewController in one of my application and its working properly.
But i want to modify the way it is displaying currently in portrait mode.So i want in portrait mode whenever application open it should display both master as well as detail view controller.So on pressing the navigation bar button it will again hide and show the left root view controller.
So i have changed the code to
- (BOOL)shouldShowMasterForInterfaceOrientation:(UIInterfaceOrientation)theOrientation
{
// Returns YES if master view should be shown directly embedded in the splitview, instead of hidden in a popover.
//return ((UIInterfaceOrientationIsLandscape(theOrientation)) ? _showsMasterInLandscape : _showsMasterInPortrait);
return YES;
}
Now both portrait and landscape mode is showing both root and detail view controller as i wants but the problem is navigation bar button is not working to hide & show left root view controller.
Any body have done this?

Instead of modifying the MGSplitViewController source or subclassing it, you can use the showsMasterInPortrait property of the MGSplitViewController to toggle the master on and off from your application code. This has always worked fine for me.
Update with specifics:
I wouldn't use the bar button item that the split view controller provides - it's not overly useful for our purposes. Instead, set up your own button, with an associated action which toggles the showsMasterInPortrait property of your split view controller. To get to the latter, wire up an outlet property. You'll also need an outlet for the button itself if you're going to hide the button in landscape. Make sure that's wired up correctly in IB, too.
In the header, that means something like this:
#property(nonatomic,assign) IBOutlet MGSplitViewController* splitVC;
#property(nonatomic,assign) IBOutlet UIBarButtonItem* toggleButton;
- (IBAction)toggleMasterViewTouched:(id)sender;
And in the class definition:
#synthesize splitVC, toggleButton;
- (IBAction)toggleMasterViewTouched:(id)sender
{
BOOL master_shown = !self.splitVC.showsMasterInPortrait;
// Note: toggle the button's label text and/or icon between "hide" and "show" versions
self.toggleButton.title = master_shown ? #"Hide Master" : #"Show Master";
self.splitVC.showsMasterInPortrait = master_shown;
}
If you only want the button to appear in portrait, you'll also need to hide it on autorotation, so respond to the rotation event (still in the detail controller):
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
// hide when in landscape, show when in portrait
self.toggleButton.hidden = UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
And that should hopefully be all. You'll also want to set up defaults for showsMasterInPortrait and the toggle button label and visibility somewhere, probably in viewDidLoad.

Related

UISplitViewController how to show the master view?

I need a ridiculously simple thing - in one of the detail views of my UISplitViewController I have a button. Clicking it should show/open master view. That's it. Is it even possible?
P.S. it should work for all the layouts (iphone & ipad) and orientations. Even if the detail view part is a navigation and I am deep inside several pages, just want to open master view. You can assume iOS8+.
EDIT: Just to clarify what I meant by "deep inside several pages". Here is my storyboard screenshot:
Suppose I have a button in Detail Page 2 which should show the master. Setting the preferredDisplayMode works only for non-compact sizes, like iPad. On iPhone 6, for example, nothing changes after setting it. The back button points on Detail Page 1 so even swiping doesn't open master, it goes to previous page in detail navigation. I noticed that in this mode there is no split view at all, it is simulated by a navigation controller. So the questions is: is what I need possible at all or am I wrong trying to conceptually treat it as a "left drawer" which can be opened in any case and device?
At iOS8+ you can change visibility of the master view using
an animatable property preferredDisplayMode
#property (nonatomic) UISplitViewControllerDisplayMode preferredDisplayMode
Universal way to change visibility for all iOS versions is overriding delegate method
- (BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
return _needsHideMasterView;
}
Here _needsHideMasterView is BOOL ivar which can be changed in your code to hide master view. For example,
- (void)hideMasterView:(BOOL)needsHide
{
_needsHideMasterView = needsHide;
[splitViewController.view setNeedsLayout];
[splitViewController.view layoutIfNeeded];
}
try setting preferredDisplayMode to UISplitViewControllerDisplayModeAllVisible like
self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;

iPhone how to display a different view controller in response to device rotation?

I've noticed that most consumer-friendly Android and iPhone fitness apps have two interface modes - in portrait mode the user gets more detailed information, but when the user turns the device to landscape mode, a full screen graph is added to cover the entire screen.
I'm interested in how to implement transition to a different view controller in response to device rotation on iPhone. My initial thoughts are to intercept (willRotateToInterfaceOrientation event, then get the app delegate and add a full screen graph view controller to the window).
Is there a better way of turning an iPhone rotation into a transition to another view controller? Like hiding the status bar and pushing a modal view controller in landscape mode with animation?
First ask yourself whether you really need a separate view controller. One view controller can easily hide or unhide a graph.
If this graph needs its own view conroller then you could use a container view that contains the graph which refers to its own view conroller. That is what container views are made for.
The "Master" view controller then would just hide and unhide the container view in response to rotation events (and layout them accordingly etc. pp.)
If you prefer to add or remove the container view from its super view (most probably self.view from the "Master" view controller's point of view) then do that instead of hiding and unhiding. That is probably most appropriate.
The upside of this appoach would be that it works regardless of the navigaiton structure you are in, regardless of whether the rotated view controller was pushed or presented modally, regardless of whether you are in a tab bar driven app or a single view app, whether you are using storyboard, works with IB as well as programmatically, etc. pp.
There is nothing wrong with fetching the window instance from the app's delegate. I just don't see the need for doing so. Seems rather complicated to me compared to the alternatives.
The willRotateToInterfaceOrientation method works well.
In addition to switching views, two other useful things you might want to do in there are:
1) Hide/Show the status bar. (I like to hide it in landscape)
[[UIApplication sharedApplication] setStatusBarHidden:UIInterfaceOrientationIsLandscape(toInterfaceOrientation) withAnimation:UIStatusBarAnimationSlide];
2) Hide/Show any UINavigationBar. (Maybe your landscape view will benefit from the extra height)
[self.navigationController setNavigationBarHidden:UIInterfaceOrientationIsLandscape(toInterfaceOrientation) animated:YES];
You could have one view controller that has the willRotateToInterfaceOrientation method, and that viewcontroller has two other viewcontrollers as variables.
Once the device rotates, you switch the viewcontrollers' views (very crude code example:)
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if ((orientation == UIInterfaceOrientationLandscapeLeft) || (orientation == UIInterfaceOrientationLandscapeRight)) {
[self.secondViewController.view removeFromSuperView];
self.firstViewController.view.frame = self.bounds;
[self.view addSubView:self.firstViewController.view];
} else {
[self.firstViewController.view removeFromSuperView];
self.secondViewController.view.frame = self.bounds;
[self.view addSubView:self.secondViewController.view];
}
}

How to add iPad subview controller to act like a Tab Bar

I have an app that I just got setup with a split view controller to display blogs on the ipad version of the app. The current setup is master controller is a table view to show the different articles off the blog, and the detail controller is a view controller with a webview inside used to show the content of the article. The issue is that I have a few other features in the app, and on the iPhone version, I use a tab bar controller to navigate. What would be some options to add buttons to the detail controller that would allow me to navigate to the other sections of the app? I know I can't get a Tab Bar Controller within the Split View Controller so I just need some guidance.
I know that the Engadget app is setup so that when you open in portrait mode, it shows the table view of apps, along with a controller at bottom to go to different things like photos, and when in landscape the table view is on the left and the text of the articles is on the right. I just want it set up so there is no blank page if you open in portrait mode, and have a feature to view other pages, besides just adding buttons to the navigation bar.
you CAN add a tabBarController to your SplitViewController's detail view. Simply create a UIViewController that responds to <UITabBarControllerDelegate> and in the xib file for that controller add a UITabBarController object and link it to your UIViewController. In your viewDidLoad add the Tab bar controller to the view:
-(void)viewDidLoad {
...
[self addChildViewController:myTabBarController];
//add the tabBarController view
[self.view addSubview:myTabBarController.view];
}
This UIViewController will be assigned to your detail view. this works and I've tried it before. However, whether apple would allow it? i don't know!
EDIT:
first of you should never call viewDidLoad yourself.
Also, i don't think you need rootipad and detailipad as long as the controllers are linked to the SplitViewController in the xib file.
in your RootViewiPad (master) add the following inside its viewDidLoad :
-(void)viewDidLoad {
...
if (self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
//select the first row to load
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:NO scrollPosition:UITableViewScrollPositionNone];
//NOTE, I'm assuming that selecting the cell will load the detail view.
//if that is not the case, you need to do what ever you need to load the
//detail view.
}
}
if you want this behavior to happen every time (not only on first load) then add the code to the -(void)viewWillAppear method instead.

Affecting a UINavigationBar's Back Button Method (iOS)

I have a table view that pushes to a detail view controller. From the detail view controller, when I press the 'back' button, I'd like an integer value to change. How do I edit the navigation bar back button's action programatically. The back button is automatically placed in my app because I'm using a table view so I didn't actually create the button, so I don't know how to affect it's method.
To be clear, I still want the back button to go back to the original view, but simultaneously change an integer's value. Thanks!
Thanks PengOne to point me to this direction.
Add the UINavigationBarDelegate in the header file and use this in the .m file:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
//insert your back button handling logic here
// let the pop happen
return YES;
}
I've figured out an easy fix to this. I simply unchecked 'Shows Navigation Bar' in the Interface Builder for the UINavigationController that the Table View was contained in. Then I used a UINavigationBar to replicate the look (but be able to add and delete buttons as I pleased).
After that I just created IBAction's that I connected to the buttons and could control an integer value from there.
(P.S. The only problem with this is that there is no 'Back' button left pointing arrow shape in the XCode interface builder as many of you know. There are solutions around this that are pretty easily found if you search).
If you're using a UINavigationController, then UINavigationBarDelegate is the delegate class and it implements -navigationBar:shouldPopItem. You can put the action you want to trigger in that method, e.g. incrementing or decrementing a counter.
You could try implementing viewDidDisappear, which should be called as the detail view controller's view goes out of view.

willHideViewController called despite the app is in landscape mode

I'm developing an iPad app that launches in landscape mode.
The first screen displays a UISplitViewController and my issue is that altough the app is in landscape mode the delegate is notified on splitViewController:willHideViewController:withBarButtonItem:forPopoverController: despite that the documentation states that:
When the split view controller rotates
from a landscape to portrait
orientation, it normally hides one of
its view controllers. When that
happens, it calls this method to
coordinate the addition of a button to
the toolbar (or navigation bar) of the
remaining custom view controller. If
you want the soon-to-be hidden view
controller to be displayed in a
popover, you must implement this
method and use it to add the specified
button to your interface.
As the app is in landscape mode and not transitioning to portrait I don't get why my delegate is notified. Why is it so?
valentin, to directly answer "why is it so?", i think the answer is simply that it's a bug in the implementation of their API.
as you seem to have found, when in landscape orientation, it calls the above when it sort of seems that it shouldn't, and then calls splitViewController:willShowViewController:invalidatingBarButtonItem: .
also, i discovered that when in portrait orientation, it sends a very early message (i.e. before the view.frame has been adjusted) to splitViewController:willHideViewController:withBarButtonItem:forPopoverController: .
the one thing i saw that annoyed me the most was that, using the code provided from their template creation, the button bar would appear and then disappear at startup.
my solution was to implement a workaround, which i have posted on git#github.com:johnkdoe/freeforall.git in the class KludgeWorkaroundForBuggySplitViewDelegateStartup .
make this a superclass of your current detail view controller class, as in
//#interface MyViewController : UIViewController<UISplitViewControllerDelegate>
#interface MyViewController : KludgeWorkaroundForBuggySplitViewDelegateStartup
this will set the initial button bar title to Master if you don't have something you prefer. you can override this by overriding the #property getter in your subclass implementation. if you want to do more than what's in this kludgeWorkaround class, you can override these yourself and (either copy and paste or) call [super ...] on them prior to doing your own work.
i can't say this solves the problem of what appears to me to be an implementation bug, but the workaround gets rid of the brief appearance of the button bar at startup of a split-view-controller app in landscape mode.

Resources