Skip to content

MainView

WooSeok Suh edited this page Jul 5, 2021 · 8 revisions

2021-07-05

by Seok

Step.1

  • MainView 하단의 ViewController 이동 Button을 UIButton으로 생성하고 Horizontal StackView에 배치
  • AutoLayout 확인 (iPod touch, 8, 11, 12)

Step.2

Button의 다형성 구현을 위하여 ButtonController 및 ButtonMapper 객체 생성

  • 각 Button Touch시 어떤 Button이 눌리는지 구분 기능 구현 완료
  • 해당 기능 바탕으로 이동할 ViewController와 Mapping 예정
import UIKit

enum ViewControllerType: CaseIterable {
    case giftVC
    case advertiseVC
    case rankVC
    case itemVC
}

final class ButtonMapper {
    private var map: [UIButton: ViewControllerType]
    
    init(from buttons: [UIButton]) {
        self.map = Dictionary(uniqueKeysWithValues: zip(buttons, ViewControllerType.allCases))
    }
    
    subscript(button: UIButton) -> ViewControllerType? {
        return map[button]
    }
}

final class ButtonController: NSObject {
    
    @IBOutlet var moveToVCButtons: [UIButton]!
    
    private var moveToVCMapper: ButtonMapper?
    private var buttonTouchedHandler: (ViewControllerType) -> ()
    
    override init() {
        buttonTouchedHandler = { _ in }
    }
    
    func setupButton() {
        self.moveToVCMapper = ButtonMapper(from: moveToVCButtons)
    }
    
    @IBAction func buttonTouched(sender: UIButton)  {
        guard let vc = moveToVCMapper?[sender] else { return }
        buttonTouchedHandler(vc)
    }
    
    func bind(action: @escaping (ViewControllerType) -> ()) {
        self.buttonTouchedHandler = action
    }
}

이때 SceneCoordinator 생성하여 진행할지 회의 필요 -> SceneCoordinator로 구현하기로 협의하여 객체생성

import Foundation
import RxSwift
import RxCocoa

final class SceneCoordinator: SceneCoordinatorType {
    
    private var window: UIWindow
    private var currentVC: UIViewController
    
    required init(window: UIWindow) {
        self.window = window
        currentVC = window.rootViewController!
    }
    
    @discardableResult
    func transition(to scene: Scene, using style: TransitionStyle, animated: Bool) -> Completable {
        let subject = PublishSubject<Void>()
        let target = scene.instantiate()
        
        switch style {
        case .root:
            currentVC = target
            window.rootViewController = target
            subject.onCompleted()
            
        case .fullScreen:
            target.modalPresentationStyle = .fullScreen
            currentVC.present(target, animated: animated) {
                subject.onCompleted()
            }
            currentVC = target
            
        case .modal:
            currentVC.present(target, animated: animated) {
                subject.onCompleted()
            }
            currentVC = target
        }
        return subject.ignoreElements().asCompletable()
    }
    
    @discardableResult
    func close(animated: Bool) -> Completable {
        let subject = PublishSubject<Void>()
        
        if let presentingVC = self.currentVC.presentingViewController {
            self.currentVC.dismiss(animated: animated) {
                self.currentVC = presentingVC
                subject.onCompleted()
            }
        } else {
            subject.onError(TransitionError.unknown)
        }
        
        return subject.ignoreElements().asCompletable()
    }
}

Step.3

ViewController가 ViewModel로 Button이 가지고있는 ViewControllerType을 넘기면 VC 이동기능 구현

  • CommonViewModel이 가지고 있는 SceneCoordinator 객체 활용
import Foundation

class MainViewModel:CommonViewModel {
    
    func makeMoveAction(to viewController: ViewControllerType) {
        switch viewController {
        case .giftVC:
            let giftViewModel = GiftViewModel(sceneCoordinator: self.sceneCoordinator)
            let giftScene = Scene.gift(giftViewModel)
            self.sceneCoordinator.transition(to: giftScene, using: .modal, animated: true)
            
        case .advertiseVC:
            let advertiseViewModel = AdvertiseViewModel(sceneCoordinator: self.sceneCoordinator)
            let advertiseScene = Scene.ad(advertiseViewModel)
            self.sceneCoordinator.transition(to: advertiseScene, using: .fullScreen, animated: true)
            
        case .rankVC:
            let rankViewModel = RankViewModel(sceneCoordinator: self.sceneCoordinator)
            let rankScene = Scene.rank(rankViewModel)
            self.sceneCoordinator.transition(to: rankScene, using: .modal, animated: true)
            
        case .itemVC:
            let itemViewModel = ItemViewModel(sceneCoordinator: self.sceneCoordinator)
            let itemScene = Scene.item(itemViewModel)
            self.sceneCoordinator.transition(to: itemScene, using: .modal, animated: true)
        }
    }
    
}

추후 로직에 따라 Storage 등의 추가 객체가 필요하면 CommonViewModel의 init에 주입하여 SceneDelegate에서 넣어주면 쉽게 코드 변경 가능

  • 이때도 마찬가지로 Protocol활용하여 다형성 구현가능
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let coordinator = SceneCoordinator(window: window!)
    let mainViewModel = MainViewModel(sceneCoordinator: coordinator)
    let mainScene = Scene.main(mainViewModel)
    coordinator.transition(to: mainScene, using: .root, animated: false)
}

Clone this wiki locally