-1

Tìm hiểu về UISearchController

I. Giới thiệu

Trong quá trình sử dụng phần mềm, nhiều trường hợp dữ liệu của người dùng có thể trở nên ngày càng lớn. Trong những trường hợp này, người dùng sẽ mất nhiều thời gian hơn để tìm kiếm dữ liệu họ muốn. Đôi khi, dữ liệu quá lớn khiến người dùng phải tốn rất nhiều công sức để tìm kiếm, khiến việc trải nghiệm ứng dụng của người dùng trở nên tệ hơn. Vì vậy, việc tích hợp các biện pháp tìm kiếm dữ liệu cho người dùng là việc các lập trình viên nên đưa vào trong ứng dụng của mình.

Trong bài viết này, tôi xin giới thiệu đến các bạn về UISearchController, một class trong iOS để lập trình viên tích hợp công cụ tìm kiếm vào trong ứng dụng của mình. Nếu các bạn đã lập trình iOS từ các version iOS trước đây, các bạn có thể đã làm quen với class UISearchDisplayController. Tuy nhiên, từ phiên bản iOS 8, class này đã bị loại bỏ, và thay thế bằng class UISearchController. So sánh với class UISearchDisplayController cũ, mặc dù class UISearchController không hỗ trợ interface builders(không sử dụng được giao diện mà phải tự khởi tạo code), nhưng class UISearchController đơn giản và dễ sử dụng hơn rất nhiều.

Dưới đây, tôi sẽ giới thiệu một ứng dụng demo đơn giản sử dụng UISearchController để tìm kiếm trong ứng dụng của mình. So, let's start!

II. Demo app

1. Tạo project

Do mục đích của bài viết này là tìm hiểu về UISearchController, nên tôi sẽ không hướng dẫn cụ thể việc tạo project. Các bạn download started project tại đây

Sau khi download started project về, các bạn giải nén và mở project bằng xCode. Các bạn có thể thấy, code tôi đã tạo trong project này khá đơn giản, chúng ta có một UITableViewController để hiển thị danh sách tên các loại điện thoại, và một UIViewController để hiển thị chi tiết (trong demo app này, chi tiết cũng chỉ có tên điện thoại 😄)

Bây giờ, nhiệm vụ của chúng ta là tạo ra chức năng search trong màn UITableViewController, để người dùng có thể tìm kiếm điện thoại trong danh sách ở table view.

2. Tích hợp UISearchController vào project

Như đã nói bên trên, UISearchController không hỗ trợ trong interface builders, vì thế chúng ta phải tạo bằng tay. Đầu tiên, chúng ta thêm property vào đầu class TableViewController như sau:

let searchController = UISearchController(searchResultsController: nil)

Tiếp theo, chúng ta thêm đoạn code sau vào cuối hàm viewDidLoad()

// 1
searchController.searchResultsUpdater = self
// 2
searchController.dimsBackgroundDuringPresentation = false
// 3
definesPresentationContext = true
// 4
tableView.tableHeaderView = searchController.searchBar
// 5
searchController.searchBar.barTintColor = UIColor(red: 52.0/255.0, green: 200.0/255.0, blue: 114.0/255.0, alpha: 1.0)
searchController.searchBar.tintColor = UIColor.whiteColor()

Trong đoạn code trên:

  1. chúng ta gán self cho protocol searchResultsUpdater của instance searchController. Các bạn hãy tưởng tượng việc gán này cũng giống như việc gán delegate của một UITableView. Bằng việc gán protocol searchResultsUpdater, chúng ta có thể xác định mỗi khi ô text trong search bar được thay đổi.
  2. dimsBackgroundDuringPresentation được set là false để trong quá trình search, tableView của chúng ta không bị che khuất.
  3. Chúng ta cần set definesPresentationContext bằng true để search bar của chúng ta không bị lỗi layout khi sử dụng (đây là property của UIViewController, được giới thiệu từ iOS 8, các bạn có thể google để tìm hiểu thêm về property này).
  4. Chúng ta gán searchBar view của instance searchController cho header view của tableView. Bằng việc gán này, search bar của chúng ta có thể hiển thị trên màn hình.
  5. Set barTintColor và tintColor của search bar cho đẹp hơn, hợp với màu của app

Sau khi Chúng ta thêm đoạn code trên, các bạn có thể thấy sảy ra lỗi ngay trong đoạn code:

// 1
searchController.searchResultsUpdater = self

Ở đây, chúng ta gán protocol cho self, nhưng chúng ta chưa implement protocol này trong TableViewController class. Để implement protocol này, chúng ta thêm extension vào cuối class TableViewController như sau:

extension TableViewController: UISearchResultsUpdating {
    func updateSearchResultsForSearchController(searchController: UISearchController) {
        // To do: Implement search method
    }
}

Build và chạy thử project, nếu không có lỗi gì sảy ra, ứng dụng của chúng ta sẽ hiển thị như sau:

Screen Shot 2016-04-20 at 5.20.20 PM.png

Search bar của chúng ta đã hiển thị, chúng ta đồng thời có thể gõ chữ vào ô text của search bar. Tuy nhiên, khi chúng ta gõ vào ô text, không có gì sảy ra cả. Bây giờ, chúng ta sẽ implement code để thực hiện việc tìm hiếm.

Bây giờ, quay trở lại với extension của TableViewController bên trên mà chúng ta đã implement protocol UISearchResultsUpdating. Hàm updateSearchResultsForSearchController(_😃 sẽ được gọi mỗi khi ô text của search bar thay đổi. Đầu tiên, chúng ta thêm property sau vào class TableViewController:

var filteredPhones = [Phone]()

Tiếp theo, chúng ta thêm hàm sau để thực hiện việc filter:

func filterContentForSearchText(searchText: String, scope: String = "All") {
    filteredPhones = phones.filter { phone in
        return phone.name.lowercaseString.containsString(searchText.lowercaseString)
    }

    tableView.reloadData()
}

Implement hàm updateSearchResultsForSearchController(_😃 như sau:

func updateSearchResultsForSearchController(searchController: UISearchController) {
    filterContentForSearchText(searchController.searchBar.text!)
}

Sửa lại các hàm của UITableViewDataSource để hiển thị cả kết quả tìm kiếm:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if searchController.active && searchController.searchBar.text != "" {
        return filteredPhones.count
    }
    return phones.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath)

    let phone: Phone
    if searchController.active && searchController.searchBar.text != "" {
        phone = filteredPhones[indexPath.row]
    } else {
        phone = phones[indexPath.row]
    }

    cell.textLabel?.text = phone.name
    return cell
}

Sửa lại hàm prepareForSegue(_:, _😃 để truyền đúng data sang UIDetailViewController:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "ToDetailVCSegue" {
        if let indexPath = tableView.indexPathForSelectedRow {
            let detailViewVC = segue.destinationViewController as! DetailViewController
            let phone: Phone
            if searchController.active && searchController.searchBar.text != "" {
                phone = filteredPhones[indexPath.row]
            } else {
                phone = phones[indexPath.row]
            }

            detailViewVC.phone = phone
        }
    }
}

Build và chạy thử project, các bạn có thể thấy việc search của chúng ta đã được thực hiện.

3. Tìm kiếm nâng cao

Bên Trên, chúng ta đã thực hiện việc tìm kiếm điện thoại theo tên. Bây giờ, chúng ta sẽ nâng cao hơn một chút việc tìm kiếm: chúng ta tìm kiếm điện thoại theo tên, trong category(iPhone, android hoặc cả 2)

Để làm được việc này, đầu tiên chúng ta tạo thêm extension cho class TableViewController để implement hàm searchBar(_:, selectedScopeButtonIndexDidChange _😃 của UISearchBarDelegate như sau:

extension TableViewController: UISearchBarDelegate {
    func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
        filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
    }
}

Tiếp theo, chúng ta cần sửa lại hàm filterContentForSearchText(_:, _😃 một chút để có thể filter kết quả, như sau:

func filterContentForSearchText(searchText: String, scope: String = "All") {
    filteredPhones = phones.filter { phone in
        let categoryMatch = (scope == "All") || (phone.category == scope)
        return categoryMatch && phone.name.lowercaseString.containsString(searchText.lowercaseString)
    }

    tableView.reloadData()
}

Tiếp theo, chúng ta sửa hàm updateSearchResultsForSearchController(_😃 để việc search được chính xác:

func updateSearchResultsForSearchController(searchController: UISearchController) {
    let searchBar = searchController.searchBar
    let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
    filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}

Cuối cùng, chúng ta thêm đoạn code sau và cuối hàm viewDidLoad() để thêm scope cho searchBar:

searchController.searchBar.scopeButtonTitles = ["All", "iPhone", "android"]
searchController.searchBar.delegate = self

Build và chạy thử project, các bạn có thể thấy chúng ta có thể search theo từng category của điện thoại. Finally, we done (yeah3)

Screen Shot 2016-04-20 at 6.15.45 PM.png

III. Tổng kết

Trong bài viết này, tôi đã giới thiệu đến các bạn UISearchController, một class không thể thiếu khi chúng ta làm việc với tìm kiếm dữ liệu trong iOS. Các bạn có thể download final demo sourcecode tại đây. Hi vọng bài viết này có thể có ích cho các bạn.

Cuối cùng, xin cảm ơn các bạn đã theo dõi bài viết này!!!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí