用于管理应用程序中导航的充分且超级方便的解决方案

🚂 过渡

Transitionfor Swift 是管理应用程序内导航的足够且超级方便的解决方案。您不需要“包装”控制器,或使用“基”类或协调器等。

使用该库归结为创建 and 类并从 .您甚至不必使用新协议扩展控制器本身。RouterRouteUIViewController

以下是它的工作原理:

🌱 Getting started

// 1️⃣ For UIViewController you create a router that will
// call the screen with the correct way to open
class HomeRouter: Router<HomeViewController> {}

// 2️⃣ Router should be in your UIViewController and be
// initialized at the time of the call
class HomeViewController: UIViewController {
    var router: ProfileRouter
    
    init(router: ProfileRouter) {
        self.router = router
    }
}


// 3️⃣ At the same time, each module must have its own route,
// which will describe the methods in which the new screen will be initialized
protocol ProfileRoute {
    func presentProfile()
    func pushProfile()
}

extension ProfileRoute where Self: RouterProtocol {
    // This is how the function to show the Profile in the modal window would look like
    func presentProfile() {
        // Use one of the preset opening schemes or implement your own.
        let transition = ModalTransition()
        
        // Transition needs to be passed to the router, which is stored in your controller
        // It is convenient to do this in a class that assembles a module,
        // such as Assembly in VIPER
        let module = ProfileModule(transition: transition)
        
        // The Transition library method is called into which the opened UIViewController
        // will be passed and opening method
        open(module.view, transition: transition)
    }
    
    // This is how the function to push Profile in current Navigation stack
    func pushProfile() {
        let transition = PushTransition()
        let module = ProfileModule(transition: transition)
        open(module.view, transition: transition)
    }
}

// 4️⃣ And... that's all - you can call the router methods in your `UIViewController`
// in a convenient way for you, like this:
profileButton.addAction(
    UIAction(handler: { [weak self] _ in
        self?.router.presentProfile()
    }),
    for: .touchUpInside
)

🌠 Demo

For a better understanding of how to work with , please check out the demo project.Transition

🦖 For more

  • The module’s native “push” and “present” are already defined in and , just pass an instance of them to the method.PushTransitionModalTransitionopen(_ : UIViewController, : Transition)

  • In addition, there is which replaces the current controller with a new one using the methodEmbedTransitiondidMove(toParent _: UIViewController?)

  • For a modal view, you can pass configuration in the BottomSheetProps object. It is supported to change all (or at least the most important) settings that will ensure the correct behavior of the screen.

// Each property replicates native settings in UIKit, you shouldn't have
// not understanding how it works. Just in case, for the BottomSheetProps properties,
// their documentation is duplicated
let transition = ModalTransition(
    isAnimated: true,
    isNeedToEmbedInNavigationController: false,
    modalPresentationStyle: .pageSheet,
    bottomSheetProps: BottomSheetProps(
        doNotCloseOnDrag: false,
        detents: [
            .large(),
            .medium()
        ],
        selectedDetentIdentifier: .medium,
        prefersScrollingExpandsWhenScrolledToEdge: true,
        largestUndimmedDetentIdentifier: .medium,
        preferredCornerRadius: 32,
        prefersGrabberVisible: true
    )
)
  • If your project already uses , or any other screen opening animator, you can use it in conjunction with . Just implement your own version of the Transition protocol. Like that:PanModalFloatingPanelTransition

import FloatingPanel
import UIKit

final class ModalFloatingPanelTransition: NSObject {
    weak var viewController: UIViewController?
    weak var floatingPanel: FloatingPanelController?
}

// MARK: - Transition

extension ModalFloatingPanelTransition: Transition {
    func open(_ viewController: UIViewController) {
        guard let selfController = self.viewController else { return }

        guard
            let delegate = viewController as? FloatingPanelControllerDelegate
        else {
            assertionFailure("`\(viewController.self)` must conform `FloatingPanelControllerDelegate`")
            return
        }

        let fpc = FloatingPanelController()
        floatingPanel = fpc
        delegate.floatingPanel = fpc
        fpc.set(contentViewController: viewController)
        delegate.floatingPanelDidCreate(fpc)
        
        // Other FloatingPanelController settings if needed

        let controller = selfController.presentedViewController ?? selfController
        controller.present(fpc, animated: animated) { [weak delegate, weak fpc] in
            guard
                let delegate = delegate,
                let fpc = fpc
            else {
                return
            }
            delegate.floatingPanelDidPresent(fpc)
        }
    }

    func close(_ viewController: UIViewController) {
        close(viewController, completion: {})
    }

    func close(_: UIViewController, completion: @escaping () -> Void) {
        floatingPanel?.dismiss(animated: animated) { [weak self] in
            self?.floatingPanel?.set(contentViewController: nil)
            completion()
        }
    }
}

GitHub

点击跳转