1
votes

I'm trying to use a UISplitViewController where the secondary controller should expose a "close" function (via a button or button bar item) whenever the UISplitViewController is in side-by-side mode, but should hide the function at other times. I tried putting this in the secondary view controller:

override func viewWillAppear(_ animated: Bool) {
    if splitViewController!.primaryHidden {
        // hide the "close" UI artifact
    } else {
        // show the "close" UI artifact
    }
}

This correctly sets the visibility of the "close" function when the secondary view is first displayed, but if the UISplitViewController switches between expanded and collapsed (say, by rotating an iPhone 6s Plus), then this function is not called again (which makes sense, as the secondary controller remains visible). Consequently, the "close" function remains in its initial state--hidden or shown--even as the UISplitViewController changes mode.

How can I get the "close" function to hide or show in reaction to changes in the mode of the UISplitViewController?

3
Can you upload some photos of the problem. I’m using 4 SplitViewControllers in my app and they were a HEADACHE to get working. I’ll try and help if I can but it’d be easier for me to understand with some pics of the problem and what you want it to look like - Lance Samaria

3 Answers

2
votes

There is the UIViewControllerShowDetailTargetDidChangeNotification notification for that:

// Sometimes view controllers that are using showViewController:sender and
// showDetailViewController:sender: will need to know when the split view
// controller environment above it has changed. This notification will be 
// posted when that happens (for example, when a split view controller is
// collapsing or expanding). The NSNotification's object will be the view
// controller that caused the change.
UIKIT_EXTERN NSNotificationName const UIViewControllerShowDetailTargetDidChangeNotification NS_AVAILABLE_IOS(8_0);

Use as follows

- (void)viewDidLoad{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showDetailTargetDidChange:) name:UIViewControllerShowDetailTargetDidChangeNotification object:self.splitViewController];
}

- (void)showDetailTargetDidChange:(NSNotification *)notification{
    // changed from collapsed to expanded or vice versa
}

This Apple sample demonstrates how the table cell accessory changes from a disclosure indicator in portrait (denoting that a push will happen) to it being removed when changing to landscape split view: https://developer.apple.com/library/archive/samplecode/AdaptivePhotos/Introduction/Intro.html

Note on iOS 13 beta, use addObserver with object nil because there currently is a bug they send the notification using the wrong object. They use their new UISplitViewControllerPanelImpl from the internal class cluster instead of the UISplitViewController object. http://www.openradar.appspot.com/radar?id=4956722791710720

0
votes

Okay, I found a simple solution. I was making a novice mistake. The trick is to override viewWillLayoutSubviews() instead of viewWillAppear(animated:). Then everything works as I want. It seems that viewWillLayoutSubviews() is called (sometimes more than once) every time the containing UISplitViewController changes its display mode, and that's exactly what I need to respond to. The only gotcha is that splitViewController might be nil on some of those calls, so it needs to be implemented like this:

override func viewWillAppear(_ animated: Bool) {
    if let svc = splitViewController {
        if svc.primaryHidden {
            // hide the "close" UI artifact
        } else {
            // show the "close" UI artifact
        }
    }
}

As part of my stumbling around to find a solution, I tried overriding traitCollectionDidChange(previousTraitCollection:). (I tried this because I wanted to react to device rotations.) At first I thought I was onto something, because this function also get called whenever the device rotates. Interestingly (and, frustratingly) I found that my view's splitViewController property was nil when this function is called. It seems odd that this should be so, since neither viewDidDisappear(animated:) nor viewWillAppear(animated:) is called when the UISplitViewController reconfigures itself. But why it should be nil is, I suppose, a question for another day.

0
votes

For future reference:

What about using the UISplitViewControllerDelegate??

It has a method called

splitViewController:willChangeToDisplayMode:

that should do exactly what you where looking for.

Documentation here