구현 내용
- Search 탭에 서치바 기능을 추가하고 검색 결과를 테이블 + 컬렉션뷰와 혼합하여 보여준다.
구현 순서
- UISearchController 타입의 searchController 라는 변수를 선언한다.
- searchController와 SearchResultsViewController 사이에는 searchController.searchResultsUpdater = self 가 있다.
- 해당 변수에 결과를 받아서 보여주는 SearchResultsViewController를 생성한다.
- SearchResultsViewController 내에는 결과 값을 어떤 식으로 보여줄 지 정한다.
구현 코드
- TMDB 사이트 내 Search -> Multi 부분에 들어가서 API 리퀘스트 함수를 확인한다.
https://developer.themoviedb.org/reference/search-multi
- APICaller.swift 내에 아래 함수를 구현한다.
- keyWord 라는 파라미터를 통해 얻은 입력값을 쿼리에 넣어 확인한다.
func getDiscoverMulti(with keyWord: String, completion: @escaping (Result<[Title], Error>) -> Void) {
guard let keyWord = keyWord.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else { return }
let url = URL(string: "https://api.themoviedb.org/3/search/multi")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
let queryItems: [URLQueryItem] = [
URLQueryItem(name: "query", value: keyWord),
URLQueryItem(name: "include_adult", value: "false"),
URLQueryItem(name: "language", value: "en-US"),
URLQueryItem(name: "page", value: "1"),
]
components.queryItems = components.queryItems.map { $0 + queryItems } ?? queryItems
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.timeoutInterval = 10
request.allHTTPHeaderFields = [
"accept": "application/json",
"Authorization": "Bearer \(Constants.API_KEY)"
]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Error occurred: \(error.localizedDescription)")
completion(.failure(APIError.failedToGetData))
return
}
guard let data = data else {
print("No data received")
completion(.failure(APIError.failedToGetData))
return
}
do {
// Print JSON data to debug
// if let json = try? JSONSerialization.jsonObject(with: data, options: []) {
// print("JSON Response: \(json)")
// }
let results = try JSONDecoder().decode(TrendingTitleResponse.self, from: data)
completion(.success(results.results))
} catch {
print("JSON Decoding error: \(error.localizedDescription)")
completion(.failure(APIError.failedToGetData))
}
}
task.resume()
}
- SearchViewController.swift 내에 아래 코드를 구현한다.
import UIKit
class SearchViewController: UIViewController {
// MARK: UI Components
private let searchController: UISearchController = {
let controller = UISearchController(searchResultsController: SearchResultsViewController())
controller.searchBar.placeholder = "Search for a Movie or Tv or Person"
controller.searchBar.searchBarStyle = .minimal
return controller
}()
// MARK: Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Searh"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
navigationItem.searchController = searchController
navigationController?.navigationBar.tintColor = .label
searchControllerDelegate()
}
// MARK: Functions
private func searchControllerDelegate() {
searchController.searchResultsUpdater = self
}
}
// MARK: Extensions
extension SearchViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
guard let keyWord = searchBar.text,
!keyWord.trimmingCharacters(in: .whitespaces).isEmpty,
keyWord.trimmingCharacters(in: .whitespaces).count >= 2,
let resultsController = searchController.searchResultsController as? SearchResultsViewController else { return }
APICaller.shared.getDiscoverMulti(with: keyWord) { result in
DispatchQueue.main.async {
switch result {
case .success(let titles):
DispatchQueue.main.async {
resultsController.titles = titles
resultsController.searchResultsCollectionView.reloadData()
}
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
}
- UISearchController의 searchResultsUpdater 속성에 현재 뷰 컨트롤러(self)를 할당한 것으로, 이를 통해 UISearchController는 검색 텍스트가 변경될 때마다 현재 뷰 컨트롤러의 updateSearchResults(for:) 메서드를 호출한다.
- updateSearchResults(for:) 메서드에서는 검색어를 사용하여 데이터를 가져오고, 검색 결과를 업데이트한다.
searchController.searchResultsUpdater = self
- SearchResultsViewController.swift 파일 내 코드 구현한다.
import UIKit
class SearchResultsViewController: UIViewController {
// MARK: Variables
var titles: [Title] = []
// MARK: UI Components
let searchResultsCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let width = UIScreen.main.bounds.width / 3 - 2
layout.itemSize = CGSize(width: width, height: 200)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 2
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(TitleCollectionViewCell.self, forCellWithReuseIdentifier: TitleCollectionViewCell.identifier)
return collectionView
}()
// MARK: Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.addSubview(searchResultsCollectionView)
searchResultsCollectionViewDelegate()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
searchResultsCollectionView.frame = view.bounds
}
// MARK: Functions
private func searchResultsCollectionViewDelegate() {
searchResultsCollectionView.delegate = self
searchResultsCollectionView.dataSource = self
}
}
// MARK: Extensions
extension SearchResultsViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return titles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TitleCollectionViewCell.identifier, for: indexPath) as? TitleCollectionViewCell else { return UICollectionViewCell() }
let title = titles[indexPath.row]
cell.configure(with: title.poster_path ?? "")
return cell
}
}
'iOS > UIKIT' 카테고리의 다른 글
webkit을 통해 유튜브 영상의 주소로 영상 틀어보기 (0) | 2024.07.08 |
---|---|
유튜브에서 제공하는 API를 통해 영화 트레일러 가져오기 (0) | 2024.07.08 |
API를 통해 가져온 데이터를 테이블 내에 컬렉션뷰 이미지로 넣기 (0) | 2024.07.05 |
공공 API를 통해 데이터 불러오기 (TMDB) (0) | 2024.07.04 |
스크롤해서 화면을 내릴 때, 네비게이션 타이틀이 같이 올라가게 하기 (1) | 2024.07.03 |