iOS Swift优雅的拆分ViewController的View

MVC对于iOS开发的意义

对于iOS开发而言 始终无法绕开UIKit这个框架, 加之SwiftUI并不成熟, 所以你懂的, 而UIKit框架就是基于的MVC的设计模式, 所以这也是为什么MVC是苹果官方推荐的设计模式.

为什么我个人比较推荐项目中用MVC.

一. 官方推荐这个标签肯定是有一定分量的.
二. 在一个基于MVC的框架上强行使用MVVM或MVP的设计模式, 不是不可以, 但总有些地方会差强人意, 当你使用各种知名框架时就会体会到这种感觉, 当然你可以选择忽略不计, 但它们确实存在.
三. 简单, 非常的简单, 没有学习成本, 是个开发者就懂, 基本没有"交流"障碍. 越简单越利于维护(这也是我比较推崇的开发风格 一切从简).
四. 可扩展性强, 因为足够通用, 所以在MVC基础上可以根据具体业务需要转变成其他设计模式, 总的来说 整个项目的基础设计模式还是MVC, 根据不同业务模块情况可以再使用最合适的设计模式.

老生常淡 MVC的最大弊端

没错 臃肿的C层代码.
所以很多优化方案都是围绕这点展开的, 咱们也不例外.

UIViewController与UIView的纠葛

UIViewController在实际开发中会遇到很多问题, 这里我们只说View相关的问题.

按照苹果的设计理念:
UIViewController对应MVCC, UIView对应MVCV, XXXModel对应MVCM.

但尴尬的是 我们使用UIViewController时 不免会有很多View的处理在其中, 纯代码的方式还好一些, 可以通过封装自定义View类来解决, StoryboardXib的方式就尤为明显了.

纯代码:

自定义View类来编写视图相关的代码, 可以将V的处理从UIViewController中分离出去, 但是不免要在UIViewController中再次编写初始化 布局等代码. 试想每个UIViewController都要写一遍某个View的初始化 添加父视图 布局等.

Storyboard或XIB:

拖线链接的控件通常会在UIViewController中, 经常见到UIViewController中拖了一堆视图控件对象. 当然除了拖进来还要写一下其他视图相关的代码.
也有使用自定义View类来承载所有拖线链接的控件对象 和上面纯代码的方式差不多.

同样的困境:
UIViewController中view的类型永远都是UIView, 上面两种方式遇到的问题一样, 需要做类型转换才能访问到自定义View类中的属性和方法, 这无疑是很麻烦的.

Swift 泛型优雅的解决类型转换问题

  • 创建一个基类 (实际项目开发中应该要有一个ViewController基类, 并保证基类的干净, 这是一个很好的习惯)
class ViewController<Container: UIView>: UIViewController {

    var container: Container { view as! Container }
    
    override func loadView() {
        super.loadView()
        if view is Container {
            return
        }
        view = Container()
    }
}
  • 所有视图控制器都继承自该基类, 并明确声明该控制器所使用的View类型
class HomeController: ViewController<HomeView> {

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}
  • 通过container属性直接访问上面声明的自定义View类型对象, 当然你也可以改为其他名字
class XXXXController: ViewController<XXXXView> {

    // CODE
  
    override func viewDidLoad() {
        super.viewDidLoad()
        // CODE
        container.xxxx()
    }
}

使用演示:

纯代码:

class XXXXView: UIView {

    private lazy var titleLabel = UILabel()
    private lazy var iconImageView = UIImageView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        titleLabel.textColor = .black
        titleLabel.font = .systemFont(ofSize: 13, weight: .semibold)
        
        iconImageView.contentMode = .scaleAspectFill
        
        addSubview(titleLabel)
        addSubview(iconImageView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        titleLabel.frame = .init(x: 100, y: 100, width: 100, height: 40)
        iconImageView.frame = .init(x: 100, y: 100, width: 100, height: 100)
    }
    
    func set(title: String) {
        titleLabel.text = title
    }
    
    func set(image: UIImage) {
        iconImageView.image = image
    }
}
class XXXXController: ViewController<XXXXView> {

    private let model = XXXXModel()
    // CODE
  
    override func viewDidLoad() {
        super.viewDidLoad()
        // CODE
        container.set(title: model.title)
        container.set(image: model.image)
    }
}
let controller = XXXXController()
present(controller, animated: true)

Storyboard或XIB:

设置Controller类:

设置Controller类

设置View类:

设置View类

向View中拖线链接:

向View中拖线链接

在Controller中为视图设置数据:

为视图设置数据

总结

方法简单, 很好的解决了上面提到的这些问题, 使Controller与View的分离更加优雅.

class HomeController: ViewController<HomeView> { }

头部的声明可以直观的看到Controller的View类型, 可读性强.

因明确了类型 调用更加顺畅自然, 省去了多余的类型转换代码.

使 Controller 可以更专注于Model与View的协调和调用, 职责更明确.

截止现在, 这个方法我自己已经使用2年了 其中也经历了几个项目的洗礼, 还是没什么问题的, 大家如果感兴趣可以放心采纳.

Demo传送门

如果你有更好的想法 欢迎评论交流

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345