Leigh E
Leigh E

Reputation: 31

Using a UISearchController in a UITableView with an unwind segue on cell selection

I have a UITableViewController that has a cell which drills down to another UITableViewController displaying a large dataset. To improve usability I have added a UISearchController to the view. When the search is completed the user should click the cell which, using an unwind segue, returns the user to originating view controller bringing back its data. There is an embedded navigator as well to assist with navigation.

If I don't perform a search and simply click the cell it correctly unwinds and populates the selection in the originating cell, but if I do a search and then select a cell I get the following error:

popToViewController:transition: called on while an existing transition or presentation is occurring; the navigation stack will not be updated.

Here is my initialization code of the UISearchController:

override func viewDidLoad() {
    super.viewDidLoad()

    // Initialise search controller settings
    searchController = UISearchController(searchResultsController: nil)
    searchController.searchResultsUpdater = self
    searchController.searchBar.sizeToFit()
    searchController.dimsBackgroundDuringPresentation = false
    searchController.delegate = self
    searchController.searchBar.delegate = self
    tableView.tableHeaderView = searchController.searchBar
    definesPresentationContext = true
}

Is there a trick to achieving this functionality?

Thanks.

Upvotes: 3

Views: 565

Answers (2)

Kee Reel
Kee Reel

Reputation: 45

I had the same issue and there is my solution and explainations: When searchController.isActive = true, it pushes viewController (which contains searchController) at navigationController by itself. So, attempting to unwind from viewController to initialViewController, gives an error you described. To avoid this problem you must firstly dismiss searchController. So, instead using this line

performSegue(withIdentifier: "unwindSegueFromViewControllerToInitialViewController", sender: self)

you must use this block:

if searchController.isActive {

  searchController.dismiss(animated: false, completion: {
    self.performSegue(withIdentifier: "unwindSegueFromViewControllerToInitialViewController", sender: self)
  })

} else {

  performSegue(withIdentifier: "unwindSegueFromViewControllerToInitialViewController", sender: self)

}

Upvotes: 0

sangjoon moon
sangjoon moon

Reputation: 720

I got same errors from searchResultTableView in iOS 9.0 later. I call unwind segue trigger in searchResultTableView but it doesn't move to the destinationVC even though I have a success to deliver its data to the destinationVC.

I solve this issue with creating custom unwind segue.

Let's take a look at the app structure and its logic. The structure would be looks like this:

CustomNavigationController(subclass of UINavigationController) -> MainVC -> push segue -> SearchVC(subclass of UISearchController)

  1. MainVC is embedded in CustomNavigationController
  2. If you select the cell from the searchResultTableView, an app is move to MainVC using unwind segue. I think that you are already set unwind segue in the storyboard. So, you can just set data to deliver to the destionationVC and call performSegueWithIdentifier method in this method.

    // SearchVC or SearchResultVC
    -(void)tableView:(UITableView *)tableView 
    didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    
        CustomData *customData = self.searchResults[[self.tableView indexPathForSelectedRow].row];
    
        self.selectedData = customData;
    
        [self performSegueWithIdentifier:@"PassResult" sender:nil];
    
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
     }
    
  3. Implement prepareForSegue:sender method in SearchVC or SearchResultVC for passing data to the destinationVC(MainVC)

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    
        if ([segue.identifier isEqualToString:@"PassResult"]) {
            MainVC *destVC = (MainViewController *)segue.destinationViewController;
            destVC.selectedData = self.selectedData;
        }
    }
    
  4. Move to rootVC in your navgiation controller when you call unwind segue with performSegueWithIdentifier method. You are two options to do that. Use unwind segue method or create custom segue.

unwind segue method

If you connect searchWithResult method(in MainVC) for unwind segue id PassResult:

    - (IBAction)searchWithResult:(UIStoryboardSegue *)segue
    {

        [self.navigationController popToRootViewControllerAnimated:YES];

        // Add your code to process your custom data from unwind segue
        ....
    }

custom segue

Create custom segue and implement -perform method. You can add your custom animation. I just want to move to RootVC in here.

    @implementation CustomUnwindSegue

    - (void)perform
    {

        UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;
        UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;

        [destinationViewController.navigationController popToRootViewControllerAnimated:YES];

    }

    @end

implement segueForUnwindingToViewController:fromViewController:identifier method in CustomNavigationController. This method could not be called depend on push or modal state, but it works in this app structure.

    // refer to : http://hmcreation.net/?p=279
    -(UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
                                     fromViewController:(UIViewController *)fromViewController
                                             identifier:(NSString *)identifier{
        UIStoryboardSegue *segue;

        if ([identifier isEqualToString:@"PassResult"]) {

            segue = [[CustomUnwindSegue alloc]
                                    initWithIdentifier:identifier
                                    source:fromViewController
                                    destination:toViewController];

        }else{

            if ([super respondsToSelector:@selector(unwindForSegue:towardsViewController:)]) {


                NSLog(@"unwinding in iOS9 later");

                [super unwindForSegue:segue towardsViewController:toViewController];

            }

            NSLog(@"segueForUnwinding in iOS9 below");

            segue = [super segueForUnwindingToViewController:toViewController
                                  fromViewController:fromViewController
                                          identifier:identifier];

        }

        return segue;

    }

In iOS9 later, you can override unwindForSegue:towardsViewController method in your custom navigationController. The document of this method describe that it can dealt with pop up viewControllers to move to the destination. But it looks like that it doesn't work in UISearchController.

Upvotes: 0

Related Questions