UISplitViewController Cannot Change the Master View Controller?
I am trying to change the master view controller (that appears in the popover in portrait, and on the left side in landscape) in a UISplitViewController. I'd like to switch between the master view controller being one of two different UIViewControllers (depending on an action taken elsewhere in my application).
I am using the following:
mySplitViewController.viewControllers = [NSArray arrayWithObjects:newMasterController, detailController, nil];
This correctly changes the master viewcontroller as you would expect in landscape mode. However it does not seem to change the viewcontroller used for the popover in portrait mode.
I notice that the barbuttonitem to show this master view controller is just calling showMasterInPopover: on the splitviewcontroller, and so would expect it to work in portrait mode as well, but it does not.
In fact, I can set the master view controller, see the new viewController correctly in landscape mode, ro开发者_如何学Gotate back to portrait, and the viewcontroller used in the popover is still the old one.
Is this a bug?
In case anyone is looking for a solution to this issue (or a word-around), I had a similar issue and worked it out here: Changing the master view in Split View does not update the popover in portrait
Basically, I used a navigation controller for the master view controller, and pushed/poped view controllers within that navigation controller, in order to change view controllers in the master view whilst still displaying the correct viewcontroller in portrait orientation, in the popup view.
UPDATED: please read final update at bottom! Original answer + update below may not be useful!
We have just had exactly the same problem. Sometimes I wonder if Apple actually test the classes they write with anything resembling realistic use cases, because UISplitViewController is not their finest moment.
The problem is that when you replace the master view controller in the split view, the code inside UISplitViewController doesn't update its popover controller's contentViewController property. The result is that the popover controller still has a handle on an out of date view controller, resulting in old UIs appearing, or even memory faults, when in portrait mode.
Here is our workaround.
I will assume that you have a UISplitViewControllerDelegate conforming class which stores the popoverController as a class property (see the standard sample code for UISplitViewController).
At the point at which you set the new master view controller, you also need to update the contentViewController, as follows:
mySplitViewController.viewControllers
= [NSArray arrayWithObjects:newMasterController, detailController, nil];
// in the cases where the uisplitview has already shown a popovercontroller,
// we force the popovercontroller to update its content view controller.
// This ensures any old content view in popover actually gets released by
// the popovercontroller.
if (popoverController) {
[popoverController setContentViewController:theMasterViewController
animated:NO];
}
You also must set the popover's contentViewController when your UISplitViewControllerDelegate gets informed that the popover controller is going to present a view controller:
- (void)splitViewController:(UISplitViewController*)svc
popoverController:(UIPopoverController*)pc
willPresentViewController:(UIViewController *)aViewController
{
// set the popoverController property - as per Apple's sample code
self.popoverController = pc;
// THE LINE BELOW IS THE NEW LINE!
[popoverController setContentViewController:aViewController animated:NO];
Yes, I know the above code looks insane, and you're wondering why apple couldn't just set the content view controller themselves. But they apparently don't, and this is the fix.
UPDATE
The above scheme, with setting the content view, turns out not to work after all. For example, if you set the content view to be a uinavigationcontroller, later on you get passed the root view inside the nav controller, instead of the nav controller itself. UISplitViewController just doesn't handle changing the master view in any workable way, as far as I can see.
The workaround I currently have is to install a UINavigationController as the master view, and change the root view controller of that nav controller. So I get to change the master view 'by the back door', in a way.
UPDATE 2
I give up. The approach in the first update above is flawed too; I get problems upon rotation still. Basically, it seems that if you use UISplitViewController, you shouldn't attempt any changing of the master view controller (even if you're switching the master view when the master view, e.g. as a popover, has been hidden again). Fiddling with the contents of a UINavigationController in the master view (while the master view is showing) appears like it will be ok, but anything beyond that leads to problem after problem.
Technical note: I believe the problems stem from an apparent weakness in Apple's handling of UIs: namely, Apple code will call release on UIViews and controller once hidden or removed from view, but will then later, if the containing viewcontroller is shown again, send deferred messages like viewDidDisappear to the released views/controllers (which at that point may have been deallocated).
精彩评论