[iOS 11] iOS 11で追加されたUINavigationItemのsearchControllerプロパティを使ってSearchBarをナビゲーションインターフェースに統合する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

iOS 11で追加されたUINavigationItemのsearchControllerプロパティ

iOS 11でUINavigationItemに追加されたsearchControllerプロパティを使用すると簡単にUINavigationBarにSearchBarを統合することができます。 ここで「統合」と表現したのは、「ナビゲーションバーに埋め込まれたように表示することができる」という意味です。

検証環境

本エントリは以下の環境で検証を行っています。

  • macOS Sierra バージョン 10.12.6
  • Xcode Version 9.0.1 (9A1004)
  • Swift 4
  • iPhone X シミュレーター iOS 11.0

こんな見た目になります

先にこのプロパティを使ってSearchBarを表示した結果を載せておきます。
ご覧のようにナビゲーションバーに埋め込まれる形でSearchBarが表示されます。
また、スクロール時にSearchBarが消えたり現れたりしています。

ios_11_searchController

ソースコード(該当部分を抜粋)

以下が該当のプロパティを使っている部分のソースコードです。 UISearchControllerを生成したらsearchControllerプロパティにセットするだけです。簡単ですね。 hidesSearchBarWhenScrollingもiOS 11から追加されたプロパティでスクロール時のSearchBarの表示を制御します。
注意点としてiOS 11から追加されたプロパティなので11未満の場合は別の処理が必要になります。 今回はtableHeaderViewにSearchBarをセットしています。

searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false

navigationItem.title = "Sample data"
if #available(iOS 11.0, *) {
    // UISearchControllerをUINavigationItemのsearchControllerプロパティにセットする。
    navigationItem.searchController = searchController

    // trueだとスクロールした時にSearchBarを隠す(デフォルトはtrue)
    // falseだとスクロール位置に関係なく常にSearchBarが表示される
    navigationItem.hidesSearchBarWhenScrolling = true
} else {
    // iOS 11未満は別処理が必要
    tableView.tableHeaderView = searchController.searchBar
}

ソースコード全体

ソースコード全体は以下です。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    private var searchController: UISearchController!

    private let titles = [
        "row1",  "row2",  "row3",  "row4",  "row5",
        "row6",  "row7",  "row8",  "row9",  "row10",
        "row11", "row12", "row13", "row14", "row15",
        "row16", "row17", "row18", "row19", "row20",
        "row21", "row22", "row23", "row24", "row25",
        "row26", "row27", "row28", "row29", "row30"
    ]

    private var filteredTitles = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }

    // MARK: Private Methods

    private func setup() {
        searchController = UISearchController(searchResultsController: nil)
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false

        navigationItem.title = "Sample data"
        if #available(iOS 11.0, *) {
            // UISearchControllerをUINavigationItemのsearchControllerプロパティにセットする。
            navigationItem.searchController = searchController

            // trueだとスクロールした時にSearchBarを隠す(デフォルトはtrue)
            // falseだとスクロール位置に関係なく常にSearchBarが表示される
            navigationItem.hidesSearchBarWhenScrolling = true
        } else {
            tableView.tableHeaderView = searchController.searchBar
        }

        tableView.dataSource = self
        tableView.delegate = self
    }
}

// MARK: - UISearchResultsUpdating
extension ViewController: UISearchResultsUpdating {

    func updateSearchResults(for searchController: UISearchController) {
        // SearchBarに入力したテキストを使って表示データをフィルタリングする。
        let text = searchController.searchBar.text ?? ""
        if text.isEmpty {
            filteredTitles = titles
        } else {
            filteredTitles = titles.filter { $0.contains(text) }
        }
        tableView.reloadData()
    }
}

// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if searchController.isActive {
            return filteredTitles.count
        } else {
            return titles.count
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = searchController.isActive ? filteredTitles[indexPath.row] : titles[indexPath.row]
        return cell
    }
}

// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }
}

おわりに

iOS 11で追加されたUINavigationItemのsearchControllerプロパティをご紹介しました。
このプロパティはBuilding Apps for iPhone Xの中で、SearchBar部分のレイアウト崩れを修正する手段としても使われています。
iOS 11からはこのプロパティを使っていきましょう。

参考