13

I have a pretty standard setup of UITableview as master controller in UISplitviewController, universal iOS9 app.

Again, standard fare, I inserted a UISearchBar as the header of the table.

self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;

[self.searchController.searchBar sizeToFit];
self.tableView.tableHeaderView = self.searchController.searchBar;

self.definesPresentationContext = YES;

No need for showing the rest of my code, the search and all work as expected, on the iPhone. My issue is that the search bar does not ever receive focus or present the keyboard when run on iPad. No response, seems not to receive touches. Does not even respond to programmatically attempting to becomeFirstResponder.

Nothing happens.

The search bar is visible and placed appropriately, there are no underlay or overlay issues.

It works fine on iPhone. Receives touch, presents keyboard, search works. Presumably there is a critical difference in presentation when UISplitviewController is collapsed. With hours of search, pouring through the documentation and every tutorial I come across, I have not found a similar experience which is surprising.

Additional note: If I use multi tasking to shrink the split view to collapsed on iPad, the search bar then works, same as on iPhone. It definitely has to do with the collapsed state of the split view.

UPDATE: (Getting closer)

After further experimentation I find that the search bar works on the iPad if you start the app in landscape. In portrait mode, my primary controller is hidden. It slides in by selection of the displayModeButtonItem, which is set to the leftBarButtonItem of the detail controller. That is when the searchBar breaks and no longer will respond to touches.

If I start in landscape, then rotate to portrait, it stops working as well. Rotating back to landscape does not fix it. Once broke, it stays broke until a restart.

I tried moving definesPresentationContext to the viewWillAppear method of the master controller, but that had no effect.

Further Update:

Another unexpected circumstance is that there is no problem with the search bar at all on iPhone 6plus. Works regardless of orientation, collapsed or no and doesn't matter which state it starts in. I would expect this would be same as iPad when in landscape orientation. Works completely on iPhone.

I still have not figured this out. My latest attempt to fix, I moved all the search controller initialization to the viewDidLayoutSubviews method. No change whatsoever.

Also, I noticed that when the search is active, keyboard onscreen, if I rotate the iPad the keyboard goes away but the search bar remains active. I didn't realize at first, then I saw the cancel button still displayed. It won't receive touches and does not have keyboard, but it is apparently still first responder.

Sample Project Uploaded to GitHub: Github Repository

NOTE- There was no issue until I added my UISplitview Delegate methods to the project. I wanted to add this to my question right away, so, I haven't yet even tried to see exactly how these delegate methods affect the search bar but obviously the issue is created there somewhere.

Update:

I tried a few more variations with no success.

  • Move search bar to section header. No joy.
  • Put search bar in a UIView as a content view. No joy.
  • Remove search bar on disappear, reinsert on did appear. No joy.
  • Manipulate the size of search bar frame to ensure it is not clipped. No joy.

I also looked at the view hierarchy in profiler, the search bar is top level, not covered by anything else.

Final Wrap:

@tomSwift workaround did correct my issue on iPad. On further testing, I found it broke my iPhone UI. Presumably, this is more an issue with my own setup and the timing of creation of the Master/Detail View Controllers. It would seem some important objects no longer were created in time as perhaps the detail view controller didn't exist yet. I poked around to fix by moving critical items into App Delegate but that became unwieldy and I couldn't easily narrow it down, so I hacked a fix with:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
}

A more elegant solution is likely to be had, but this has taken enough of my time. Everything works now. Radar filed- 28304096

Community
  • 1
  • 1
Dean Davids
  • 4,174
  • 2
  • 30
  • 44
  • Dean, please provide a demo project where this reproduces. – Léo Natan Sep 10 '16 at 17:35
  • @LeoNatan, I did manage to reproduce the issue and uploaded a sample project to github. Thanks. – Dean Davids Sep 13 '16 at 14:53
  • @Dean your sample project is missing files; does not open and build in Xcode. E.g. Main.storyboard and LaunchScreen.storyboard are missing – TomSwift Sep 13 '16 at 16:30
  • It's been a while since I put something out there. I'll check and fix ASAP. – Dean Davids Sep 13 '16 at 21:07
  • According to [this](https://developer.apple.com/reference/uikit/uisplitviewcontroller) you aren't supposed to use UISplitViewControllers on a Navigation stack, but both your master and detail views are nested under navigation controllers. You might try removing those. – inorganik Sep 13 '16 at 21:44
  • New repository created. – Dean Davids Sep 13 '16 at 21:48
  • @inorganik, I don't think so. This example was started from the project template. The child view controllers are in Navigation Controllers which is typical case. I think what you are referring to would apply if I tried to put the Splitview Controller in a Navigation controller. – Dean Davids Sep 13 '16 at 21:52
  • I still haven't solved this issue. Bounty is running down. Anyone have any ideas? – Dean Davids Sep 14 '16 at 15:24

2 Answers2

4

This is clearly a bug in UISplitViewController and/or UISearchController. Certainly file a radar w/ Apple and attach your code sample.

I think the bug involves UISplitViewController.displayMode UISplitViewControllerDisplayModeAutomatic. When I change the preferredDisplayMode to UISplitViewControllerDisplayModeAllVisible then everything starts to work:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
    UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
    navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
    splitViewController.delegate = self;
    splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;

    return YES;
}

The main thing this changes is the default layout for portrait on iPad, which I understand may not be ideal. I also played with setting preferredDisplayMode to UISplitViewControllerDisplayModeAllVisible initially then setting it back to UISplitViewControllerDisplayModeAutomatic; this worked great until the screen orientation changed - then it all broke again. You might have some luck going further down that path.

I also made the following addition in the AppDelegate, to force the search controller to deactivate on displayMode change. (I also had to expose the searchController property publicly on the MasterViewController).

- (void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode {

    UINavigationController *navigationController = [svc.viewControllers firstObject];
    MasterViewController* mvc = (MasterViewController*) navigationController.topViewController;
    [mvc.searchController setActive: NO];
}
TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • Nice, I had just read the docs re: preferredDisplayMode and was going to did there myself. I will try your setting, confirm, and see how that affects my intended work flow. Hopefully I can live with that. – Dean Davids Sep 14 '16 at 17:42
  • In my actual app, I setActive:NO as you suggest but I do it on view will disappear in the Master. I think that covers it and keeps the code contained where it belongs. – Dean Davids Sep 14 '16 at 17:44
  • Success! That one line appears to have solved my issue completely and no serious alteration of my intended UI. Thank you so much, Tom, for looking at it. – Dean Davids Sep 14 '16 at 17:49
  • submitted radar: 28304096 – Dean Davids Sep 14 '16 at 18:11
1

Played around with your project and discovered that the issue is that your search controller is never set to active. So I subclassed UISplitViewController and made it a delegate for itself so it could notify the child view controllers when the mode changes, so you can set it active at the right time.

Use this delegate method to create the notification:

- (void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode {
    NSLog(@"WILL CHANGE TO DISPLAY MODE: %ld", (long)displayMode);

    NSNumber *displayNumber = [NSNumber numberWithInteger:displayMode];
    NSDictionary *userInfo = @{ @"displayMode": displayNumber };
    NSNotification *modeChangedNotif = [NSNotification notificationWithName:@"modeChanged" object:nil userInfo:userInfo];
    [[NSNotificationCenter defaultCenter] postNotification:modeChangedNotif];
}

Then in your MasterViewController you can do this in viewDidLoad:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(modeChanged) name:@"modeChanged" object:nil];

In that method, you can do a check for a particular displayMode enum, but for the sake of getting it working I just did:

- (void) modeChanged {
    [self.searchController setActive:YES];
}
inorganik
  • 24,255
  • 17
  • 90
  • 114
  • I am not sure how that would help, though I haven't tried it myself. Did you confirm the search bar becomes active when you make that call? In any case, it looks like your code would arbitrarily present the keyboard on rotation which would not be acceptable. Does the search bar respond to touches when you make these changes? – Dean Davids Sep 14 '16 at 18:17
  • It's setting the `searchController` active, not the search bar. Yes, I actually ran the code in the simulator, and tried both orientations – inorganik Sep 14 '16 at 19:24