MVC: Many View Controllers - 2020 dotSwift

rbw·2022년 12월 30일
0

TIL

목록 보기
64/97

참조

https://www.youtube.com/watch?v=ZShE3toDPIk

위 영상을 보고 정리한 글, 개인적으로 매우 좋은 세션이라고 생각해씀다.


Container

한 화면에 하나의 뷰컨을 권장하고 있지만 여러 뷰컨으로 만드는것은 결합성을 낮추는 장점이 있다고 설명하였음.

여러 뷰컨을 추가하는 간단한 방법의 예제 코드는 아래와 같음.

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()
    }
}

Generic

어떤 타입의 뷰도 붙일 수 있는 제네릭을 사용하는 테이블/컬렉션 뷰를 만들어서 매우 쉽게 컬렉션을 만들 수 있다고 하심

첫 단계는 컨테이너 컬렉션 보기 셀을 만드는것임

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 }

    //...
}

위 코드는 셀이 생성되거나 재활용시 호출된다.

DUMB ViewController

책임이 적은 뷰컨트롤러를 설명함. 뷰를 배치하고, 속성만 설정하는 컨트롤러. 또한 탭과 같은 간단한 뷰 이벤트에 응답할 수 있지만 가능한 한 멍청해야한다

뷰컨이 많이 책임을 가지고 알고 있다면, 재사용하는 경우 매우 어려울 것이라고 설명함

최대한 논리가 거의 없는 뷰 컨을 선호.

FlowController

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) {
        // ...
    }
}

중요한 것은 제품을 만드는데 사용되는 도구보다 제품이 더 중요하다. 늘 사용자의 경험을 생각하자라고 말하고 끝남

profile
hi there 👋

0개의 댓글