Computer >> Máy Tính >  >> Điện thoại thông minh >> iPhone

Cách sử dụng UISearchController trong ứng dụng iOS

Chào mọi người! Trong bài viết này, chúng ta sẽ tìm hiểu cách sử dụng UISearchController trong Ứng dụng iOS.

Chúng ta sẽ xây dựng cái gì?

Chúng tôi sẽ xây dựng một ứng dụng tìm kiếm phim sử dụng TMDB API để tìm nạp thông tin phim và hiển thị thông tin đó bằng UICollectionView dựa trên truy vấn tìm kiếm của người dùng.

Thiết lập dự án

Mở Xcode và tạo một dự án Ứng dụng iOS trống mới - đảm bảo rằng bạn chọn UIKit chứ không phải SwiftUI.

Trong ứng dụng này, chúng tôi sẽ sử dụng MVC Pattern để tổ chức dự án bằng cách tạo các nhóm và tệp Swift sau:

Cách sử dụng UISearchController trong ứng dụng iOS

Bây giờ hãy đóng dự án Xcode của bạn. Mở thiết bị đầu cuối và chuyển đến thư mục dự án của bạn. Ở đây, chúng tôi cần thêm SD WebImage Cocoa Pods để tải xuống không đồng bộ và lưu vào bộ nhớ cache hình ảnh áp phích phim.

Nhập lệnh sau vào terminal:

pod init

Bây giờ khi bạn liệt kê nội dung của thư mục, bạn có thể thấy rằng có một Podfile mới. Mở tệp bằng bất kỳ trình soạn thảo văn bản nào (ở đây tôi đã sử dụng Vim). Chỉnh sửa Podfile của bạn để nó trông giống như hình dưới đây. Lưu và đóng Podfile.

Cách sử dụng UISearchController trong ứng dụng iOS

Bây giờ chúng ta đã chỉ định SD WebImage, chúng ta có thể cài đặt các phụ thuộc bằng cách chạy lệnh dưới đây:

pod install

Như bạn có thể thấy, chúng tôi đã thêm thành công nhóm SD WebImage trong dự án iOS của mình. Bây giờ hãy chạy lệnh dưới đây để mở dự án của chúng tôi bằng Xcode.

open PROJECT_NAME.xcworkspace

Sau khi mở Xcode, hãy đảm bảo rằng bạn xây dựng dự án của mình bằng cách nhấn Command + B.

Cách thiết kế giao diện người dùng bằng UIKit và giao diện người dùng có lập trình

Ứng dụng của chúng tôi cần ba thanh điều hướng UIElements để giữ thanh tìm kiếm, UISearchBarController để tìm kiếm thực tế và một UICollectionView để hiển thị kết quả tìm kiếm.

Mở tệp Scenedelegate.swift của bạn và thêm mã sau vào bên trong nó, mã này sẽ kết nối với phương thức phiên:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)       window?.rootViewController=UINavigationController(rootViewController:HomeVC())
        window?.makeKeyAndVisible()
    }

Vì chúng ta đang sử dụng giao diện người dùng có lập trình, trước tiên chúng ta cần đề cập đến bộ điều khiển Root View - tức là màn hình đầu tiên sẽ được hiển thị khi người dùng khởi chạy ứng dụng.

Ở đây trong ứng dụng này, chúng tôi chỉ sử dụng một View Controller, vì vậy chúng tôi bọc nó bên trong một UINavigationController. Điều này cung cấp một thanh điều hướng nơi chúng tôi có thể đặt UISearchController của mình.

Mở tệp HomeVC.swift và thêm các thuộc tính sau:

 private var SearchBar: UISearchController = {
        let sb = UISearchController()
        sb.searchBar.placeholder = "Enter the movie name"
        sb.searchBar.searchBarStyle = .minimal
        return sb
    }()
    
    private var MovieCollectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.itemSize = CGSize(width: UIScreen.main.bounds.width/3 - 10, height: 200)
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.register(MovieCell.self, forCellWithReuseIdentifier: MovieCell.ID)
        return cv
    }()

Đầu tiên, chúng tôi tạo UISearchController và định cấu hình các thuộc tính của nó như văn bản và kiểu trình giữ chỗ.

Sau đó, chúng tôi tạo một UICollectionView và chỉ định loại bố cục mà chế độ xem bộ sưu tập của chúng tôi nên sử dụng. Trong trường hợp này, đó là UICollectionViewFlowLayout và các thuộc tính khác như hướng cuộn, kích thước mục và chỉ định một lớp ô CollectionView tùy chỉnh mà chúng tôi sẽ tạo sau này trong dự án của mình.

Bên trong lớp HomeVC, hãy tạo một hàm mới và thêm mã sau để định cấu hình các ràng buộc bố cục tự động theo lập trình cho UICollectionView của chúng tôi:

    //MARK: - HELPERS
    func configureUI(){
        MovieCollectionView.translatesAutoresizingMaskIntoConstraints = false
        MovieCollectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        MovieCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        MovieCollectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        MovieCollectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    }

Đầu tiên, chúng tôi nói rằng chúng tôi không cần phải chuyển đổi mặt nạ tự động thay đổi kích thước thành các ràng buộc. Sau đó, chúng tôi ghim chế độ xem bộ sưu tập của mình vào tất cả bốn mặt của Bộ điều khiển Chế độ xem.

Bên trong viewDidLoad() phương pháp thêm các dòng mã sau:

  override func viewDidLoad() {
        super.viewDidLoad()
        
        navigationItem.title  = "Movie Search"
        view.backgroundColor = .systemBackground
        SearchBar.searchResultsUpdater = self
        navigationItem.searchController = SearchBar
        view.addSubview(MovieCollectionView)
        MovieCollectionView.delegate = self
        MovieCollectionView.dataSource = self
        configureUI()
    }

Ở đây, đầu tiên chúng ta chỉ định tiêu đề cho ViewController của chúng ta, tiếp theo là màu nền là màu systemBackground. Nếu thiết bị ở chế độ ánh sáng, nó sẽ hiển thị nền trắng. Nếu nó ở chế độ tối, thì nó sẽ hiển thị nền tối.

Sau đó, chúng tôi đặt bộ điều khiển chế độ xem hiện tại làm trình cập nhật kết quả tìm kiếm, sau đó thêm SearchController của chúng tôi vào thanh điều hướng, thêm UICollectionView vào ViewController và thiết lập ủy quyền và nguồn dữ liệu. Cuối cùng, chúng tôi ghim UICollectionView bằng cách sử dụng bố cục tự động.

Tạo tiện ích mở rộng cho HomeVC và triển khai giao thức UISearchResultsUpdating và cập nhật phương thức sơ khai của nóSearchResults.

extension HomeVC: UISearchResultsUpdating{
    
    func updateSearchResults(for searchController: UISearchController) {
        guard let query = searchController.searchBar.text else{return}
      
        }
        
    }
    
    
}

updateSearchResults() phương thức sẽ được gọi bất cứ khi nào văn bản được nhập vào thanh tìm kiếm thay đổi hoặc khi người dùng nhấn vào nút tìm kiếm trên bàn phím của họ.

Tiếp theo, chúng ta cần tạo ô UICollectionView tùy chỉnh đó. Bên trong tệp MovieCell.swift, hãy thêm mã sau:

import Foundation
import UIKit
import SDWebImage

class MovieCell: UICollectionViewCell{
    
    static let ID = "MovieCell"
    private var MoviePosterImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
      //  imageView.image = UIImage(systemName: "house")
        return imageView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(MoviePosterImageView)
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

extension MovieCell{
    func configureUI(){
        MoviePosterImageView.translatesAutoresizingMaskIntoConstraints = false
        MoviePosterImageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        MoviePosterImageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        MoviePosterImageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        MoviePosterImageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    }
    func updateCell(posterURL: String?){
        if let posterURL = posterURL {
            guard let CompleteURL = URL(string: "https://image.tmdb.org/t/p/w500/\(posterURL)") else {return}
            self.MoviePosterImageView.sd_setImage(with: CompleteURL)
        }
       
    }
}

Ở đây, chúng tôi tạo ô dạng xem bộ sưu tập tùy chỉnh của mình bằng cách phân lớp phụ lớp UICollectionView và triển khai init() chức năng.

Chúng tôi tạo một UIImageView để hiển thị hình ảnh áp phích phim và thiết lập các ràng buộc bố cục tự động cho nó. Sau đó, chúng tôi tạo một chức năng do người dùng xác định, lấy tham số chuỗi URL áp phích phim và tải xuống không đồng bộ mà không ảnh hưởng đến Chuỗi giao diện người dùng / Chuỗi chính. Nó thực hiện điều này bằng cách sử dụng SD WebImage CocoaPod mà chúng tôi đã thêm trước đó.

Cách thiết lập API của chúng tôi

Trước khi tiếp tục, bạn sẽ cần lấy khóa API của mình cho TMDB API bằng cách tạo một tài khoản (hoàn toàn miễn phí). Chúng tôi sẽ sử dụng điểm cuối Tìm kiếm phim của API lấy Khóa API và tên phim làm tham số.

https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&query=batman

Bạn có thể kiểm tra phản hồi API bằng cách chạy nó trong Postman.

Cách sử dụng UISearchController trong ứng dụng iOS

Cách tạo mô hình cho phản hồi API

Bây giờ chúng tôi nhận được phản hồi JSON từ API. Chúng ta cần giải mã chúng thành Swift mà chúng ta có thể thực hiện bằng cách tạo cấu trúc mô hình triển khai giao thức Codable.

CHÚNG TÔI có thể tạo cấu trúc mô hình cho phản hồi JSON của chúng tôi một cách dễ dàng bằng cách sử dụng các trang web JSON cho Swift. Đây là mã mô hình cho phản hồi API - bạn có thể chỉ cần sao chép và dán nó vào bên trong tệp Model.swift:

import Foundation

struct TrendingTitleResponse: Codable {
    let results: [Title]
}

struct Title: Codable {
    let id: Int
    let media_type: String?
    let original_name: String?
    let original_title: String?
    let poster_path: String?
    let overview: String?
    let vote_count: Int
    let release_date: String?
    let vote_average: Double
}

struct YoutubeSearchResponse: Codable {
    let items: [VideoElement]
}


struct VideoElement: Codable {
    let id: IdVideoElement
}


struct IdVideoElement: Codable {
    let kind: String
    let videoId: String
}

Cách Thực hiện Yêu cầu HTTP bằng Swift

Bây giờ chúng ta cần viết một số mã Swift để thực hiện các yêu cầu HTTP GET trả về phản hồi JSON của API.

Swift cung cấp một lớp URLSession giúp viết mã mạng dễ dàng hơn mà không cần bất kỳ thư viện bên thứ ba nào như AFNetworking, AlamoFire, v.v.

Mở APIService.swift và thêm mã sau:

import Foundation

class APIService{
    static var shared = APIService()
    let session = URLSession(configuration: .default)
    
    func getMovies(for Query: String,completion:@escaping([Title]?,Error?)->Void){
        guard let FormatedQuery = Query.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)  else{return}
        
        guard let  SEARCH_URL = URL(string: "https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&query=\(FormatedQuery)") else {print("INVALID")
            return}
        
        
        
        let task = session.dataTask(with: SEARCH_URL) { data, response, error in
            if let error = error {
                print(error.localizedDescription)
                completion(nil,error)
            }
            if let data = data {
                do{
                    let decodedData = try JSONDecoder().decode(TrendingTitleResponse.self, from: data)
                 //   print(decodedData)
                    completion(decodedData.results,nil)
                }
                catch{
                    print(error)
                }
            }
        }
        task.resume()
    }
}

Ở đây, chúng tôi đã tạo một lớp có tên là Dịch vụ API với mẫu singleton, vì vậy chúng tôi cần một thể hiện cho lớp này như một thành viên tĩnh của lớp. Sau đó, chúng tôi tạo một phiên cho tác vụ mạng của mình với cấu hình mặc định, tiếp theo là phương thức do người dùng xác định getMovies ().

Sau đó, chúng tôi tạo tác vụ mạng của mình - trong trường hợp này, chúng tôi cần thực hiện các yêu cầu HTTP GET có thể được thực hiện bằng cách sử dụng dataTask() phương thức của lớp URLSession. Nó lấy URL làm tham số và cung cấp một trình xử lý hoàn thành chứa dữ liệu trả về từ API, dữ liệu lỗi nếu có bất kỳ lỗi nào xảy ra và phản hồi có thông tin Phản hồi HTTP như mã trạng thái và thông báo tương ứng của chúng.

Nếu có bất kỳ lỗi nào, thì chúng tôi thoát khỏi chức năng này với dữ liệu lỗi. Nếu không, thì chúng tôi giải mã dữ liệu JSON của mình dựa trên Mô hình Swift của chúng tôi và thoát khỏi chức năng này với dữ liệu đã giải mã.

Cách Hiển thị Kết quả Tìm kiếm trên UICollectionView

Trong HomeVC.swift, hãy tạo một thuộc tính riêng là một mảng các đối tượng Title. Những thứ này sẽ lưu giữ thông tin của từng bộ phim do API trả về.

private var Movies = [Title]()

Trong HomeVC.swift, hãy tạo một phần mở rộng cho lớp HomeVC và triển khai các giao thức UIColletionViewDelegate và UICollectionViewDatasource. Sau đó, triển khai numberOfItemsInSection (bằng với số lượng phim được API trả về) và cellForItemAt (thực sự điền vào ô với các phản hồi API, như tải xuống và đặt hình ảnh áp phích).

 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return Movies.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MovieCell.ID, for: indexPath) as? MovieCell{
           // cell.backgroundColor = .systemBackground
            
            cell.updateCell(posterURL: Movies[indexPath.row].poster_path)
            return cell
        }
        return UICollectionViewCell()
        
    }

Cuối cùng, chúng ta cần thực hiện lệnh gọi API thực tế mà chúng ta thực hiện bên trong updateSearchResults() phương pháp ủy quyền mà chúng tôi đã triển khai trước đây. Bên trong phương thức đó, hãy thêm mã sau:

    func updateSearchResults(for searchController: UISearchController) {
        guard let query = searchController.searchBar.text else{return}
        APIService.shared.getMovies(for:query.trimmingCharacters(in: .whitespaces)) { titles, error in
            if let titles = titles {
                self.Movies = titles
                DispatchQueue.main.async {
                    self.MovieCollectionView.reloadData()
                }
       
            }
        }
        
    }

Tại đây, bất cứ khi nào người dùng nhập vào thanh tìm kiếm hoặc nhấn nút tìm kiếm, chúng tôi sẽ thực hiện yêu cầu HTTP GET để tìm nạp phim (dựa trên tên đã nhập trong thanh tìm kiếm). Sau đó, chúng tôi tải lại Chế độ xem bộ sưu tập để cập nhật các ô chế độ xem bộ sưu tập với áp phích phim.

Lưu ý rằng chúng ta cần thực hiện việc này trong Chủ đề chính / Chủ đề giao diện người dùng, vì theo mặc định, iOS sẽ tự động thực hiện yêu cầu HTTP trong chuỗi nền. Điều này có nghĩa là chúng ta cần sử dụng UI / Main Thread để cập nhật các phần tử UI của mình.

Bây giờ, hãy chạy ứng dụng của bạn trong trình mô phỏng để xem kết quả:

Cách sử dụng UISearchController trong ứng dụng iOS

Xin chúc mừng! Bạn đã học cách sử dụng UISearchController trong ứng dụng iOS.