➰ 이전 코드 내용
💡Head Space Focus(2)에서 할 것은 Navigation 구현!
- 상세 뷰로 넘어가게 하기!
❗️Navigation을 구현할 때는, 사용자가 최대한 개미지옥에서 탈출할 수 있도록 구현해줘야 함을 잊지말자!
1️⃣ 상세 뷰를 위한 다른 Storyboard, View Controller 만들기
- QuickFocusStoryboard
- QuickFocusListViewController
- 새로운 스토리보드의 Class와 Storyboard ID 까아쥐
2️⃣ 화면의 Component, AutoLayout 설정
- CollectionView 이용 → CollectionViewCell도 필요하겠지? "QuickFocusCell" 만들기
// QuickFocusCell.swift
import UIKit
class QuickFocusCell: UICollectionViewCell {
@IBOutlet weak var thumbnailImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
func configure(_ quickFocus: QuickFocus) {
thumbnailImageView.image = UIImage(named: quickFocus.imageName)
titleLabel.text = quickFocus.title
descriptionLabel.text = quickFocus.description
}
}
3️⃣ Diffable Datasource 만들기
// QuickFocusListViewController.swift
import UIKit
class QuickFocusListViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
let breathingList = QuickFocus.breathing
let walkingList = QuickFocus.walking
// diffable datasource를 사용하기 위한, Section 정의
// CaseIterable 프로토콜을 따르도록
enum Section: CaseIterable {
case breathe
case walking
// 각 Section별로 title을 정해주기
var title: String {
switch self {
case .breathe: return "Breathing exercises"
case .walking: return "Mindful walks"
}
}
}
typealias Item = QuickFocus
var datasource: UICollectionViewDiffableDataSource<Section, Item>!
override func viewDidLoad() {
super.viewDidLoad()
}
}
→ CaseIterable 프로토콜을 따르게 한다면?
let allItems: [Section] = [.breathing, .walking]
대신, Section.allCases
로 불러올 수 있다.
→ 각 Section 별로 title을 위와 같이 설정해주면,
let section: Section = .breathe
section.title
라고 코드를 작성하여, "Breathing exercises"라는 title을 가져올 수 있게 된다.
4️⃣ Presentation, Data, Layout
- Presentation : datasource
- Data : Snapshot
- Layout : compositional layout
// QuickFocusListViewController.swift
import UIKit
class QuickFocusListViewController: UIViewController {
// ...
override func viewDidLoad() {
super.viewDidLoad()
// presentation
datasource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "QuickFocusCell", for: indexPath) as? QuickFocusCell else {
return nil
} // collectionView에서 재사용 Cell 가져오기
cell.configure(item) // 자동으로 업데이트 하게끔
return cell
})
// data
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(Section.allCases) // == snapshot.appendSections([.breathe, .walking])
// breathingList를 breathe Section에
// walkingList를 walking Section에 넣어주기
snapshot.appendItems(breathingList, toSection: .breathe)
snapshot.appendItems(walkingList, toSection: .walking)
datasource.apply(snapshot)
// layout
collectionView.collectionViewLayout = layout() // 너무 기니까 따로 빼서 만들기
}
private func layout() -> UICollectionViewCompositionalLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .estimated(50))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
// 내부 컨텐츠에 따라 높이가 달라질 경우, .estimated 사용
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))
// 수평방향으로 동일하게 3개의 group이 만들어 질 것 -> horizontal
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 2)
// item 간의 spacing
group.interItemSpacing = .fixed(10)
let section = NSCollectionLayoutSection(group: group)
// section에 좌우패딩 주기
section.contentInsets = NSDirectionalEdgeInsets(top: 30, leading: 20, bottom: 30, trailing: 20)
// group 간의 spacing
section.interGroupSpacing = 20
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
}
Compositional Layout을 어떻게 구성했나?!
5️⃣ Section Header Title을 넣어보자!
- Collection Reusable View 이용
- UICollectionReusableView 파일 만들기 → "QuickFocusHeaderView"라고 명명
- 마찬가지로 Storyboard 설정에서 Custom Class와 Collection Reusable View의 identifier을 "QuickFocusHeaderView"로 설정
// QuickFocusHeaderView.swift
import UIKit
class QuickFocusHeaderView: UICollectionReusableView {
@IBOutlet weak var titleLabel: UILabel!
func configure(_ title: String) {
titleLabel.text = title
}
}
- title datasource 만들기
- supplymentaryViewProvider을 통해 collectionView, kind, indexPath를 받아옴
// QuickFocusListViewController.swift
import UIKit
class QuickFocusListViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// presentation
// ...
datasource.supplementaryViewProvider = {(collectionView, kind, indexPath) in
guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "QuickFocusHeaderView ", for: indexPath) as? QuickFocusHeaderView else {
return nil
}
let allSections = Section.allCases
let section = allSections[indexPath.section]
header.configure(section.title)
return header
}
// ...
}
// ...
}
- header view 관련한 layout도 정의 마무리
// QuickFocusListViewController.swift
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
section.boundarySupplementaryItems = [header]
6️⃣ Delegate 설정
- delegate를 self로 설정하고 프로토콜 준수까지 완료하기
// FocusViewController.swift
import UIKit
class FocusViewController: UIViewController {
// ...
override func viewDidLoad() {
super.viewDidLoad()
// ...
// 화면 넘어감을 위한 delegate 설정
collectionView.delegate = self
}
// ...
}
// For delegate
extension FocusViewController: UICollectionViewDelegate {
// collectionView에서 indexPath에 있는 item을 선택을 했을 때 실행되는 함수
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// let item = items[indexPath.item]
// 상세뷰로 넘어가는 storyboard 접근
let storyboard = UIStoryboard(name: "QuickFocus", bundle: nil)
// viewController도 가져와
let vc = storyboard.instantiateViewController(withIdentifier: "QuickFocusListViewController") as! QuickFocusListViewController
present(vc, animated: true) // 이전에 배웠던 모달 띄우기
}
}
자, 여기까지 구현한 화면! 🚀
→ 일단 지금은 전에 구현해봤던 modal을 사용해서 상세페이지를 띄워봤지만, 이제 Navigation page로 나타나게 해보자!
7️⃣ Navigation Controller 설정
// For delegate
extension FocusViewController: UICollectionViewDelegate {
// ...
let vc = storyboard.instantiateViewController(withIdentifier: "QuickFocusListViewController") as! QuickFocusListViewController
navigationController?.pushViewController(vc, animated: true)
}
}
present(vc, animated: true)
→ navigationController?.pushViewController(vc, animated: true)
navigation이 설정된 화면으로 넘어감 🚀
8️⃣ 상세 뷰의 navigation 바에 title 넣기!
vc.title = item.title // 상세 화면으로 넘어갔을 때, title 나오게!
근데 이렇게 하면, large title로 적용되어서 나옴
그냥 작게 navigation 바에만 뜨게 하려면,
// QuickFocusListViewController.swift
self.navigationItem.largeTitleDisplayMode = .never
로 설정
📌 정리,
- Presentation → Diffable DataSource
- Data → Snapshot
- Layout → Compositional Layout
- Compositional layout을 통해, Content 길이에 따라 group과 item의 길이가 자동으로 달라지게 하기! .estimated
- group안의 itemd의 개수가 2개로 명확할 경우
→ NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 2)
- Section이 2개! → CaseIterable 프로토콜 사용해봄!
- Navigation Controller 설정
- Collection Reusable View를 이용한, Section의 Title 설정
- UICollectionReusableView 파일 생성
- title datasource 만들기 → supplymentaryViewProvider을 통해 collectionView, kind, indexPath를 받아옴
- layout도 정의 마무리 → compositional layout에 맞춰서,,
Reference
- 패스트 캠퍼스
'iOS > Toy project' 카테고리의 다른 글
[iOS : Toy Project] Github Profile (1) (0) | 2022.07.16 |
---|---|
[iOS : Toy Project] Apple Framework List (4) : Combine (0) | 2022.07.08 |
[iOS : Toy Project] Apple Framework List (3) : Modal (0) | 2022.06.29 |
[iOS : Toy Project] Spotify Paywall : CollectionView, Paging Control (0) | 2022.06.28 |
[iOS : Toy Project] Head Space Focus (0) | 2022.06.07 |