1
votes

Expected:

When a UIButton is tapped, show a viewcontroller modally that has a search controller and tableview with results.

When tapping on an item in the list, change the text of the search bar to what was tapped and dismiss the viewcontroller back to the original with the UIButton now set to that text.

Actual:

UIButton calls a segue to the searchViewController. searchViewController shows, and configures the searchController and tableView correctly. Tapping on a cell calls the exit segue that unwinds to the original screen and correctly updates the text in the UIButton and searchbar...

but, a freaking white screen lags on the unwind segue and its driving me crazy.

Mitigation tried:

  1. Resigning the searchController then calling the segue programmatically
  2. Calling self.dismiss(animated: true completion:nil) in didSelectRowAt
  3. Putting the dismiss on the main thread with: DispatchQueue.main.async { }
  4. calling self.presentingViewController?.dismiss(animated: true)

Video Demo of flashing

Code:

SearchDetailsViewController - the Viewcontroller to unwind to

import UIKit

class SearchDetailsViewController: UIViewController {

  @IBOutlet weak var destinationButton: UIButton!
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if searchDestination != "" {
        destinationButton.setTitle(searchDestination, for: UIControlState.normal)
        destinationButton.setTitleColor(UIColor.black, for: UIControlState.normal)
    } else {
        destinationButton.setTitle("Search Nearby", for: UIControlState.normal)
    }
  }

  @IBAction func unwindToSearchDetailsViewController(segue: UIStoryboardSegue){
  }
}

SearchViewController - the problem child. I currently have the tableview cell as the exit segue in storyboard.

    class SearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate {


    @IBOutlet weak var searchResultsTableView: UITableView!

    var destinationsObj:[String:[String]] = [:]
    var destinations:[String] = []
    var defaultDestinations:[String] = ["Search Nearby"]
    var filteredDestinations:[String] = ["Search Nearby"]
    var shouldShowSearchResults = false

    var searchActive:Bool = false
    var searchController: UISearchController!

    override func viewDidLoad() {
        super.viewDidLoad()

        defaultDestinations = recentSearches
        configureTableView()
        configureSearchController()

    }

    override func viewDidAppear(_ animated: Bool) {
        // Show search bar keyboard
        searchController.isActive = true
        DispatchQueue.main.async {
            self.searchController.searchBar.becomeFirstResponder()
        }

    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: Configure Functions
    func configureSearchController() {
        searchController = UISearchController(searchResultsController: nil) //nil lets the view controller also be the search results
        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false

        searchController.searchBar.placeholder = "Where to?"
        searchController.searchBar.delegate = self

        searchController.searchBar.sizeToFit()
        searchResultsTableView.tableHeaderView = searchController.searchBar
        searchController.delegate = self

    }

    func configureTableView() {
        searchResultsTableView.delegate = self
        searchResultsTableView.dataSource = self

        //searchResultsTableView.isMultipleTouchEnabled = false
    }

    // MARK: TableView Delegate Functions
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if shouldShowSearchResults {
            return filteredDestinations.count
        } else {
            return defaultDestinations.count
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "idCell", for: indexPath)

        if shouldShowSearchResults {
            cell.textLabel?.text = filteredDestinations[indexPath.row]
        } else {

            cell.textLabel?.text = defaultDestinations[indexPath.row]
        }

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 40.0
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


        if let value = tableView.cellForRow(at: indexPath)?.textLabel?.text {
            self.searchController.searchBar.text = value
            searchDestination = value
            if !recentSearches.contains(value) {
                recentSearches.append(value)
            }

        }
        //self.searchController.resignFirstResponder()
//        tableView.deselectRow(at: indexPath, animated: false)
//        DispatchQueue.main.async {
//            self.dismiss(animated: true, completion: nil)
//        }

       // self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
    }

    // MARK: UISearchBar Delegate Functions
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        searchBar.resignFirstResponder()
        //self.dismiss(animated: true, completion: nil)
        self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {

        if let value = searchBar.text {
            searchDestination = value
            if !recentSearches.contains(value) {
                recentSearches.append(value)
            }
        }


        //self.dismiss(animated: true, completion: nil)
        self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
    }

    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {

    }


    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        shouldShowSearchResults = true
        if searchText.characters.count > 1 {
            return
        } else {
            if let firstLetter = searchText.characters.first{
                print("Typed \(firstLetter)")
                getPredictionData(firstLetter:firstLetter.description)
            }
        }
    }

    func dismissCurrentView() {
       // self.presentingViewController?.dismiss(animated: true, completion: nil)
        self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
    }

Screenshot of my storyboard

2

2 Answers

0
votes

Well I thought I post the answer incase this happens to anyone else.

In ViewDidAppear, I was setting the searchController to active searchController.isActive = true

Well before I dismissed, I needed to set it to inactive!

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  if let value = tableView.cellForRow(at: indexPath)?.textLabel?.text {
    self.searchController.searchBar.text = value
    searchDestination = value
    if !recentSearches.contains(value) {
        recentSearches.append(value)
    }
  }
 self.searchController.isActive = false
 self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
}
0
votes

Don't perform segue, try to dismiss view controller directly and before dismissing set the property of presenting view controller to the search result