XCoordinator icon indicating copy to clipboard operation
XCoordinator copied to clipboard

Implementing sliding side menu with XCoordinator

Open DaliborK opened this issue 3 years ago • 2 comments

Hi, I really like this framework and would like to use it for navigating with a side menu, however, I'm stuck with its implementation into a container beside a sideMenu controller. Or would be possible to create an animation style for sliding the existing controller and showing the menu? Can you please advise on a possible way how to implement it?

DaliborK avatar Dec 22 '21 16:12 DaliborK

You would need to implement your own container view controller (ie. SlideMenuController) that manages two child view controllers: a bottom (menu) and top (current screen) Your container view controller would need to have some ability to open/close the menu.

Once you have that setup you would need to do the following:

  1. Create your own SlideMenuCoordinator inherited from BaseCoordinator. Your rootViewController would be your SlideMenuController type.
  2. Create your own SlideMenuTransition based on Transition<SlideMenuController>, adding open and close options for the transitions types available.
  3. Use your coordinator like normal!

jaron-m-lowe avatar Mar 17 '22 18:03 jaron-m-lowe

@DaliborK Hi bro

Recently, I developed the feature you want using REFrostedViewController and XCoordinator. so, I want to share with you and the rest of the communities that used this framework.

you have to create one main coordinator and a side menu coordinator.

import XCoordinator
import REFrostedViewController
import RxSwift

enum MainRoute: Route {
    case initial
    case side
    case action(Destination)
}

final class MainCoordinator: BaseCoordinator<MainRoute, Transition<REFrostedViewController>> {

    required init(route: RouteType? = .initial, factory: FactoryType) {
        self.factory = factory
        super.init(rootViewController: REFrostedViewController(), initialRoute: .initial)
        prepareRootViewController()
    }

    override func prepareTransition(for route: RouteType) -> TransitionType {
        do {
            switch route {
            case .initial:
                let mainViewController = MainViewController()
                rootViewController.contentViewController = mainViewController

                mainViewController.sideMenuButton.rx.tap
                    .bind(with: self) { (this, _) in
                        this.trigger(.side)
                    }.disposed(by: disposeBag)

                return .none()
            case .side:

                var sideMenuVC = rootViewController.menuViewController as? SideMenuViewController

                if rootViewController.menuViewController == nil {
                    sideMenuVC = SideMenuViewController()
                    rootViewController.menuViewController = sideMenuVC

                    sideMenuVC?.viewModel?.didSelectItem.debounce(.microseconds(200))
                        .asObservable()
                        .bind(with: self, onNext: { (this, destination) in
                            this.rootViewController.hideMenuViewController {
                                this.trigger(.action(destination))
                            }
                        }).disposed(by: disposeBag)
                }

                rootViewController.presentMenuViewController()

                return .none()
            case .action(let destination):
                let coordinator = SideMenuCoordinator(rootViewController: rootViewController)
                return .route(destination.menuRoute(), on: coordinator)
            }
        }catch let error {
            log.error(error, tag: Self.self)
            return .none()
        }
    }

    private func prepareRootViewController() {
        rootViewController.direction = .left
        rootViewController.liveBlur = false
        rootViewController.animationDuration = 0.25
        rootViewController.liveBlurBackgroundStyle = .dark
        rootViewController.blurRadius = 10.0
        rootViewController.blurTintColor = .primaryGray
        rootViewController.blurSaturationDeltaFactor = 0.5
        let width = UIScreen.main.bounds.width * 0.863_111_1
        rootViewController.limitMenuViewSize = true
        rootViewController.menuViewSize = CGSize(width: width, height: 0)
    }

}

the SideMenuCoordinatoor would be

enum MenuRoute: Route {
    case inbox
    case unknown
}

final class SideMenuCoordinatoor: BaseCoordinator<MenuRoute, Transition<UIViewController>> {

    deinit {
        log.verbose("Deinit \(Self.self)", tag: Self.self)
    }

   override func prepareTransition(for route: MenuRoute) -> TransitionType {
        switch (route) {
        case .inbox:
            let coordinator = InboxCoordinator()
            return .presentOnRoot(coordinator, animation: .default)
        case .unknown:
            return .none()
        }
    }
 }

farshadmb avatar Jul 21 '22 22:07 farshadmb