【发布时间】:2018-01-30 06:56:06
【问题描述】:
我是 MVVM 的新手,对如何避免在子视图控制器中对视图模型实例进行类型转换有点困惑。为了更好地解释它,我添加了下面的代码。
现在我知道每个 ViewController 都应该有一个视图模型,并且应该在 init 或 awakeFromNib 中进行初始化。所以我将协议声明为
public protocol ViewModelCreatorProtocol : class {
var viewModel : BaseControllerViewModel! {get}
func createViewModel()
}
我的BaseControllerViewModel 看起来像
public class BaseControllerViewModel {
//some variables common to all child class
}
现在我有一个BaseViewController,它确认了这个协议,在我的案例中,所有其他 VC 都将从 BaseViewController 扩展
class BaseViewController : UIViewController, ViewModelCreatorProtocol {
var viewModel: BaseControllerViewModel!
lazy var disposeBag : DisposeBag = {
return DisposeBag()
}()
override func awakeFromNib() {
super.awakeFromNib()
self.createViewModel()
}
override func viewDidLoad() {
super.viewDidLoad()
}
func createViewModel() {
viewModel = BaseControllerViewModel()
}
func hookupViewModelObservables() {
//hookup observables
}
}
如您所见,BaseViewController 为所有 childViewControllers 提供了一些公共属性,例如 disposeBag,并在 awakeFromNib 中调用 createViewModel,并确保即使在加载 ViewController 之前也始终实例化 viewModel。 (现在让我们假设我所有的 viewModel 都使用默认的 init 进行初始化,并且没有传递给它们的参数)
问题:
现在我创建一个 ChildViewController 让我们说AudioPlayerViewController
class AudioPlayerViewController: BaseViewController {
//override createViewModel of parent to pass my ViewController specific view model
override func createViewModel() {
self.viewModel = AudioPlayerViewModel()
}
}
显然我的AudioPlayerViewModel 是从BaseControllerViewModel 扩展而来的
class AudioPlayerViewModel : BaseControllerViewModel {
var totalDuration : Int = 0
var timeObserver : Any? = nil
//some more variables and logics
}
一切正常,但现在如果我必须在我的AudioPlayerViewController 中访问totalDuration,我必须以身份访问它
(self.viewModel as! AudioPlayerViewModel).totalDuration
这使得我在 ViewController 中的整个代码库都被(self.viewModel as! AudioPlayerViewModel) 填充,我认为这提高了我的代码可读性。
有没有更好的方法来解决这个问题?
【问题讨论】:
-
我的建议是避免使用基本视图控制器和视图模型。重用基类的一些属性或小方法所获得的最小好处远远超过了增加的复杂性。协议更好,但我不知道为什么需要 ViewModelCreatorProtocol。
-
@mike-taverne :问题是我正在尝试设置规则,即每个视图控制器都应该在加载之前初始化其视图模型。所以我认为协议在这里会做得更好。如果我删除了协议,那么创建视图模型将成为视图控制器的责任,但由于协议,它将强制所有子 VC 创建一个名为视图模型的变量,并提供通过覆盖 createViewModel 来提供自己的视图模型实例的机会。故事板的使用使得视图模型的动态注入变得困难
-
和基类从 nib 实现中唤醒确保所有子 VC 都有机会使用 awakeFromNib 中的实例初始化他们的 vie 模型
-
您的视图控制器如何知道在调用 awakeFromNib 时要加载哪个视图模型?我知道它知道什么类型的模型,但是它怎么知道要创建该类型的哪个实例呢?例如,如果您的视图控制器显示正在播放的曲目的持续时间,如果您没有告诉它要播放什么曲目,它怎么可能知道该持续时间?
-
@mike-taverne :我明白你在说什么,你说的是viewModel的空初始化。现在没关系,因为我已经在我的问题中提到过。我打算从情节提要中实例化一个视图控制器,然后访问其视图模型属性并将值分配给视图模型内的所有变量。我知道这不是正确的方法,但现在可行