我想知道您是如何在使用Swift开发IOS时正确尊重MVC设计模式的我现在的感觉是,视图控制器混合了控制器部分和视图部分,但感觉不对,对吧?为了尊重MVC,我们显然需要将控制器和视图分开你是怎么做的也许,这在MVVM或MVP等其他设计模式中更为明显?
您可以分离您的项目,并创建一个结构,其中所有逻辑和模型都在一个位置,所有viewControllers也在一个地方。
例如:
Core
Models
Person.swift
Car.swift
Helpers
Extensions.swift
APIHelper.swift
Webservice
Webservice.swift
Controllers
ViewController.swift
SecondViewController.swift
因此,您基本上在Core
中拥有所有逻辑和计算,在Controllers
中拥有所有视图和UI元素。通过这种方式,您不必多次执行相同的逻辑代码。您也可以创建自定义视图,并将它们添加到Core
中,稍后可以在Controllers
中调用。
虽然回答这样的问题可能过于宽泛,但我将回答您关于的具体问题:
视图控制器混合了控制器部分和视图部分。。。
请注意,在处理iOS项目时,它会隐式地引导您应用MVC模式。默认情况下,视图控制器表示Controller
部分,.stebox和.xib文件表示View
部分,用于封装数据的任何模型对象(数据模板)表示Model
。
我现在的感觉是视图控制器混合了控制器部分和视图部分,但感觉不对吧?
视图控制器有许多职责需要处理,除了解释用户操作外,它还应该是视图和模型之间的中介,不要忘记处理与web服务的集成。。。这就是大规模视图控制器的问题。
如果你试图做一些关于解决这个问题的研究,你会发现有很多方法可以遵循,比如应用其他结构模式,比如MVVM、MVP,VIPER或Clean Architecture或者甚至更简单的方法,比如划分项目文件以增加作业的独立性,从而使其更清晰、更容易跟踪,MVC-N可能是一个很好的例子。
但对于您询问的特定情况(混合控制器部分和视图部分),请保持简单:我建议根据视图分离数据表示的逻辑,例如:
构建iOS项目时最流行的情况之一是使用表视图及其单元格,请考虑以下内容:
ViewController.swift:
class ViewController: UIViewController {
// ...
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myDataSourceArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellID") as! MyCustomCell
// ...
return cell
}
}
MyCustomCell.swift:
class MyCustomCell: UITableViewCell {
@IBOutlet weak var lblMessage: UILabel!
}
现在,想象一下,在这种情况下,需要基于一系列复杂的计算来更改单元格中的lblMessage
标签文本颜色:
不要这样做:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellID") as! MyCustomCell
// do calulations which might needs tens of lines to be achieved, based on that:
cell.lblMessage.textColor = UIColor.red
return cell
}
这导致视图控制器变得庞大,并包含大量作业,而不是:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellID") as! MyCustomCell
//...
cell.doCalaulations()
return cell
}
在单元格类中:
class MyCustomCell: UITableViewCell {
@IBOutlet weak var lblMessage: UILabel!
func doCalaulations() {
// do calulations which might needs tens of lines to be achieved, based on that:
blMessage.textColor = UIColor.red
}
}
这导致项目组件更加封装,重要的是视图控制器不需要来处理整个事情。对我来说,在与此类似的情况下,我甚至更愿意将blMessage
设置为private
,这保证了它只能从所有者类中编辑(更多的封装),以处理任何需要的行为,因此任何视图控制器都应该调用类方法,而不是直接访问其属性和IBOutlets。
将常用的ViewController
分为两个不同的部分:View
和Presenter
。View
响应仅用于显示数据和收集用户交互。Presenter
准备用于查看和处理来自View
的用户动作的数据。
这个想法源于Bob叔叔的Clean Architecture,并在VIPER架构中实现。
View
有两个协议:
ViewInput
:包含用于传递数据以显示的函数,如set(labelText: String)
。该协议应实现View
。CCD_ 21具有类型为CCD_ViewOutput
:包含函数,当视图中发生某些事件(如viewDidLoad()
或rowDidSelect(at: IndexPath)
)时调用这些函数。该协议应实现Presenter
。CCD_ 27具有类型为CCD_
VIPER不是一件小事,我花了几天时间来了解它的原理。因此,请阅读文章并尝试在代码中实现它。不要犹豫,提出问题。