美文网首页swift
iOS StoryBoard 优雅创建控制器方式

iOS StoryBoard 优雅创建控制器方式

作者: 幻梦者8805 | 来源:发表于2018-08-12 22:15 被阅读47次

    StoryBoard优雅创建控制器方式

    我的项目中使用到了StoryBoard, 我也是喜欢上了这种创建页面的方式, 所见即所得, 每当产品让我改页面的时候, 我都说分分钟就好. 不用在一坨代码中找一个label, 然后修改其颜色, 改颜色几秒钟, 找代码却找了两分钟.

    StoryBoard的优点

    • 创建快, 容易上手
    • 页面直观
    • 页面逻辑关系清晰
    • TableViewController的Static.Cell功能

    StoryBoard的缺点

    • StoryBoard内容多的时候加载慢, 这个是所有程序员所不能忍受的.
    • StoryBoard的segue乱, 页面少的时候还好, 一旦多起来,那个线会像蜘蛛网一样, 蜘蛛网还好, 还是有规律的, StoryBoard就是乱的不得了了
    • StoryBoard的segue不灵活, 使用起来比较麻烦, 需要配置segue.identifier, 属于是硬编码, 有时候很容易配置错
    • 页面间传递参数的时候麻烦, 一个方法里写跳转逻辑, 另一个方法里写参数传递, 一个控制中多个跳转的时候, 还要判断segue.identifier 这种写法我真的是受不了.
    • UIStoryBoard方式创建麻烦, 创建控制器之前先创建一个UIStoryBoard对象, 然后在根据StoryBoard.identifier去创建, 标识符的配置,也是硬编码.

    优雅的使用StoryBoard

    基于以上的啰嗦, 我在想是不是可以, 像纯代码创建控制器的方式那样创建StoryBoard的VC呢, 像 CustomViewController.creatVC() 这样是不是就和原来的用法一样了, 既然想到了, 那就去实现以下吧, 正好可以用Swift来实现一下. 想要的效果

    let vc = CustomViewController.creatVC()
    vc.model = model
    navigationController?.pushViewController(vc, animated: true)
    

    我的思路

    • 分模块拆分StoryBoard, 这样可以减少StoryBoard的体积, 避免加载慢的问题

    • segue只是做业务之间的逻辑显示, 不用它来做跳转, 或者不用它

    • StoryBoard.identifier的约定, 约定所有的StoryBoard.identifier为控制器类名, 方便统一创建VC, 也不用纠结叫什么, 主要也是怕失误的时候写错, 我一般是写上类名的时候, 直接copy一下类名放到identifier上.

        let sb = UIStoryboard(name: "storyBoardName", bundle: nil)
        let vc = sb.instantiateViewController(withIdentifier: "storyboardId") as! CustomerController
      
    • 根据以上代码分析出需要, storyBoardName, storyboardId, 两个变量, storyboardId就是类名, 可以直接通过类名获得, 但是这个storyBoardName怎么获取呢?

    • 通过属性, 好像是不行, 因为创建控制之前还没有属性, 类属性, 我也不想每个控制器都写一下, 而且也觉得不是一个好的方法, 接下来我就想到了用协议, 协议中定义一个方法, 让它返回StoryBoard name.

    我的实现

    /// 这里可以把StoryBoard以模块划分,
    enum StoryBoardName: String {
        case none = ""
        case main = "Main"
        case project = "Project"
        case login = "Login"
        case other = "Other"
        case mine = "Mine"
    }
    
    protocol StoryBoardDelegate {
        static func storyBoardName() -> StoryBoardName
    }
    
    protocol ClassNameDelete {
        static func className() -> String
    }
    
    
    extension UIViewController: ClassNameDelete {
        static func className() -> String {
            if let className = classForCoder().description().components(separatedBy: ".").last {
                return className
            } else {
                return classForCoder().description().components(separatedBy: ".").first!
            }
        }
    }
    
    
    extension BaseViewController {
    
        /// 通用创建VC方式
        class func creatVC() -> Self {
            
            let type = self.storyBoardName()
            if type == .none {
                return self.init()
            } else {
                if let vc = UIStoryboard.initVC(classType: self) {
                    return vc
                } else {
                    debugPrint("className=\(className())创建失败")
                    return self.init()
                }
            }
        }
    }
    
    extension UIStoryboard {
        /// 根据类名创建 由StoryBoard 创建的控制器.
        class func initVC<T>(classType: T.Type) -> T? where T: ClassNameDelete, T: StoryBoardDelegate {
            let name = T.storyBoardName()
            let sb = UIStoryboard.init(name: name.rawValue, bundle: nil)
            let vc = sb.instantiateVC(classType: classType)
            return vc
        }
        
        func instantiateVC<T>(classType: T.Type) -> T? where T: ClassNameDelete {
            let className = T.className()
            let vc = instantiateViewController(withIdentifier: className)
            return vc as? T
        }
    }
    

    具体使用

    /// 在基类中实现这个协议
    class BaseViewController: UIViewController, StoryBoardDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
         
        class func storyBoardName() -> StoryBoardName {
            return .none
        }
    } 
    
    /// 子类中实现, 若是纯代码方式创建则不需要实现这个方法.
    class TestView1Controller: BaseViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            title = "TestView1"
        }
    
        override static func storyBoardName() -> StoryBoardName {
            return .main
        }
        
        @IBAction func btnToTest2Action(_ sender: UIButton) {
            let vc = TestView2Controller.creatVC()
            navigationController?.pushViewController(vc, animated: true)
        }
    }
    

    总结

    自我感觉使用起来比较方便, 若是谁有更好的方式, 或可以优化的地方, 欢迎交流.
    代码Demo

    相关文章

      网友评论

        本文标题:iOS StoryBoard 优雅创建控制器方式

        本文链接:https://www.haomeiwen.com/subject/gckebftx.html