[tvOS] 탭바와 SplitView 메뉴 열기/닫기 with RxTV

tvOS Advent Calendar 2017 13일째 기사입니다.

이번에는 RxSwift를 사용하여 UITabBarControllerUISplitViewController 메뉴의 열고 닫기를 구현해 보겠습니다.

두 가지 모두 Interface Builder에서 쉽게 작성할 수 있는 표준 UI 구성요소로, iOS와 마찬가지로 컨테이너 뷰 컨트롤러의 구조를 기반으로 만들어졌습니다.


우선은 UITabBarControllerUISplitViewController 의 열고 닫기를 프로그램으로 하기 위해서는 어떤 처리를 써야 하는지 확인합니다. 둘 다 솔직한 API가 없기 때문에 약간의 해킹이 필요했습니다.

탭바 열기/닫기



탭 바에 대해서는 StackOverflow에 단편적으로 실려 있습니다만 그대로는 움직이지 않고, 결과 이런 형태에 침착했습니다.
extension UITabBarController {

   func showTabBar() {

        self.tabBar.alpha = 1
        self.tabBar.isHidden = false

        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 1,
            initialSpringVelocity: 1,
            options: UIViewAnimationOptions.curveEaseOut,
            animations:
        {
            self.base.tabBar.frame.origin.y = 0

        }, completion: { _ in

            self.setNeedsFocusUpdate()
            self.updateFocusIfNeeded()

        })
    }

    func hideTabBar() {

        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 1,
            initialSpringVelocity: 1,
            options: UIViewAnimationOptions.curveEaseOut,
            animations:
        {
            self.base.tabBar.frame.origin.y = -self.base.tabBar.bounds.height

        }, completion: { _ in

            self.tabBar.isHidden = true
            self.tabBar.alpha = 0

            if let focused = UIScreen.main.focusedView,
                focused.isDescendant(of: self.base.tabBar) {

                self.setNeedsFocusUpdate()
                self.updateFocusIfNeeded()
            }
        })
    }
}

SplitView 열기/닫기



이쪽은 iOS에서도 비슷한 느낌이 되는 것 같네요.
extension UISplitViewController {

    func hideMasterViewIfNeeded() {
        if !_isMasterViewHidden { toggleMasterView() }
    }

    func showMasterViewIfNeeded() {
        if _isMasterViewHidden { toggleMasterView() }
    }

    private func toggleMasterView() {
        let barButtonItem = displayModeButtonItem
        UIApplication.shared.sendAction(barButtonItem.action!,
                                        to: barButtonItem.target,
                                        from: nil,
                                        for: nil)
    }

    private var _isMasterViewHidden: Bool {
        return view.subviews.contains { $0.frame.origin.x < 0 }
    }

}

열고 닫다



그리고는 열고 닫고 싶을 때에 위의 실장을 불러 주면 됩니다.
    isTabBarHidden
        .throttle(latest: true, scheduler: MainScheduler.instance)
        .observeOn(MainScheduler.instance)
        .subscribe(onNext: { [weak self] isHidden in
            if isHidden {
                self?.tabBarController?.hideTabBar()
            } else {
                self?.tabBarController?.showTabBar()
            }
        })
        .disposed(by: rx.disposeBag)

RxTV



위의 코드 그대로도 괜찮습니다만, RxSwift의 세계에서는 기본적으로는 bind만으로 끝나면 코드가 깨끗이 하므로, 정리합시다.
RxTV를 사용하면, 위와 같은 코드는 일절 서지 않고 Binder로서 구현하고 있다 rx.isMasterViewHiddenrx.isTabBarHidden 의 프로퍼티에 각각 bind 하는 것만으로 끝납니다.
htps : // 기주 b. 코 m / 0383 / RxTV

타이머로 자동 문처럼 해 보았습니다.
import RxSwift
import RxTV
import UIKit

class SplitViewController: UISplitViewController {

    private var disposeBag = DisposeBag()

    override func viewDidLoad() {

        super.viewDidLoad()

        let main = MainScheduler.instance

        Observable<Int>.timer(1.0, period: 1.0, scheduler: main)
            .map { $0 % 2 == 0 }
            .bind(to: rx.isMasterViewHidden)
            .disposed(by: disposeBag)

        Observable<Int>.timer(1.2, period: 1.2, scheduler: main)
            .map { $0 % 2 == 0 }
            .bind(to: tabBarController!.rx.isTabBarHidden)
            .disposed(by: disposeBag)

    }

}



하는 것에 비하면, 대부분의 코드가 깨끗이 하고 있다고 생각합니다.

요약



탭 바와 SplitView 메뉴 자동 열고 닫기를 RxSwift와 RxTV로 구현해 보았습니다. RxTV는 그 밖에도 rx.didUpdateFocus 이나 rx.didUpdateFocus(match:) 라고 하는 편리 API를 제공하고 있으므로 좋으면 사용해 보세요.

tvOS Advent Calendar 2017 내일은 @m_유키오 에 의한 「tvOS 재입문」입니다!

좋은 웹페이지 즐겨찾기