Problems presenting modal view controller over UISplitViewController - ios

I have a requirement to present a modal ViewController over a UISplitViewController (i.e. obscuring both the master and detail views, when in landscape).
My code is based on Apple's sample code for Multiple Detail Views, and I've added the presentModalViewcontroller to didFinishLaunching.
To present the modal view, I've used this,
[self.window.rootViewController presentModalViewController:entryView animated:NO];
and this,
[[self.splitViewController.viewControllers objectAtIndex:0] presentModalViewController:entryView animated:NO];
...and both behave in the same way (explained below).
The modal view is presented as I intend, i.e. the view pops up and covers the UISplitView as required. All good so far.
However, as a consequence of presenting the modal view, iOS calls willHideViewController for the obscured UISplitViewController master view. I guess that's reasonable, as the master view is being hidden.
The real problem occurs when the modal view is dismissed, because willShowViewController is NOT called to undo the changes made in willHideViewController (i.e. remove the popover button added to the detail view when the master view was hidden).
This unwanted popover button then sits there, on my Detail view navbar, generally causing a nuisance.
Any thoughts on how to get willShowViewController to be called when the modal view is dismissed, or to otherwise fix this issue would be appreciated.

Related

Is it possible to segue from a modal view to a view that is not in the hierarchy on a Navigation Controller in swift?

On my main view in a NavigationController I have a button that brings up a new view modally to submit a post. I have a button to dismiss this view which brings the user back to the main view, however if the users proceeds to make a post, I want it to dismiss the view and proceed to a show the post in the Navigation controller.
The ideal effect would have the standard navigation controller's back button on the top that can bring the user back to the main view when they have finished looking at their post.
I have tried several different methods, but I tend to get an error stating "Warning: Attempt to present (The View I Want to Show) whose view is not in the window hierarchy!
Thanks for any info!
Yes, it's possible. This is the best way I've found to structure the app to allow this functionality.
RootViewController
UINavigationViewController
ContentViewController (your current "main" view controller)
You're basically going to create a new RootViewController that is going to contain your current UINavigationController as a child. I usually setup the window.rootViewController programmatically in the AppDelegate. I would also keep a global reference to it.
Have a look at Apple's documentation on this. Basically, the RootViewController code would look like this:
[self addChildViewController:navController];
navController.view.frame = self.view.bounds
[self.view addSubview:self.navController.view];
[navController didMoveToParentViewController:self];
Now, whenever you need to present a modal view controller, present it from the RootViewController, instead of the current/top view controller on the UINavigationBar. This allows you to manipulate the UINavigtaionController independently and you won't get the error you're seeing.
So present the modal view controller from the RootViewController, this covers the UINavigationController and its content (top view controller). Make any changes you need to the UINavigationController stack (push, pop, etc...) with no animation. When you're done dismiss the modal view controller (with animation) and it will show you're adjusted UINavigationController.
Hopefully, this all makes sense. It's kind of complex and hard to explain in text. :p
If you don't want to setup a RootViewController, you might want to try presenting the modal view controller from the UINavigationController, as that might have the same effect. From the view controller just do self.parentViewController.present.
The main thing you're trying to avoid is presenting a modal from a view controller and then removing that view controller (pop) before the modal is dismissed. The view controller that presents the view controller is responsible for dismissing it.
I finally got it all working (I'm still new to Swift) and the two comments on the question were very helpful. The most helpful of all was the top answer here's example on github.
Similar question

Dismiss Modally presented view makes tab bar controller (kind of) reset

I have an app which has tab bar controller as main controller. Each tab has a series of views with navigation controller and I normal push and pop those view in stack.
Weird problem is
Case 1 : If I create a UINavigationController and make a new viewController as its root, and present this NavigationController. Within this new navigation stack, I can easily present a view modally and dismiss it without a problem.
Case 2: Now without make a new UINavigationController, I present a view, and when I dismiss a view, the view beneath is behave weirdly. For example, it's the presenting view was UICollectionView, it just scroll back to 1st cell, like it's doing "reload" action and "scrollTo" the first cell. If the presentingView is a pushed view from rootView, it will just popToRoot view, which is definitely not intended.
I didn't have this problem until I implement UITabbarController, so I guess, I should know more that's going on under the hood when presenting a view and dismiss a view in UITabbarController.
I GUESS, when dismiss a view in UITabbarController view, it sort of "RESET" everything to the very first view of it's current tab. I really am not sure it's trure though.
I know it's kind of conceptual, but I can't help to think there must be something critical I am missing here.
I made silly mistake that I sublclass UITabbarController and define navigation controlllers in viewDidAppear instead viewdidLoad, so when I make the window's rootview to tabbar controller, the navigation controllers are not set properly. That's why all punky things happened. It would be nicer if just crash instead of this weird behaviors.
You can try this to go back to your first viewcontroller.
- (IBAction)buttonPressedFromVC2:(UIButton *)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
} // This is going back to VC1.
This method is will be in second viewcontroller.m file. It is button click method.

QLPreviewController loses touch after presenting a modal view controller over it

In an iPad master-detail application (the master is an UITableViewController and the detail a QLPreviewController) whenever I present a view controller modally and then dismiss it, the QLPreviewController stops receiving touch events or at least reacting to them. So, it's no longer possible to zoom in or out, or "bouncing" an image/document currently being shown, even after another image/document is selected and shown.
Keep in mind that before presenting the view controller modally everything works as expected and it's possible to zoom in/out.
Anyone knows a possible solution for this weird problem?
EDIT: added that this happens after dismissing the view controller presented modally.
EDIT 2: if the presentation style is UIModalPresentationFormSheet, the problem does not occur. It seems to only occur with UIModalPresentationFullScreen.
Although not a solution for this problem, a workaround is to present the view controller as UIModalPresentationFormSheet instead of fullscreen.
Yes that is expected behaviour.
When a ViewController is presented modally (I assume you are presenting something in a form sheet?), it prevents other view controllers receiving touch events.
The other alternative is to have a look at child view controllers as an alternative; but again, their touch events will take precedence over anything underneath.

UISplitViewController and Modal view controller presentation issues

I am having troubles with an application where I have a split view controller and would like to display a modal view controller over the top.
To test this, I have created a basic project which mimics the structure of my application. I have uploaded this to Github for anyone to download: https://github.com/CaptainRedmuff/SplitViewDemo
There are two main issues which I will detail below:
Issue 1:
When presenting the modal view controller in portrait orientation with the master view controller visible (as a popover I believe), the modal view controller is displayed underneath the master view controller. Any attempts to interact with the model view controller cause the app to crash.
Issue 2:
When presenting the modal view controller from the tab bar controller (in the master view controller), the modal view controller is automatically dismissed when the device is rotated to landscape orientation as the master view controller is removed from the hierarchy.
One possible fix I have found is to conform to the UISplitViewControllerDelegate method - (BOOL)splitViewController:shouldHideViewController:inOrientation: and return NO to force the master view controller to always be visible. This is not the behaviour that I want however so this is not a viable fix.
Considering that there is no way to programatically display or dismiss the master view controller, I am at a loss for alternative methods to present a view controller modally over the top of the entire split view controller.
Before presenting modal VC you must dismiss popover like:
[self.popover dismissPopoverAnimated:NO];
The issue occurred probably because UIPopoverController is added to the window instead of UISplitViewController.

Modal disappearing after rotating UISplitViewController

I have a strange problem UISplitViewController. I have a button in my master view controller which opens a modal view when tapped (using a simple storyboard segue).
But the modal view disappears when I rotate the iPad, but only when rotating from portrait to landscape. My master view controller is hidden in portrait, like in the native Mail application.
If I'm in landscape (when the master is always visible) and open my modal, rotating the device works correctly and my modal stays on screen.
I tried manually triggering the segue programmatically, if I call performSegueWithIdentifier: on the splitViewController, rotating works both ways. But I was wondering if this was fixable in a simpler way because I have other buttons displaying modals in the master view controller and I don't want to do an IB action for each one and lose the advantages of storyboard segues.
unfortunately it is like that, when your ipad is on portrait mode, you have a popover of your master, it is not the master in another shape. What means that you are presenting a modal using this popover as presentingViewController, so when you move from portrait to landscape the method splitViewController:willShowViewController will make your popover nil as you can see:
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
So I understand that is acceptable that your modal is going with it. So, with this you understand why when you put your action an call the performSegueWithIdentifier: on your splitViewController it doesn't happen, your modal is no longer connected with your popover.
So you may ask why it doesn't happen when you move from landscape to portrait.. and the reason is splitViewController:willHideViewController, it hides the viewController it doesn't remove it, so your modal is always connected.
So, unfortunately there is no solution and you will have to perform the actions by code..
I hope it helps,
Roberto

Resources