阅读 919

Swift-Router 自己写个路由吧,第三方总是太复杂

先看看这个路由的使用吧
  1. 如果是网络地址,会直接自动跳转到 OtherWKWebViewController
  2. 如果是应用内部的手动调用跳转
  • 直接跳转视图控制器
    • EPRouter.pushViewController(EPSMSLoginViewController())
  • 先在 RouteDict 注册映射关系再跳转
    • EPRouter.pushAppURLPath("goods/detail?spellId=xxx&productId=xxx")
  1. 又服务器来控制跳转 也得在 RouteDict 注册映射关系,只不过多加了一个 scheme
    • EPRouter.pushURLPath("applicationScheme://goods/detail?spellId=xxx&productId=xxx")

**!!!支持Swift、OC、Storyboard的跳转方式,可以在 loadViewController 看到实现方式 **

EPRouter的全部代码
class EPRouter: NSObject {

    private static let RouteDict:[String:String] = [
        "order/list"            :"OrderListPageViewController",   // 订单列表 segmentIndex
        "order/detail"          :"OrderDetailViewController",     // 订单详情 orderId
        "goods/detail"          :"GoodsDetailViewController",     // 商品详情productId
        "goods/list"            :"GoodsCategoryViewController",   // type brandId 跳转到某个分类;跳转到某个品牌
        "goods/search"          :"SearchListViewController",      // 搜索商品 text
        "coupon/list"           :"CouponListViewController",      // 优惠券列表
        "cart/list"             :"CartViewController",            // 购物车列表
        "address/list"          :"AddressListViewController",     // 收货地址列表
    ]
    
    
// 返回首页,然后指定选中模块
public static func backToTabBarController(index: NSInteger, completion:(()->())?=nil) {

    guard let vc = EPCtrlManager.getTopVC(), let nav = vc.navigationController, let tabBarCtrl = nav.tabBarController  else {
        return
    }

    nav.popToRootViewController(animated: false)
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+0.1) {
        tabBarCtrl.selectedIndex = index
        completion?()
    }
}


// 销毁n个界面 不建议使用这个方法 可以在pushAppURLPath方法中设置destroyTime达到一样的效果,又可以避免用户侧滑返回
public static func popViewController(animated: Bool, time:NSInteger=1) {

    guard let nav = EPCtrlManager.getTopVC()?.navigationController else {
        return
    }
    let vcs = nav.viewControllers
    let count = vcs.count
    let index = (count - 1) - time
    if index >= 0 {
        let vc = vcs[index]
        nav.popToViewController(vc, animated: true)
    } else {
        nav.popViewController(animated: true)
    }
}


    /// 回到目标控制器
    public static func popViewController(targetVC: UIViewController.Type, animated: Bool, toRootVC: Bool=true) {

        popViewController(targetVCs: [targetVC], animated: animated, toRootVC: toRootVC)
    }

    

    /// 回到目标控制器[vc],从前到后 没有目标控制器是否回到根视图
    public static func popViewController(targetVCs: [UIViewController.Type], animated: Bool, toRootVC: Bool=true) {

        guard let nav = EPCtrlManager.getTopVC()?.navigationController else {
            return
        }
        let vcs = nav.viewControllers
        var canPop = false
        for vc in vcs {
            for tvc in targetVCs {
                if vc.isMember(of: tvc) {
                    canPop = true
                    nav.popToViewController(vc, animated: animated)
                    break
                }
            }
        }
        if !canPop && toRootVC {
            nav.popToRootViewController(animated: animated)
        }
    }

    /// push 一个vc --- destroyTime: push之前要销毁的几个压栈vc
    @objc public static func pushAppURLPath(_ path: String, query: [AnyHashable: Any]=[:], animated: Bool=true, destroyTime:NSInteger=0) {

        var urlString = "applicationScheme://"+path
        if path.contains("http://") || path.contains("https://") {
            urlString = path
        }
        pushURLString(urlString, query: query, animated: animated, destroyTime: destroyTime)
    }


    @objc public static func pushURLString(_ urlString: String, query: [AnyHashable: Any]=[:], animated: Bool=true, destroyTime:NSInteger=0) {

        guard let tvc = loadViewControllerWitURI(urlString, query: query) else {
            return
        }
        pushViewController(tvc, animated: animated, destroyTime: destroyTime)
    }


    @objc public static func pushViewController(_ tvc: UIViewController, query: [AnyHashable: Any]=[:], animated: Bool=true, destroyTime:NSInteger=0) {

        guard let vc = EPCtrlManager.getTopVC() else {
            return
        }

        if let _ = tvc.pushInfo {
            tvc.pushInfo?.merge(query, uniquingKeysWith: { (_, new) in new })
        }else {
            tvc.pushInfo = query
        }
        guard let nav = vc.navigationController else {
            vc.present(tvc, animated: true, completion: nil)
            return
        }
        tvc.hidesBottomBarWhenPushed = true

        if destroyTime > 0 {
            let vcs = nav.viewControllers
            let count = vcs.count
            var index = (count - 1) - destroyTime
            if index < 0 { // destroyTime 很多时,直接从根视图push
                index = 0
            }

            var reVCS = [UIViewController]()
            for vc in nav.viewControllers[0...index] {
                reVCS.append(vc)
            }
            reVCS.append(tvc)
            nav.setViewControllers(reVCS, animated: animated)
        }else {
            nav.pushViewController(tvc, animated: animated)
        }
    }

    public static func loadViewController(_ className: String, parameters: [AnyHashable: Any]? = nil) -> UIViewController? {

        var desVC: UIViewController?
        let spaceName = (Bundle.main.infoDictionary?["CFBundleExecutable"] as? String) ?? "ApplicationName"

        if let vc = storyboardClass(className) { // storyboard
            desVC = vc
        }else if let aClass = NSClassFromString("\(spaceName).\(className)") { // Swift
            if aClass is UIViewController.Type {
                let type = aClass as! UIViewController.Type
                desVC = type.init()
            }
        }else if let aClass = NSClassFromString("\(className)") { // OC
            if aClass is UIViewController.Type {
                let type = aClass as! UIViewController.Type
                desVC = type.init()
            }
        }

        desVC?.pushInfo = parameters
        return desVC
    }


    public static func loadViewController(_ viewController: UIViewController, parameters: [AnyHashable: Any]? = nil) -> UIViewController {

        viewController.pushInfo = parameters
        return viewController

    }

    public static func loadViewControllerWitURI(_ urlString: String, query: [AnyHashable: Any]? = nil) -> UIViewController? {

        

        // 先进行编码,防止有中文的带入, 不行进行二次编码
        var urlString = urlString
        if (URLComponents(string: urlString) == nil) {
            urlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? urlString
        }

        guard let url = URLComponents(string: urlString), let scheme = url.scheme else {
            HGLog("无效的地址:\(urlString)")
            return nil
        }

        if scheme == "http" || scheme == "https" {

            let webVC = OtherWKWebViewController()
            webVC._urlStr = urlString
            return webVC

        } else if String(format: "%@://", scheme) == "appcationScheme://" {
            let path = (url.host ?? "") + url.path
            guard  var vcClassName = RouteDict[path] else {
                HGLog("没有配置视图控制器呢。。。:\(urlString)")
                return nil
            }

            var info: [AnyHashable: Any]?
            if query?.count ?? 0 > 0 {
                info = [AnyHashable: Any]()
                for (key, value) in query! {
                    info![key] = value
                }
            }

            if let queryItems = url.queryItems {
                if info == nil {
                    info = [AnyHashable: Any]()
                }
                for item in queryItems {
                    if let value = item.value {
                        info![item.name] = value
                    }
                }
            }
            return loadViewController(vcClassName, parameters: info)
        }

        HGLog("未知scheme:\(urlString)")
        return nil

    }

    

    private static func storyboardClass(_ className: String) -> UIViewController? {

        if className == "VIPWithdrawViewController" { // 提现
            let vc = UIStoryboard.init(name: "VIP", bundle: nil).instantiateViewController(withIdentifier: "withdrawTVC")
            return vc
        }else if className == "VIPRecordListViewController" { // 提现记录
            let vc = UIStoryboard.init(name: "VIP", bundle: nil).instantiateViewController(withIdentifier: "recordListVC")
            return vc
        }
        return nil
    }
}
复制代码
用来跳转传递数据的扩展属性
extension UIViewController {

    private struct PushAssociatedKeys {
        static var pushInfo = "pushInfo"
    }

    @objc open var pushInfo: [AnyHashable: Any]? {
        get {
            return objc_getAssociatedObject(self, &PushAssociatedKeys.pushInfo) as? [AnyHashable : Any]
        }
        set(newValue) {
            objc_setAssociatedObject(self, &PushAssociatedKeys.pushInfo, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}
复制代码
可见视图控制器的获取
class EPCtrlManager: NSObject {

    public static let `default`: EPCtrlManager = {
        return EPCtrlManager()
    }()

    // MARK: **- 查找顶层控制器、**
    // 获取顶层控制器 根据window
    @objc public static func  getTopVC() -> UIViewController? {

        var window = UIApplication.shared.keyWindow
        //是否为当前显示的window
        if window?.windowLevel != UIWindow.Level.normal{
            let windows = UIApplication.shared.windows
            for  windowTemp in windows{
                if windowTemp.windowLevel == UIWindow.Level.normal{
                    window = windowTemp
                    break
                }
            }
        }
        let vc = window?.rootViewController
        return getTopVC(withCurrentVC: vc)
    }

    ///根据控制器获取 顶层控制器
    private static func  getTopVC(withCurrentVC VC :UIViewController?) -> UIViewController? {

        if VC == nil {
            print("🌶: 找不到顶层控制器")
            return nil
        }

        if let presentVC = VC?.presentedViewController {
            //modal出来的 控制器
            return getTopVC(withCurrentVC: presentVC)
        }else if let tabVC = VC as? UITabBarController {
            // tabBar 的跟控制器
            if let selectVC = tabVC.selectedViewController {
                return getTopVC(withCurrentVC: selectVC)
            }
            return nil
        } else if let naiVC = VC as? UINavigationController {
            // 控制器是 nav
            return getTopVC(withCurrentVC:naiVC.visibleViewController)
        } else {
            // 返回顶控制器
            return VC
        }
    }
}
复制代码
文章分类
iOS
文章标签