Created
June 26, 2020 11:13
-
-
Save MihaelIsaev/0c0950ccec4a74fee67ba2b052d3d6eb to your computer and use it in GitHub Desktop.
Revisions
-
MihaelIsaev created this gist
Jun 26, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,312 @@ import UIKitPlus class MainViewController: ViewController { override var statusBarStyle: StatusBarStyle { if let controller = controllers[current]?.protocolController { if let controller = controller as? ViewController { return controller.statusBarStyle } return .from(controller.preferredStatusBarStyle) } return .default } lazy var home = WrappedViewControllerView.home(self) lazy var statistics = WrappedViewControllerView.statistics(self) lazy var trophy = WrappedViewControllerView.trophy(self) lazy var marketplace = WrappedViewControllerView.marketplace(self) lazy var balance = WrappedViewControllerView.balance(self) typealias Controllers = [HomeTab: WrappedViewControllerable] lazy var controllers: Controllers = [.home: home, .statistics: statistics, .trophy: trophy, .marketplace: marketplace, .balance: balance] @UState var current: HomeTab = .home lazy var bottomBar = BottomTabBar($current) var presentingGoalEndedView = false override func buildUI() { super.buildUI() view.backgroundColor = 0xf5f6f8.color body { home statistics trophy marketplace balance bottomBar } switchToController(current) $current.listen { old, new in guard old != new else { return } self.switchToController(new) } } func switchToController(_ new: HomeTab) { controllers.forEach { $0.value.hidden($0.key != new) } navigationController?.setNeedsStatusBarAppearanceUpdate() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) (navigationController as? NavigationController<MainViewController>)?.isSwipeBackEnabled = true } } #if canImport(SwiftUI) && DEBUG import SwiftUI @available(iOS 13.0, *) struct MainViewController_Preview: PreviewProvider, DeclarativePreview { static var preview: Preview { Preview { MainViewController() } .colorScheme(.dark) .device(.iPhoneX) .language(.en) } } #endif // MARK - AppDelegate extension AppDelegate { static var shared: AppDelegate { UIApplication.shared.delegate as! AppDelegate } } extension AppDelegate { var safeInsets: UIEdgeInsets { window?.safeInsets ?? .zero } } // MARK: - Tab images extension UIImage { static var homeTab: UIImage? { UIImage(named: "tabBarHome") } static var statisticsTab: UIImage? { UIImage(named: "tabBarStatistics") } static var trophyTab: UIImage? { UIImage(named: "tabBarTrophy") } static var marketplaceTab: UIImage? { UIImage(named: "tabBarMarketplace") } static var balanceTab: UIImage? { UIImage(named: "tabBarBalance") } } // MARK: - Tab titles extension String { static var homeTitle: String { String(.en("___"), .ru("___")) } static var statisticsTitle: String { String(.en("___"), .ru("___")) } static var trophyTitle: String { String(.en("___"), .ru("___")) } static var marketplaceTitle: String { String(.en("___"), .ru("___")) } static var balanceTitle: String { String(.en("___"), .ru("___")) } } // MARK: - Tab colors extension UIColor { static var tabBarActive = 0x3EC900.color static var tabBarInactive = UIColor.lightGray } // MARK: - Tab types enum HomeTab { case home, statistics, trophy, marketplace, balance var title: String { switch self { case .home: return .homeTitle case .statistics: return .statisticsTitle case .trophy: return .trophyTitle case .marketplace: return .marketplaceTitle case .balance: return .balanceTitle } } var icon: UIImage? { switch self { case .home: return .homeTab case .statistics: return .statisticsTab case .trophy: return .trophyTab case .marketplace: return .marketplaceTab case .balance: return .balanceTab } } } // MARK: TabBar view class BottomTabBar: UView { var state: UState<HomeTab> init (_ state: UState<HomeTab>) { self.state = state super.init(frame: .zero) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func buildView() { super.buildView() background(.white) border(.top, 1, 0xF3F3F3) height(84 + AppDelegate.shared.safeInsets.bottom) edgesToSuperview(leading: 0, trailing: 0, bottom: 0) body { UHStack { BottomTabBarButton(.home, state: state) BottomTabBarButton(.statistics, state: state) BottomTabBarButton(.trophy, state: state) BottomTabBarButton(.marketplace, state: state) BottomTabBarButton(.balance, state: state) } .centerXInSuperview() .topToSuperview(16) .height(50) .distribution(.equalSpacing) } } } // MARK: TabBar item view class BottomTabBarButton: UView { let image: UIImage? let title: String let type: HomeTab let state: UState<HomeTab> lazy var imageView = Image(image) lazy var titleLabel = Text(title) init(_ type: HomeTab, state: UState<HomeTab>) { self.title = type.title self.image = type.icon self.type = type self.state = state super.init(frame: .zero) updateSelection() state.listen(updateSelection) onTapGesture(tapAction) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func buildView() { super.buildView() body { UVStack { imageView .mode(.scaleAspectFit) .size(25) .topToSuperview() .centerXInSuperview() titleLabel .top(to: .bottom, of: imageView, 4) .edgesToSuperview(leading: 0, trailing: 0, bottom: 0) .alignment(.center) .font(.helveticaNeueMedium, 12) }.edgesToSuperview().width(75 !! .iPhone6(70) !! .iPhone5(70)) } } func updateSelection() { imageView.tint(state.wrappedValue == type ? .tabBarActive : .tabBarInactive) titleLabel.color(state.wrappedValue == type ? .tabBarActive : .tabBarInactive) } func tapAction() { guard state.wrappedValue != type else { return } ImpactFeedback.selected() state.wrappedValue = type } } // MARK: - Helper extensions extension WrappedViewControllerView { static func home(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController1>> { let r = improve(_home(parent)).bottom(to: .top, of: parent.bottomBar, -20) r.inner.style(.transparent).hideNavigationBar() return r } static func statistics(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController2>> { let r = improve(_statistics(parent)).bottom(to: .top, of: parent.bottomBar, -20) r.inner.style(.transparent).hideNavigationBar() return r } static func trophy(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController3>> { let r = improve(_trophy(parent)).bottom(to: .top, of: parent.bottomBar, -20) r.inner.style(.transparent).hideNavigationBar() return r } static func marketplace(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController4>> { let r = improve(_marketplace(parent)).bottom(to: .top, of: parent.bottomBar, -20) r.inner.style(.transparent).hideNavigationBar() return r } static func balance(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController5>> { let r = improve(_balance(parent)).bottom(to: .top, of: parent.bottomBar, -20) r.inner.style(.transparent).hideNavigationBar() return r } static func improve<V>(_ v: V) -> V { (v as? UView)?.edgesToSuperview(top: 0, leading: 0, trailing: 0) return v } } // MARK: Tricky implementation to make generics work extension WrappedViewControllerView { fileprivate static func _home(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController1>> { .init(.init(.init(parent)), parent: parent) } fileprivate static func _statistics(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController2>> { .init(.init(.init(parent)), parent: parent) } fileprivate static func _trophy(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController3>> { .init(.init(.init(parent)), parent: parent) } fileprivate static func _marketplace(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController4>> { .init(.init(.init(parent)), parent: parent) } fileprivate static func _balance(_ parent: MainViewController) -> WrappedViewControllerView<NavigationController<ViewController5>> { .init(.init(.init(parent)), parent: parent) } } // MARK: - Tab Controllers class MainSubController: ViewController { let mainViewController: MainViewController init (_ mainViewController: MainViewController) { self.mainViewController = mainViewController super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } class ViewController1: MainSubController {} class ViewController2: MainSubController {} class ViewController3: MainSubController {} class ViewController4: MainSubController {} class ViewController5: MainSubController {}