-
Notifications
You must be signed in to change notification settings - Fork 0
MainView
WooSeok Suh edited this page Jul 15, 2021
·
8 revisions
by Seok
- MainView 하단의 ViewController 이동 Button을 UIButton으로 생성하고 Horizontal StackView에 배치
- AutoLayout 확인 (iPod touch, 8, 11, 12)
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()
}
}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)
}by Seok
GiftVC & ViewModel 객체 생성 및 CancelButton 기능구현
- init 시점에서 action을 주입하여 변화에 열려있도록 설계
import Foundation
import Action
class GiftViewModel: CommonViewModel {
let confirmAction: Action<String, Void>
let cancelAction: CocoaAction
init(sceneCoordinator: SceneCoordinatorType, storage: ItemStorageType, confirmAction: Action<String, Void>? = nil ,cancelAction: CocoaAction? = nil) {
self.confirmAction = Action<String, Void> { input in
if let action = confirmAction {
action.execute(input)
}
return sceneCoordinator.close(animated: true).asObservable().map{ _ in }
}
self.cancelAction = CocoaAction {
if let action = cancelAction {
action.execute(())
}
return sceneCoordinator.close(animated: true).asObservable().map{ _ in }
}
super.init(sceneCoordinator: sceneCoordinator, storage: storage)
}
}by Seok
SceneCoordinator Transition Method Refactoring
import Foundation
import RxSwift
protocol SceneCoordinatorType {
@discardableResult
func transition(to scene: Scene, using style: TransitionStyle, with type: StoryboardType, animated: Bool) -> Completable
@discardableResult
func close(animated: Bool) -> Completable
@discardableResult
func toMain(animated: Bool) -> Completable
}- 기존에 없던 StoryboardType을 파라미터로 추가
- Scene에서 불필요하게 중복으로 Storyboard 생성했던 코드 삭제
Disable SwiftLint Waring
// swiftlint:disable:next cyclomatic_complexity
func instantiate(from storyboard: String) -> UIViewController- Swith Case에서 처리해야하는 VC 이동 코드 증가로 인하여 cyclomatic_complexity 경고
- 일단 Lint 경고를 무시하도록 처리하였지만, 추후 sceneCoordinator 또는 Method 분리를 고려하여 수정해야 함
StoryboardType enum타입 생성
import Foundation
enum StoryboardType {
case main
case game
var name: String {
switch self {
case .main:
return "Main"
case .game:
return "Game"
}
}
}- 파라미터 타입을 한정적(안정성 고려)으로 사용하기 위해 static let이 아닌 case로 구현
GhostTypewriter 라이브러리 추가
import UIKit
import GhostTypewriter
final class MainViewController: UIViewController, ViewModelBindableType {
var viewModel: MainViewModel!
@IBOutlet var buttonController: MainButtonController!
@IBOutlet weak var titleLabel: TypewriterLabel!
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.startTypewritingAnimation()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
titleLabel.restartTypewritingAnimation()
}
}- titleLabel animation 효과 추가
- 다른 VC로 이동 후, mainVC로 돌아올때 animationRestart 호출(viewWillAppear)
created by 우송