기본적으로 검색 기능을 구현하기 위해서는 UISearchBar라는 View Componant를 사용해야 한다. 그리고 검색 결과를 나타내기 위해서UICollectionView를 사용하고 검색창에 입력한 결과를 콜렉션뷰에 반영시켜줘야 한다. 나머지 틀은 MovieListView 구성방식이랑 똑같기 때문에 수월하게 진행되었다. 가장 어려운 부분은 뷰간의 데이터 연동이 될 것 같다.
앱을 개발할 때 가장 다루기 어려운 건, 오늘 처음 써보는 게 아닐까 싶다. 사실 콜렉션뷰도 전 프로젝트에서 다뤘었고 많이 데여봤기 때문에 이젠 익숙해졌다. 새로운 뷰를 익힐때마다 어색하고 접근하기 힘들다. 우선 검색창으로 구현해야할 기능들이 무엇이 있는지 생각해보았다.
검색해서 나온 데이터를 담을 변수를 설정해준다. 타이핑을 하면 그 타이핑한 문자열을 키워드에 넣어 데이터를 호출하는 것이다. 그럼 첫째, 타이핑 관련 메서드와 둘째, 문자열을 URL 속에 넣어 데이터를 호출하는 메서드 두개가 필요하다.
//VC에서 구현된 코드
//타이핑 시, 문자열을 getSearchedMovies로 키워드를 받는 메서드
lazy var filteredMovies: [MovieData.Movie] = []
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
MovieData.shared.getSearchedMovies(userInput: searchText, completion: { [weak self] in
self?.filteredMovies = MovieData.shared.searchedMovies
DispatchQueue.main.async {
self?.searchCollectionView.reloadData()
}
})
}
//엔터를 누르면 키보드가 내려가는 메서드
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
}
//Data.swift 에서 구현된 모델
public func getSearchedMovies(userInput:String, completion: @escaping () -> Void) {
//파일 형식과 API키를 정의한 헤더부
let headers = [
"accept": "application/json",
"Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmM2Q1ZTNlYWY2MGViNWY3Njg5YjhjMjIxNTYyMzlhNCIsInN1YiI6IjY1YTUwZDgwMWZiOTRmMDBjMDc0YTFhNyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.4pi9VmylhkY94DoJk6s4Ol7txHjXcyonKy3PeI9ZdS8"
]
//요청을 보낼 URL
let url = URL(string: "https://api.themoviedb.org/3/search/movie?query=\(userInput)&include_adult=false&language=ko-KR&page=1")!
//URL에 요청
var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
//HTTP 메서드 설정
request.httpMethod = "GET"
//요청에 헤더부 추가
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) in
//에러 처리
if let error = error {
print("Error fetching data: \(error)")
return
}
//데이터 옵셔널 바인딩
guard let data = data else {
print("No data received")
return
}
//데이터 처리
do {
let decoder = JSONDecoder()
let result = try decoder.decode(Result.self, from: data)
self.searchedMovies = result.results
completion()
print ("검색 영화 세팅 완료")
} catch {
print("Error parsing JSON: \(error)")
}
})
dataTask.resume()
}
(userInput)이 검색바에서 타이핑을 치면 나오는 값이 나타내는 부분이고 API에서 검색기능을 직접 제공하기 때문에 각각의 문자열을 비교해서 데이터를 가져오는 메서드는 구현할 필요가 없었다.
그 이후로 검색바 외곽선을 지우는 작업 & placeholder 설정 & delegate 설정을 해주면 검색바 작업은 끝이난다.
override func viewDidLoad() {
super.viewDidLoad()
searchBar.searchBarStyle = .minimal
searchBar.placeholder = "Enter Movie Title"
searchBar.delegate = self
}
변수랑 동일한 데이터만 이미지로 표현해주기 위해 cellForItemAt에 fiteredMovies가 순서대로 진행될수 있도록 indexPath.row로 설정해준다
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(filteredMovies.count)
return filteredMovies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let searchCell = collectionView.dequeueReusableCell(withReuseIdentifier: "searchCell", for: indexPath) as! SearchCell
searchCell.setCell(filteredMovies[indexPath.row])
return searchCell
}
cell에 넣을 이미지를 poster_Path로 이어줘야하기 때문에 String값이 URL에 들어가도록 설정해주고 각 셀의 크기들을 MovieListView에서 설정했던 것처럼 세팅해준다.
cell을 눌렀을 때 화면전환이 일어나고 MovieDetailView로 넘어가게 된다. 단순히 화면전환만 진행되는 것이 아닌 Data도 같이 들고 가야한다. 여러가지 방법으로 데이터를 전달할 수 있지만, indexPath를 이용하여 간단하게 데이터를 전달해주기로 했다.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//선택된 cell에 해당되는 데이터 가져오기
let selectedMovie = filteredMovies[indexPath.row]
let MovieDetailStoryboard = UIStoryboard(name: "MovieDetailStoryboard", bundle: nil)
guard let MovieDetailViewController = MovieDetailStoryboard.instantiateViewController(identifier: "MovieDetail") as? MovieDetailViewController else {
return
}
//MovieDetailViewController의 movieData 프로퍼티에 인스턴스
MovieDetailViewController.movieData = selectedMovie
MovieDetailViewController.modalPresentationStyle = .automatic
self.present(MovieDetailViewController, animated: true)
MovieDetailViewController에 인스턴스로 담을 프로퍼티를 만들어주고 그 할당받을 코드를 didSelectItemAt에 설정해주면 데이터가 전달된다. 생각보다 엄청 간단하게 구현할 수 있었다.