iosswiftkeyboardcancel-button

Causing cancel button and software keyboard to appear automatically in viewDidLoad()


Note: I have attempted using .becomeFirstResponder() as shown in the link above. I stupidly left that detail out in my first post. See my code for edits.

I'm using a UISearchBar in it's own dedicated viewController (the code used for the search bar is from Google as part of their SDK). As soon as the app segues into that viewController, I want the keyboard to appear immediately without user intervention, and I don't want it to disappear. More importantly, I would like the cancel button to remain visible at all times.

I'm already aware of how to make the keyboard disappear using resignFirstResponder. I tried using .becomeFirstResponder() to no effect. However, looking on StackOverflow, out on Google, and in Apple's documentation, I'm not seeing a way to make it appear without user intervention.

All of the functions, such as editingDidBegin() require the user to do something.

This feels pretty basic, but I'm coming up empty.

private var resultsViewController: GMSAutocompleteResultsViewController?
private var searchController: UISearchController?
private var resultView: UITextView?

// tableview code for Google autocomplete
private var tableDataSource: GMSAutocompleteTableDataSource?

// not including irrelevant code...

// called in viewDidLoad()

func displaySearchBar(){

    let searchVerticalLocation = UIScreen.main.bounds.height-UIScreen.main.bounds.height+33
    resultsViewController = GMSAutocompleteResultsViewController()
    resultsViewController?.delegate = self
    //resultsViewController.becomeFirstResponder() doesn't work
    searchController = UISearchController(searchResultsController: resultsViewController)
    searchController?.searchResultsUpdater = resultsViewController
    let subView = UIView(frame: CGRect(x: 0, y: searchVerticalLocation, width: 350, height: 60))
    searchController?.searchBar.barTintColor = UIColor(red: 234/255.0, green: 93/255.0, blue: 0/255.0, alpha: 1.0)
    searchController?.searchBar.keyboardAppearance = .dark
    searchController?.searchBar.searchBarStyle = .prominent
    searchController?.searchBar.placeholder = "enter destination"
    searchController?.searchBar.isTranslucent = true
    searchController?.searchBar.leftAnchor.constraint(equalTo: subView.leftAnchor, constant: 100)
    searchController?.searchBar.rightAnchor.constraint(equalTo: subView.rightAnchor, constant: -100)
    searchController?.searchBar.delegate = self
    UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes([NSAttributedString.Key(rawValue: NSAttributedString.Key.foregroundColor.rawValue): UIColor.white], for: .normal)

    //Everything with this tap recognizer is an attempt to ensure that the cancel button on the searchbar doesn't disappear.
    let singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.cancelSearchBar(sender:)))
    singleTapGestureRecognizer.delegate = self
    singleTapGestureRecognizer.numberOfTapsRequired = 1
    singleTapGestureRecognizer.isEnabled = true
    singleTapGestureRecognizer.cancelsTouchesInView = false
    searchController?.searchBar.addGestureRecognizer(singleTapGestureRecognizer)

    subView.addSubview((searchController?.searchBar)!)
    view.insertSubview(subView, at: 1)

    // When UISearchController presents the results view, present it in
    // this view controller, not one further up the chain.
    definesPresentationContext = true
    searchController?.isActive = true
    searchController?.searchBar.becomeFirstResponder()
    //searchController?.becomeFirstResponder() doesn't work either


}

// this code brings back the cancel button if the user taps in the searchbar's text field. It's an imperfect solution. I'd rather have it so that the searchbar doesn't disappear at all. Not sure how to make that happen yet.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    searchController?.isActive = true
    return true
}

The keyboard and cancel button currently only appear when the user taps inside the text field. Tapping anywhere else on the screen causes them to disappear.


Solution

  • For the cancel button, you can make it appear automatically using searchBar.showsCancelButton = true. However, this will not respond to touches until the keyboard appears.

    Calling .becomeFirstResponder on the searchBar will only work after the searchController is finished loading. Please reference the following article: Cannot set searchBar as firstResponder.

    Calling searchBar.becomeFirstResponder() in viewDidAppear(), and even forcing it onto the main thread using DispatchQueue.main.async{} (as some people suggest) did not work. The following code snippet, called from viewDidLoad(), is my current working solution:

        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(700), execute: {
            self.searchController?.searchBar.becomeFirstResponder()
        })
    

    I may adjust the delay to make it longer, to ensure that it works on all devices. This works on an iPhoneXR.