利用 table view header 的 search bar 實現 iOS App 的搜尋功能

利用 search bar,我們可以快速在 iOS App 裡實現搜尋功能。接下來我們將示範在 table view 的 header 加上 search bar,利用它搜尋情歌王子劉德華的動人情歌。(ps: 為了簡化程式的難度,我們將重心放在本機端的搜尋,事先將劉德華歌單的 JSON 檔加到專案裡。)

加入 table view controller & navigation controller,將 table view controller 的類別設為繼承 UITableViewController 的 StoreItemListTableViewController

將 search bar 加到 table view header

方法1: 從 storyboard

將 search bar 拖曳到 table view 跟 cell 之間,讓 search bar 成為 table view 的 header。

方法2: 從程式

設定 table view 的 tableHeaderView。

class StoreItemListTableViewController: UITableViewController {    func addSearchBar() {
let searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 44))
tableView.tableHeaderView = searchBar
}
override func viewDidLoad() {
super.viewDidLoad()
addSearchBar()
}

設定 cell 畫面,搭配自訂的 cell 類別 ItemCell

設定 cell 的類別。

設定 cell 的 Reuse ID。

連結 cell 裡元件的 outlet。

class ItemCell: UITableViewCell {   @IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var itemImageView: UIImageView!

將 iTunes 抓到的劉德華歌曲 JSON 放到 asset

從以下網址抓取情歌王子劉德華的歌曲 JSON。

https://itunes.apple.com/search?term=劉德華&media=music&country=TW

定義 JSON 對應的 Codable 型別,從 asset 讀取 JSON,利用 static 變數 data 儲存歌曲的 array

import UIKitstruct StoreItem: Codable {
let trackName: String
let artistName: String
var kind: String
var artworkUrl100: URL
}
struct SearchResponse: Codable {
let results: [StoreItem]
}
extension StoreItem {
static var data: [Self] {
if let data = NSDataAsset(name: "劉德華")?.data {
let decoder = JSONDecoder()
do {
let searchResponse = try decoder.decode(SearchResponse.self, from: data)
return searchResponse.results
} catch {
print(error)
}
}
return []
}
}

加入顯示圖片的套件 Kingfisher

在 cell 裡定義設定畫面的 function configure(item:)

import UIKit
import Kingfisher
class ItemCell: UITableViewCell { @IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var itemImageView: UIImageView!

func configure(item: StoreItem) {
nameLabel.text = item.trackName
itemImageView.kf.setImage(with: item.artworkUrl100, placeholder: UIImage(systemName: "music.note"))
}
}

在 table view 顯示 StoreItem.data 儲存的歌曲 array

class StoreItemListTableViewController: UITableViewController {    var items = StoreItem.data    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "\(ItemCell.self)", for: indexPath) as! ItemCell
let item = items[indexPath.row]
cell.configure(item: item)
return cell
}

執行 App

接下來終於進入我們的重頭戲了,我們想在鍵盤按下 search 鍵時顯示搜尋的結果,比方顯示劉德華歌名有愛的歌。

當使用者按下 search 鍵時,將觸發 search bar delegate 的 function searchBarSearchButtonClicked(_:),因此接下來我們必須先設定 search bar 的 delegate。

設定 search bar 的 delegate

將 table view controller 設為 search bar 的 delegate。

方法1: 從 storyboard

從 search bar 連線到 table view controller。

方法2: 從程式

class StoreItemListTableViewController: UITableViewController {    func addSearchBar() {
let searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 44))
searchBar.delegate = self
tableView.tableHeaderView = searchBar
}

實作 table 顯示資料和 search 功能

import UIKitclass StoreItemListTableViewController: UITableViewController {    var items = StoreItem.data
var filteredItems = [StoreItem]()


override func viewDidLoad() {
super.viewDidLoad()

filteredItems = items
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return filteredItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "\(ItemCell.self)", for: indexPath) as! ItemCell
let item = filteredItems[indexPath.row]
cell.configure(item: item)
return cell
}

func search(_ searchTerm: String) {
if searchTerm.isEmpty {
filteredItems = items
} else {
filteredItems = items.filter {
$0.trackName.contains(searchTerm)
}
}
tableView.reloadData()
}
}extension StoreItemListTableViewController: UISearchBarDelegate {

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let searchTerm = searchBar.text ?? ""
search(searchTerm)
searchBar.resignFirstResponder()
}
}

說明

var filteredItems = [StoreItem]()

宣告儲存歌單的 filteredItems ,table view 改成顯示 filteredItems 的內容。

func search(_ searchTerm: String) {
if searchTerm.isEmpty {
filteredItems = items
} else {
filteredItems = items.filter {
$0.trackName.contains(searchTerm)
}
}
tableView.reloadData()
}

將 search 的功能寫在 function search(_:) 裡。當使用者沒有輸入 search 文字時,filteredItems 等於全部的歌單。當使用者輸入 search 文字時,filteredItems 等於包含 search 文字的歌單。

extension StoreItemListTableViewController: UISearchBarDelegate {

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let searchTerm = searchBar.text ?? ""
search(searchTerm)
searchBar.resignFirstResponder()
}
}

使用者按下 search 鍵時,將觸發 search bar delegate 的 function searchBarSearchButtonClicked(_:),我們在 searchBarSearchButtonClicked(_:) 裡呼叫剛剛定義的 function search(_:)進行搜尋,然後呼叫 searchBar 的 resignFirstResponder 收鍵盤。

執行 App,搜尋愛

範例的 GitHub 連結

串接網路 API 搜尋資料

剛剛的範例主要目的是介紹 search bar 的應用,因此我們簡化程式的難度,只做本機端的搜尋。如果想串接網路 API,利用 search bar 搜尋某個歌手的歌單,可進一步參考以下 Apple 官方的範例說明。

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com