0
votes

I don't understand how to use UISearchController when the view controller isn't embedded in a UINavigationController.

To show what I mean, try downloading this code: https://developer.apple.com/documentation/uikit/view_controllers/displaying_searchable_content_by_using_a_search_controller

Then, deleting the navigation controller and make the Table Search Scene the initial view controller. The table still works normally, but the search controller doesn't show up.

Is there a way to modify that code so that you can use UISearchController without embedding it in a navigation controller? I don't see anything in the documentation requiring UINavigationController. All of the stack overflow questions about this tell you to use the UISearchBar alone. I am specifically interested in using UISearchController.

2
You can use UISearchBar without the navigation bar if I remember correctly.El Tomato
it seems to me like you have no choice. There is no way to use UISearchController unless its in a navigation controllerBigBoy1337
I've made a sample app. Actually, you don't need the navigation controller. You do need a table view. Or the search controller will never appear.El Tomato
@ElTomato can I see the code? Is it on GitHub? Or can you paste it in an answer below?BigBoy1337
Give me 15 minutes.El Tomato

2 Answers

1
votes

I have a simple Xcode project where the main view controller (UIViewController) has a table view (UITableView) along with its associated table cell (UITableViewCell). The storyboard has no navigation controller. The view controller (UIViewController) comes with a table view (UITableView).

// ViewController
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
    // MARK: - Variables
    var users = [[String : Any]]()
    let searchController = UISearchController(searchResultsController: nil)
    
    
    // MARK: - IBOutlet
    @IBOutlet weak var tableView: UITableView!
    
    
    // MARK: - Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let user0 = ["name": "Jim", "age": 54, "ID": UUID().uuidString] as [String : Any]
        let user1 = ["name": "Katherine", "age": 44, "ID": UUID().uuidString] as [String : Any]
        let user2 = ["name": "Susan", "age": 28, "ID": UUID().uuidString] as [String : Any]
        let user3 = ["name": "Nancy", "age": 36, "ID": UUID().uuidString] as [String : Any]
        let user4 = ["name": "Georege", "age": 21, "ID": UUID().uuidString] as [String : Any]
        let user5 = ["name": "Arnold", "age": 68, "ID": UUID().uuidString] as [String : Any]
        let user6 = ["name": "Tom", "age": 33, "ID": UUID().uuidString] as [String : Any]
        let user7 = ["name": "Jerry", "age": 25, "ID": UUID().uuidString] as [String : Any]
        let user8 = ["name": "Kate", "age": 40, "ID": UUID().uuidString] as [String : Any]
        let user9 = ["name": "Austin", "age": 72, "ID": UUID().uuidString] as [String : Any]
        users.append(user0)
        users.append(user1)
        users.append(user2)
        users.append(user3)
        users.append(user4)
        users.append(user5)
        users.append(user6)
        users.append(user7)
        users.append(user8)
        users.append(user9)
        
        searchController.searchResultsUpdater = self
        searchController.hidesNavigationBarDuringPresentation = false
        //searchController.dimsBackgroundDuringPresentation = false
        tableView.tableHeaderView = searchController.searchBar
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    
    
    // MARK: - IBOutlet
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return users.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
        let user = users[indexPath.row]
        let name = user["name"] as! String
        let age = user["age"] as! Int
        let id = user["ID"] as! String
        cell.nameField.text = name
        cell.ageField.text = String(age)
        cell.nameField.tag = 0
        cell.ageField.tag = 1
        cell.nameField.accessibilityIdentifier = id
        cell.ageField.accessibilityIdentifier = id
        
        cell.onEditingName = { id in
            print(id)
        }
        cell.onEditingAge = { id in
            print(id)
        }
        
        return cell
    }
    
    func updateSearchResults(for searchController: UISearchController) {
        
    }
}

// MyTableViewCell.swift
import UIKit

class MyTableViewCell: UITableViewCell {
    @IBOutlet weak var nameField: UITextField!
    @IBOutlet weak var ageField: UITextField!
    
    @IBAction func nameEditingDidEnd(_ sender: UITextField) {
        onEditingName!(sender.accessibilityIdentifier!)
    }
    
    @IBAction func ageEditingDidEnd(_ sender: UITextField) {
        onEditingAge!(sender.accessibilityIdentifier!)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    
    var onEditingName: ((String) -> Void)?
    var onEditingAge: ((String) -> Void)?
}

The complete project file is available at GitHub.

1
votes

You may be over-thinking this. You could perfectly well use a UISearchController without a navigation controller or a table view. The only reason people use a table view is that it's nice to have some way of displaying the search results. But UISearchController is a perfectly ordinary view controller and just does its job regardless. It really is the simplest thing in the world — that's the beauty of it. It's so simple, it can do anything with regard to search.

Here's the tiniest example I could think of. This is basically the entire code of the app:

class ViewController: UIViewController {
    var sc : UISearchController?
    override var prefersStatusBarHidden: Bool { true }
    override func viewDidLoad() {
        super.viewDidLoad()
        let vc2 = ViewController2()
        let sc = UISearchController(searchResultsController: vc2)
        self.sc = sc
        sc.searchResultsUpdater = vc2
        let sb = sc.searchBar
        self.view.addSubview(sb)
    }
}
class ViewController2: UIViewController, UISearchResultsUpdating {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .green
    }
    func updateSearchResults(for searchController: UISearchController) {
        guard searchController.isActive else {return}
        if let t = searchController.searchBar.text, !t.isEmpty {
            print("You are searching for", t)
        }
    }
}

That illustrates perfectly what UISearchController does, namely almost nothing. It just responds to the user tapping in the search bar by putting up a secondary view controller and signaling when the user types in the search bar. Everything else is up to you.