[UIKit] Netflix Clone: Navigation Bar

Junyoung Park·2022년 10월 30일
0

UIKit

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

Building Netflix App in Swift 5 and UIKit - (Xcode 13, 2021) - Episode 4 - Navigation Bar

Netflix Clone: Navigation Bar

구현 목표

  • 커스텀 네비게이션 바 구현

구현 태스크

  • 네비게이션 바 버튼 아이템 구현
  • 테이블 뷰 셀 구현
  • 컬렉션 뷰 셀 구현
  • 커스텀 바인딩 함수
  • 가데이터를 컴바인 데이터 퍼블리셔를 통해 바인딩

핵심 코드

class HomeViewModel {
    var sectionModels: CurrentValueSubject<[HomeSectionModel], Never> = .init([])
    
    init() {
        addSubcription()
    }
    
    private func addSubcription() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [weak self] in
            self?.sectionModels.send(self?.getMockData() ?? [])
        })
    }
    
    private func getMockData() -> [HomeSectionModel] {
        let mockContents1 = [ContentModel(name: "사라진 탄환", imageURLString: nil), ContentModel(name: "12 솔져스", imageURLString: nil), ContentModel(name: "듄", imageURLString: nil)]
        let mockSection1 = HomeSectionModel(sectionName: "해외 액션", contents: mockContents1)
        let mockContents2 = [ContentModel(name: "슈룹", imageURLString: nil), ContentModel(name: "서부 전선 이상 없다", imageURLString: nil)]
        let mockSection2 = HomeSectionModel(sectionName: "지금 뜨는 콘텐츠", contents: mockContents2)
        let mockContents3 = [ContentModel(name: "스파이더맨", imageURLString: nil), ContentModel(name: "무법변호사", imageURLString: nil)]
        let mockSection3 = HomeSectionModel(sectionName: "최근 찾아본 콘텐츠", contents: mockContents3)
        let mockContents4 = [ContentModel(name: "작은 아씨들", imageURLString: nil), ContentModel(name: "크리미널 스쿼드", imageURLString: nil)]
        let mockSection4 = HomeSectionModel(sectionName: "새로 올라온 콘텐츠", contents: mockContents4)
        return [mockSection1, mockSection2, mockSection3, mockSection4]
    }
}
  • 현재 넷플릭스 컨텐츠 데이터가 API로 받아오지 않은 상태이기 때문에 네트워킹 상황이라고 가정
  • 데이터 퍼블리셔를 통해 뷰 모델에서 뷰 컨트롤러에 해당 데이터를 리턴
private func bind() {
        viewModel
            .sectionModels
            .receive(on: DispatchQueue.main)
            .sink { [weak self] models in
                self?.tableView.reloadData()
            }
            .store(in: &cancellables)
    }
  • 뷰 컨트롤러에서 해당 뷰 모델의 데이터 퍼블리셔를 구독
  • 데이터 변경 상황이 있을 때마다 해당 데이터를 통해 새로운 테이블 뷰 UI를 패치
private func setNavigationBar() {
        navigationItem.largeTitleDisplayMode = .never
        
        let leftButton = UIButton(type: .system)
        leftButton.setImage(UIImage(named: "logo")?.withRenderingMode(.alwaysOriginal), for: .normal)
        let leftItem = UIBarButtonItem(customView: leftButton)
        leftItem.customView?.translatesAutoresizingMaskIntoConstraints = false
        leftItem.customView?.heightAnchor.constraint(equalToConstant: 36).isActive = true
        leftItem.customView?.widthAnchor.constraint(equalToConstant: 20).isActive = true
        navigationItem.leftBarButtonItem = leftItem
        let playButton = UIBarButtonItem(image: UIImage(systemName: "airplayvideo.circle")?.withTintColor(.label, renderingMode: .alwaysOriginal), style: .done, target: self, action: nil)
        let profileButton = UIBarButtonItem(image: UIImage(systemName: "person")?.withTintColor(.label, renderingMode: .alwaysOriginal), style: .done, target: self, action: nil)
        navigationItem.rightBarButtonItems = [
            playButton,
            profileButton
        ]
    }
  • 커스텀 네비게이션 바 코드를 구현
  • 특정 이미지를 바 버튼 아이템에 넣어 구현(customView 사용)
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        guard let header = view as? UITableViewHeaderFooterView else { return }
        header.textLabel?.font = .systemFont(ofSize: 18, weight: .semibold)
        header.textLabel?.textColor = .white
        header.textLabel?.frame = CGRect(x: header.bounds.origin.x + 20, y: header.bounds.origin.y, width: 100, height: header.bounds.height)
    }
  • 뷰 모델의 특정 모델 데이터를 통해 헤더 뷰를 그린 뒤 헤더 뷰의 폰트, 크기 등을 커스텀 가능(헤더 뷰 자체를 커스텀한 상태로 리턴할 수도 있음)

구현 화면

테이블 뷰 셀 내에 별도의 커스텀 컬렉션 뷰가 있는 상황이기 때문에 바인딩을 여러 차례 해줘야 함

profile
JUST DO IT
post-custom-banner

0개의 댓글