[UIKit] InstagramClone: SearchView

Junyoung Park·2022년 11월 9일
0

UIKit

목록 보기
93/142
post-thumbnail
post-custom-banner

Build Instagram App: Part 16 (Swift 5) - 2020 - Xcode 11 - iOS Development

InstagramClone: SearchView

구현 목표

  • 검색 뷰 구현

구현 태스크

  • 피드 셀 UI 및 바인딩
  • 포스트 뷰 연결 바인딩

핵심 코드

private func setSearchBar() {
        searchBar.delegate = self
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(cancelButtonDidTap))
        tapGesture.numberOfTapsRequired = 1
        tapGesture.numberOfTouchesRequired = 1
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }
  • 서치 바 검색 기능 추가
  • didSelectItemAt 컬렉션 뷰 함수를 사용하기 위해 뷰에 추가된 탭 제스처의 cancelsTouchesInView 프로퍼티 false로 주기
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard
            let text = searchBar.text,
            !text.replacingOccurrences(of: " ", with: "").isEmpty else {
            cancelButtonDidTap()
            return
        }
        viewModel.addMockData()
    }
  • 서치 바의 클릭 버튼 이벤트 감지 함수
  • 뷰 모델의 검색 기능 - 현재 가데이터 추가로 검색 결과 표시 체크
extension ExploreViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        collectionView.deselectItem(at: indexPath, animated: true)
        let model = viewModel.exploreModel.value[indexPath.row]
        let vc = PostViewController(model: model)
        vc.title = model.postType.rawValue
        navigationController?.pushViewController(vc, animated: true)
    }
}
  • 선택된 포스팅 모델을 통해 네비게이션

소스 코드

import UIKit
import Combine

class ExploreViewController: UIViewController {
    private let searchBar: UISearchBar = {
        let searchBar = UISearchBar()
        searchBar.placeholder = "Search..."
        searchBar.backgroundColor = .secondarySystemBackground
        return searchBar
    }()
    private let dimmedView: UIView = {
        let view = UIView()
        view.backgroundColor = .black
        view.isHidden = true
        view.alpha = 0
        return view
    }()
    private var collectionView: UICollectionView?
    private let viewModel = ExploreViewModel()
    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()
        setUI()
        bind()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView?.frame = view.bounds
        dimmedView.frame = view.bounds
    }
    
    private func setUI() {
        view.backgroundColor = .systemBackground
        navigationController?.navigationBar.topItem?.titleView = searchBar
        setCollectionView()
        setSearchBar()
        view.addSubview(dimmedView)
    }
    
    private func bind() {
        viewModel
            .exploreModel
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.collectionView?.reloadData()
            }
            .store(in: &cancellables)
    }
    
    private func setSearchBar() {
        searchBar.delegate = self
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(cancelButtonDidTap))
        tapGesture.numberOfTapsRequired = 1
        tapGesture.numberOfTouchesRequired = 1
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }
    
    private func setCollectionView() {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        layout.itemSize = CGSize(width: (view.width - 4) / 3, height: (view.width - 4) / 3)
        layout.minimumLineSpacing = 1
        layout.minimumInteritemSpacing = 1
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView?.delegate = self
        collectionView?.dataSource = self
        collectionView?.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: PhotoCollectionViewCell.identifier)
        guard let collectionView = collectionView else { return }
        view.addSubview(collectionView)
    }
}

extension ExploreViewController: UISearchBarDelegate {
    
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard
            let text = searchBar.text,
            !text.replacingOccurrences(of: " ", with: "").isEmpty else {
            cancelButtonDidTap()
            return
        }
        viewModel.addMockData()
    }
    
    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelButtonDidTap))
        dimmedView.isHidden = false
        UIView.animate(withDuration: 0.2) {
            self.dimmedView.alpha = 0.4
        }
    }
    
    @objc private func cancelButtonDidTap() {
        searchBar.resignFirstResponder()
        navigationItem.rightBarButtonItem = nil
        UIView.animate(withDuration: 0.2, animations: {
            self.dimmedView.alpha = 0
        }) { done in
            if done {
                self.dimmedView.isHidden = true
            }
        }
        view.endEditing(true)
    }
}

extension ExploreViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        collectionView.deselectItem(at: indexPath, animated: true)
        let model = viewModel.exploreModel.value[indexPath.row]
        let vc = PostViewController(model: model)
        vc.title = model.postType.rawValue
        navigationController?.pushViewController(vc, animated: true)
    }
}

extension ExploreViewController: UICollectionViewDelegateFlowLayout {
    
}

extension ExploreViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return viewModel.exploreModel.value.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath) as? PhotoCollectionViewCell else { return UICollectionViewCell() }
        let model = viewModel.exploreModel.value[indexPath.row]
        cell.configure(with: model)
        return cell
    }
}
  • 검색 뷰 UI 구현
  • 검색 결과 시 흐린 백그라운드 애니메이션 효과
  • 백그라운드, 검색 종료 시 자동으로 검색 바 비활성화 및 키보드 내리기

구현 화면

profile
JUST DO IT
post-custom-banner

0개의 댓글