안녕하세요 Skillist입니다~~
2022년이에요. 2022년엔 코로나 상황이 끝나면 좋겠어요.
모두 새해복 많이 받으세요.
크리스마스나 연말, 연초도 어김없이 공부 해봅시다.
오늘은 간단하게, 검색 히스토리를 보여주는 검색화면을 구현해볼거에요.
------------------------------------------------------------------------------------------------------------------
11라인 : 검색 결과를 보여줄 검색 결과 VC입니다. 나중에 설명할테니, 우선 신경쓰지마세요
12라인 : 검색 히스토리를 가지는 String 어레이에 대한 저장 프로퍼티입니다. 값이 변경될 때마다, didSet을 활용하여 특정 view를 show, hide합니다.
19라인 : userDefault에 사용되는 키 값입니다. 상수로 선언하였어요.
21라인 : 검색 히스토리가 없는 경우 보여주는 emptyView입니다. view에 label을 추가하고, snapKit을 활용하여 center와 여백을 지정했어요.
40라인 : searchBar를 추가하기 위해 searchController을 사용했어요.
41라인 : 검색 결과를 보여주는 searchResultVC를 지정해줬고, searchResultVC는 나중에 구현할 예정입니다~~ 우선 패스~
42라인 : 검색 결과에서 앱을 선택했을때, 상세화면으로 이동하기 위한 delegate입니다. 이는 추후에 설명할게요. 패스~
43라인 : searchBar의 placeholder 설정합니다.
44라인 : searchBar에 text입력 시, dim 추가 여부를 결정합니다. 아래의 그림을 봐보세요.
obscuresBackgroundDuringPresentation = false인 경우의 스크린샷
obscuresBackgroundDuringPresentation = true인 경우의 스크린샷
차이가 느껴지죠?????? 그쵸???? dim(불투명도)의 여부 입니다.
45라인 : searchController에 대한 delegate입니다.
46라인 : searchBar에 대한 delegate입니다.
50라인 : 검색 히스토리에 대한 collectionView입니다. 검색 결과를 보여주기 위한 SearchHistoryCollectionViewCell을 등록했습니다.
58라인 : viewDidLoad에서 기본 작업을 진행합니다.
65라인 : historyCollectionView와 emptyView를 추가하고, snapKit으로 제약사항을 설정합니다.
84라인 : userDefault를 활용하여, 저장된 history를 가져오고 history collectionView를 reload 합니다.
89라인 : viewWillAppear에서 네비게이션바를 설정합니다. 앱 검색화면과 앱 상세화면을 넘나들거에요. 따라서 viewDidLoad가 아니라, viewWillAppear에서 네비게이션바를 설정해줘야 합니다. 그렇지 않으면 화면 이동 시 largeTitle 설정이 풀려버립니다.
96라인 : largeTitle 설정입니다.
97라인 : title text 설정입니다.
98라인 : searchController 설정입니다.
99라인 : 앞서 말한 검색 결과 화면(SearchResultVC)을 원활하게 보여주기 위해 추가했었던 값이에요.
뷰 컨트롤러 또는 해당 하위 항목 중 하나가 뷰 컨트롤러를 표시할 때 이 뷰 컨트롤러의 뷰가 포함되는지 여부를 나타내는 부울 값입니다.
구글느님의 번역을 봐도 잘 모르겠죠?
일반 뷰는 해당 값이 false이며, 탭바, 네비게이션바는 true로 설정돼있습니다. true인 경우, 해당 view에서 다른 뷰를 보여주게끔 설정하는 값입니다. 추후 상세 설명으로 작성해볼게요.
결국, navigationBar를 사용했으며, 해당 값의 설정은 더이상 필요없으니 주석 처리했습니다.
103라인 : searchControllerDelegate인데, 따로 구현해주지 않았습니다.
109라인 : searchBar의 검색 버튼을 터치했을때 호출됩니다. 검색 시, 입력한 text를 가져오고, 검색 history를 업데이트 하고, api 검색을 수행합니다. 검색은 searchResultVC에서 진행하기 때문에, 나중에 설명할게요~
117라인 : 검색 history를 업데이트 합니다.
118라인 : 기존에 이미 검색한 text라면, 어레이에서 삭제합니다.
122라인 : 검색 히스토리가 10개 이상이라면 마지막 검색 내역을 삭제합니다.
126라인 : 어레이 0번째에 검색 text를 추가합니다.
128라인 : userDefault에 저장하고, 검색 히스토리 collectionView를 reload합니다.
134라인 : 상세화면으로 이동하기 위한 delegate입니다. 이는 searchResultVC에서 호출합니다. 우선 패스
141라인 : 검색 히스토리에 대한 설정입니다. cell은 "SearchHistoryCollectionViewCell"을 사용합니다.
156라인 : 검색 히스토리에 대한 width 설정과, 아이템 선택시 수행할 작업입니다.
------------------------------------------------------------------------------------------------------------------
이번엔 "SearchHistoryCollectionViewCell"입니다. 쓰윽~ 한번 볼게요.
12라인 : 히스토리 text를 보여줄 label입니다.
18라인 : divider를 구현해줬습니다.
24라인 : 초기화 시, 레이아웃을 구성합니다. 레이아웃을 추가하고, snapKit으로 제약사항을 구성합니다.
51라인 : 히스토리 text를 설정합니다.
------------------------------------------------------------------------------------------------------------------
아주아주아주 쉽네요. 다음 설명한 searchResultVC가 걱정인데, ㅋㅋㅋㅋ ㅠㅜ 암튼 한번 해보죠
연초에도 고생하셨습니다.
잘못되거나 부족한 내용 등, 피드백 감사합니다!
Skillist의 AppleAppStore 프로젝트
https://github.com/DeveloperSkillist/AppleAppStoreCloneCode
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 전체 코드 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
import UIKit
class SearchViewController: UIViewController {
var searchResultVC = SearchResultViewController()
var searchHistory: [String] = [] {
didSet {
let isEmpty = searchHistory.count == 0
historyCollectionView.isHidden = isEmpty
emptyView.isHidden = !isEmpty
}
}
let historyQuerys = "historyQuerys"
private lazy var emptyView: UIView = {
var emptyLabel = UILabel()
emptyLabel.textColor = .label
emptyLabel.text = "search_history_empty".localized
emptyLabel.font = .systemFont(ofSize: 20)
emptyLabel.numberOfLines = 0
var emptyView = UIView()
emptyView.backgroundColor = .systemBackground
emptyView.addSubview(emptyLabel)
emptyView.isHidden = true
emptyLabel.snp.makeConstraints {
$0.center.equalToSuperview().inset(20)
}
return emptyView
}()
private lazy var searchController: UISearchController = {
var searchController = UISearchController(searchResultsController: searchResultVC)
searchResultVC.detailAppDelegate = self
searchController.searchBar.placeholder = "search_placeholder".localized
searchController.obscuresBackgroundDuringPresentation = false
searchController.delegate = self
searchController.searchBar.delegate = self
return searchController
}()
private lazy var historyCollectionView: UICollectionView = {
var collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(SearchHistoryCollectionViewCell.self, forCellWithReuseIdentifier: "SearchHistoryCollectionViewCell")
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
setupHistoryQuerys()
}
private func setupLayout() {
view.backgroundColor = .systemBackground
[
historyCollectionView,
emptyView
].forEach {
view.addSubview($0)
}
historyCollectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
emptyView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
private func setupHistoryQuerys() {
searchHistory = UserDefaults.standard.stringArray(forKey: "historyQuerys") ?? []
historyCollectionView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupNavigationBar()
}
func setupNavigationBar() {
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "search_title".localized
navigationItem.searchController = searchController
// definesPresentationContext = true
}
}
extension SearchViewController: UISearchControllerDelegate {
}
extension SearchViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let searchText = searchBar.text else {
return
}
updateSearchHistory(query: searchText)
searchResultVC.searchItems(searchText: searchText)
}
func updateSearchHistory(query: String) {
if let row = searchHistory.firstIndex(of: query) {
searchHistory.remove(at: row)
}
if searchHistory.count >= 10 {
searchHistory.removeLast()
}
searchHistory.insert(query, at: 0)
UserDefaults.standard.set(searchHistory, forKey: historyQuerys)
historyCollectionView.reloadData()
}
}
extension SearchViewController: DetailAppVCDelegate {
func pushDetailVC(item: SearchItemResult) {
let detailVC = DetailViewController()
detailVC.item = item
navigationController?.pushViewController(detailVC, animated: true)
}
}
extension SearchViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return searchHistory.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchHistoryCollectionViewCell", for: indexPath) as? SearchHistoryCollectionViewCell else {
return UICollectionViewCell()
}
cell.setupItem(historyString: searchHistory[indexPath.row])
return cell
}
}
extension SearchViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 50)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let query = searchHistory[indexPath.row]
searchController.searchBar.text = query
searchController.searchBar.becomeFirstResponder()
searchBarSearchButtonClicked(searchController.searchBar)
}
}
import UIKit
class SearchHistoryCollectionViewCell: UICollectionViewCell {
private lazy var historyLabel: UILabel = {
var label = UILabel()
label.textColor = .label
return label
}()
private lazy var lineView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
[
historyLabel,
lineView
].forEach {
addSubview($0)
}
historyLabel.snp.makeConstraints {
$0.top.bottom.equalToSuperview()
$0.left.trailing.equalToSuperview().inset(20)
$0.height.equalTo(50)
}
lineView.snp.makeConstraints {
$0.leading.equalTo(historyLabel)
$0.trailing.bottom.equalToSuperview()
$0.height.equalTo(1)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupItem(historyString: String) {
historyLabel.text = historyString
}
}
'iOS 개발 > Apple App Store 클론 코딩' 카테고리의 다른 글
AppleAppStore - 11. DetailViewController 화면 구현 (0) | 2022.01.03 |
---|---|
AppleAppStore - 10. Search Result 화면 구현 (0) | 2022.01.03 |
AppleAppStore - 8. 결과물 중간 점검 (0) | 2021.12.24 |
AppleAppStore - 7. App 화면 구현 (0) | 2021.12.17 |
AppleAppStore - 6. App 화면의 Cell 구현 (0) | 2021.12.17 |