참조
https://www.youtube.com/watch?v=ZShE3toDPIk
위 영상을 보고 정리한 글, 개인적으로 매우 좋은 세션이라고 생각해씀다.
한 화면에 하나의 뷰컨을 권장하고 있지만 여러 뷰컨으로 만드는것은 결합성을 낮추는 장점이 있다고 설명하였음.
여러 뷰컨을 추가하는 간단한 방법의 예제 코드는 아래와 같음.
let child = MyViewController()
addChild(child)
child.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(child.view)
NSLayoutConstraint.activate([
child.view.leadingAnchor.constraint(equalTo: view.leadingAnchor)
...
])
child.didMove(toParent: self)
위 코드를 뷰 컨트롤러에 확장을 사용하여 메소드로 만든다면 더 쉽게 사용이 가능하다.
public extension UIViewController {
func install(_ child: UIViewController) {
...
}
}
예를 들어, 뷰 내부에 로딩화면, 빈화면 등이 필요한 상황을 가정함. 이는 일반적으로 보통 앱에 다 있다고 함 맞는말이긴 해
이 상태들을 열거형을 통해 구현을 하였다
public final class StateViewController: UIViewController {
public enum State {
case loading(message: String)
// 내가 전달할 자식 뷰 컨트롤러
case content(controller: UIViewController)
case error(message: String)
case empty(message: String)
}
}
// 상태 속성을 가지고 있고
// 설정이 되면 적용하는 메소드를 설정함
public var state: State = .loading(message: "Loading") {
didSet {
applyState()
}
}
어떤 타입의 뷰도 붙일 수 있는 제네릭을 사용하는 테이블/컬렉션 뷰를 만들어서 매우 쉽게 컬렉션을 만들 수 있다고 하심
첫 단계는 컨테이너 컬렉션 보기 셀을 만드는것임
public final class ContainerCollectionViewCell<V: UIView>: UICollectionViewCell {
public lazy var view: V = {
return V()
}()
public override init(frame: CGRect) {
super.init(frame: frame)
view.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(view)
// autolayout ...
view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
}
}
제네릭을 사용하여 UIView
를 받고 있는 모습~
이는 컬렉션/테이블 뷰 셀을 커스텀 뷰로 구현할 수 있음을 의미한다고 함
확실히 일반적으로 특정 셀에 UI를 추가하고 싶을 때 그 셀에만 해당 UI를 구현한 뷰를 제네릭에 넣어주면 될 듯 ?
그리고 컨테이너 셀에서 컬렉션 내부에 있을 뷰를 콘텐츠로 업데이트 해야함. 이를 수행하는 여러가지 방법이 있지만 여기에서는 간단한 클로저를 사용하고 있다.
class GenericCollectionViewController<V: UIView, C: ContainerCollectionViewCell<V>>: UICollectionViewController {
init(viewType: V.Type) {
super.init(collecionViewLayout: makeDefaultLayout())
}
var numberOfItems: () -> Int { 0 } {
didSet {
collectionView?.reloadData()
}
}
var configureView: (IndexPath, V) -> () = { _, _ in } {
didSet {
collectionView?.reloadData()
}
}
var didSelectView: (IndexPath, V) -> () = { _, _in }
//...
}
위 코드는 셀이 생성되거나 재활용시 호출된다.
책임이 적은 뷰컨트롤러를 설명함. 뷰를 배치하고, 속성만 설정하는 컨트롤러. 또한 탭과 같은 간단한 뷰 이벤트에 응답할 수 있지만 가능한 한 멍청해야한다
뷰컨이 많이 책임을 가지고 알고 있다면, 재사용하는 경우 매우 어려울 것이라고 설명함
최대한 논리가 거의 없는 뷰 컨을 선호.
MVC의 최악의 문제를 해결할 친구라고 설명함. 코디네이터라고 보면 된다. 대부분 화면의 흐름을 제어하려고 코디네이터 패턴을 사용한다. 이 패턴과 플로우컨트롤러는 접근방식이 유사하지만 여기서 설명하는 플컨은 UIViewController
에서 상속된다.
뷰컨에서 시작되고, 응답자 체인, 라이프 사이클 콜백 등을 활용할 수 있는 장점이 있음 !
다음 사진으로 예제를 살펴보겠슴다
NSObject를 상속받는 코디네이터 라는 뜻
만약 디테일 뷰컨이 사라진다면 ?
여전히 디테일 뷰컨을 관리하던 코디네이터는 존재할 것이다. 라고 설명함
그래서 여기서 설명한 플로우컨트롤러는 UIViewController
를 상속함으로써 메인 플로우 컨트롤러를 가질 수 있고 UI가 자식이 될 것이며, 아래 사진과 같이 디테일 뷰컨의 코디네이터의 일부를 취하고 UI를 자식의 UI로 갖는 디테일 플로우 컨트롤러를 제공할 것입니다.
이 방식의 좋은점은 라이프사이클의 활용이 있고 위에서 살펴본 뷰컨이 사라졌을때 코디네이터가 존재하는 문제도 걱정할 필요가 없다는 점입니다.
지역을 보여주는 일부 API에서 지역 목록을 다운로드하는 앱이 있다고 가정. 유저는 지역을 선택하면 더 많은 정보를 볼 수 있다.
지역 리스트가 있고 세부사항을 표시하는 컨트롤러가 있다.
RegionsFlowController
가 전체 흐름을 다루므로, 네비게이션 내부에 있도록 하면 x
자식 중 하나가 네비게이션 컨트롤러를 소유하는것으로 설계함.
이로써 우리는 서로 완전히 분리되어 다른 컨트롤러에서 사용할 수 있는 재사용 가능한 플로우컨트롤러를 만들었습니다.
무언가를 선택하면 부모에게 해당 항목이 선택됨을 알립니다. 이를 통해 좀 더 복잡한 로직도 수행이 가능합니다.
마지막으로는 아래의 뷰모델을 설명해주면서 결국 뷰에 띄우는 모델 이라고 모델으로 부르고 MVC라고 부르는걸 선호한다? 이렇게 해석했고(틀릴 가능성 99.5%) 말하고자 하는것은 결국 명칭에 큰 의미를 두지 말자 인거 같기도함
struct Post: Hashable, Codable {
let title: String
let author: String
let publishedAt: Date
}
struct PostViewModel: Hashable {
let title: String
let author: PersonNameComponents
let publishedAt: String
}
extension PostViewModel {
init(post: Post) {
// ...
}
}
중요한 것은 제품을 만드는데 사용되는 도구보다 제품이 더 중요하다. 늘 사용자의 경험을 생각하자라고 말하고 끝남