使用协议和MVVM的可重用视图布局

想法💡

之所以将其命名为“可重用视图布局..”而不是简单地命名为“可重用视图..”,是因为在这里我不是指视图的对象级可重用性。 相反,我将讨论如何在protocolMVVM的帮助下重用视图布局。 在这个过程中,我将分享我们如何使用一个protocol概述一个视图的界面, 基本不同的配置状态 ,该protocol将使我们能够将不同的view-models注入到一个视图中。 换句话说,这里的想法是设计视图布局的数据不可知模型。

MVVM

在继续之前,让我们先简单了解一下MVVM 。 这是目前最令人困惑的设计模式之一,因此只需要确保我们在同一页面上即可。 这是一种设计模式,其中,我们使视图依赖于称为视图模型的中间模型对象。 这个视图模型获取一些数据(与网络或持久层无关),并应用一些装饰方法,这些方法最终被视图消耗。 例如,如果我们有一个配置文件视图类– ProfileView ,我们可以创建它的视图模型ProfileViewModel进行配置并保存其状态。

 class ProfileView: UIView { 

var nameLabel: UILabel!

func configure(with model: ProfileViewModel) {
nameLabel.text = model.nameTitle
}
}

struct ProfileViewModel {

var nameTitle: String { return "Profile Title" }
}

捆绑

我们可以看到,使用MVVM时, ProfileViewModelProfileViewModel结合在一起ProfileViewModel进行任何类型的配置。 如果您的视图在整个生命周期中仅具有一种视图模型,则此方法效果很好。 现在,我们要重用ProfileView类,该类应该能够显示Owner配置文件和Guest配置文件。

枚举

为了支持这一点,我们可以将ProfileViewModel转换成一个所有者和来宾情况不同的枚举。

 enum ProfileViewModel { 

case owner
case guest

var nameTitle: String {
switch self {
case .owner: return "Hi, Owner"
case .guest: return "Hi, Guest"
}
}
}

这个解决方案不会立刻看起来很糟糕,但是如果我们继续增加更多的案例,它最终将成为上帝的一等。 为了避免这种情况,我们可以修改关于MVVM的思考方式。 我建议不要使视图直接依赖于视图模型,而建议使视图的interface可以包含所有可配置的属性。 因此,我们需要一个视图就可以理解的东西,而实际上并不知道其传入的视图模型。

协议

为了实现这种“ 东西 ”,我们可以使用protocol 。 视图可以具有可以使用协议概述的接口。 该接口可以具有其视图的所有可配置状态。 最后,我们可以拥有符合它的不同视图模型。 这种方法使视图完全忽略了它可能期望的视图模型。 例如,

 class ProfileView: UIView { 

...

func configure(with interface: ProfileViewInterface) {
nameLabel.text = interface.nameTitle
}
}

protocol ProfileViewInterface {

var nameTitle: String { get }
}

因此,现在ProfileView仅意识到某些对象将对其进行配置并且符合ProfileViewInterface 。 这样做,我们已经将视图和视图模型之间的层分开了。 视图忽略了视图模型,但是视图模型知道它们需要配置哪个视图。 这提供了可伸缩的解决方案,因为不同的类现在可以符合ProfileViewInterface以便它们可以充当其视图的视图模型。 例如,

 class OwnerViewModel: ProfileViewInterface { 

var nameTitle: String { return "Owner Title" }
}

class GuestViewModel: ProfileViewInterface {

var nameTitle: String { return "Guest Title" }
}

class AdminViewModel: ProfileViewInterface {

var nameTitle: String { return "Admin Title" }
}

let view = ProfileView()

let ownerModel = OwnerViewModel()
let guestModel = GuestViewModel()
let adminModel = AdminViewModel()
 view.configure(...) 
 // here we can pass any of the three above view-models since they all conform to ProfileViewInterface 

因此,明天如果您打算添加另一种概要文件视图,则只需创建一个符合ProfileViewInterface的新视图模型类,然后将其注入ProfileView的configure方法中即可。