1

I'm having a problem that many other users have asked about on StackOverflow (examples: 1, 2, 3).

I'd like to have a fixed search bar when scrolling a tableview. I originally had a UITableViewController with a UISearchBar on top.

Most solutions recommended that I should have a UIViewController with a UISearchBar and a UITableView below it. I did exactly this. Below is a screenshot of my interface builder.

IB

This solution doesn't fix it, however :( The search bar still doesn't float, and when scrolling it will hide behind the navigation bar.

I have also tried this solution but I'd rather not have the search bar in the navigation bar itself.

Here's all of my TableView or Search related code (I removed everything irrelevant):

class NumberSelectorViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchControllerDelegate {

    @IBOutlet var searchBar: UISearchBar!
    @IBOutlet var tableView: UITableView!

    let searchController = UISearchController(searchResultsController: nil)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self

        searchController.searchResultsUpdater = self as UISearchResultsUpdating
        searchController.dimsBackgroundDuringPresentation = false
        definesPresentationContext = true

        // NOTE: Removing thie line removes the search bar entirely.
        tableView.tableHeaderView = searchController.searchBar

        addCancelButton()

    }

    // MARK: - Table view data source

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if isSearching() == true {
            return filteredNumberList.count
        }
        return NumberList.count
    }


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

        if isSearching() == true {
            cell.textLabel?.text = filteredNumberList[indexPath.row].NumberName
        } else {
            cell.textLabel?.text = NumberList[indexPath.row].NumberName
        }
        return cell
    }

    // MARK: - Table view delegate

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        if isSearching() == true {
            selectedNumber = filteredNumberList[indexPath.row]
        } else {
            selectedNumber = NumberList[indexPath.row]
        }
        performSegue(withIdentifier: "someSegue", sender: self)
    }

    // MARK: Searching

    func filterContentForSearchText(searchText: String, scope: String = "All") {
        filteredNumberList = NumberList.filter({ (Number) -> Bool in
            return (Number.NumberName?.lowercased().contains(searchText.lowercased()))!
        })

        tableView.reloadData()
    }

    // MARK: Search Bar

    func isSearching() -> Bool {
        return (searchController.isActive && searchController.searchBar.text != "")
    }

    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
    }

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        filteredNumbersList.removeAll()
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        self.tableView.reloadData()
    }

    extension NumberSelectorViewController: UISearchResultsUpdating {
        public func updateSearchResults(for searchController: UISearchController) {
            filterContentForSearchText(searchText: searchController.searchBar.text!)
        }

        func updateSearchResultsForSearchController(searchController: UISearchController) {
            filterContentForSearchText(searchText: searchController.searchBar.text!)
        }
    }
}
Fares K. A.
  • 1,273
  • 5
  • 24
  • 47
  • Don't add the search bar as table header, that's what makes it float - add it as a subview of `self.view` in your view controller, or do that in interface builder – mag_zbc Aug 04 '17 at 12:53
  • @mag_zbc Apologies this may be a stupid question, but isn't it already a subview of my view controller in the interface builder (see the screenshot in my post)? – Fares K. A. Aug 04 '17 at 13:00

4 Answers4

2

I actually discovered the answer. The issue was simply that I had the line:

tableView.tableHeaderView = searchController.searchBar

I removed this, and then the search bar disappeared, but it was only being hidden behind the navigation bar! So I played around with the constraints on the interface builder and it now works as normal :)

Fares K. A.
  • 1,273
  • 5
  • 24
  • 47
1

It appears you added the search bar to your view properly, it's just hidden under navigation bar. Try in viewDidLoad

self.edgesForExtendedLayout = UIRectEdgeNone;  

and get rid of

tableView.tableHeaderView = searchController.searchBar
mag_zbc
  • 6,801
  • 14
  • 40
  • 62
  • Thanks for answering! That didn't work though. `UIRectEdgeNone` doesn't work in Swift 3, so I just put [] instead (as per the question here https://stackoverflow.com/questions/40315841/how-to-set-edgesforextendedlayout-to-none-in-swift-3). Also interestingly the navigation bar is a darker shade of grey... – Fares K. A. Aug 04 '17 at 13:07
  • Do you use `UINavigationController`? – mag_zbc Aug 04 '17 at 13:09
  • I do, it's nested in a UINavigationController. – Fares K. A. Aug 04 '17 at 13:10
  • try `self.navigationController?.navigationBar.isTranslucent = false` – mag_zbc Aug 04 '17 at 13:12
  • I didn't try your latest code but I actually fixed it thanks to you, you're right. The search bar was hidden under the navigation bar. I played around with the constraints and it works. :) – Fares K. A. Aug 04 '17 at 13:13
0

you don't need to add the searchBar in the tableView or its header.

In your viewDidLoad method you can give the Edge Inset so that the content in tableView is not overlapped by the searchBar

override func viewDidLoad() {
    super.viewDidLoad()

    let edgeInsets = UIEdgeInsetsMake(52, 0, 0, 0)
    self.tableView.contentInset = edgeInsets
    self.tableView.register(UINib(nibName: "AbcCell", bundle: nil), forCellReuseIdentifier: "AbcCell")

}

Following is an image how i have placed it in one of my project and it dosen't float.

enter image description here

sarosh mirza
  • 702
  • 5
  • 12
0

Ia another view, so you could placed it anywhere for instance

anyView.addSubview(searchController.searchBar)
blacktiago
  • 351
  • 2
  • 11