金博注册app下载

在OkCupid工作,188bet金宝搏官网我学到了很多关于约会的科学。然而,每次我要去约会的时候,我总是不知道该穿什么。也许是纯色t恤配卡其裤,也许是深色牛仔裤配礼服衬衫?.关键是,组织数据并使用正确的自定义布局来呈现它是很困难的。
当涉及到你的约会生活时,我可能不知道所有的答案。然而,我可以提供一些关于如何编写优雅、可维护和可测试的代码的指导UICollectionView
代码(哪个比室内设计师更便宜).为了实现这个目标,我们将使用命令模式为了创建一个抽象,使我们能够在不知道单元格的具体类型的情况下对它们进行队列、配置和处理。
UICollectionView
提供了一个非常灵活的工具UIKit
.苹果将其定义为
一个对象,它管理一组有序的数据项,并使用可定制的布局来显示它们。
只要您以相同的方式表示数据,它们可以相当直接。UICollectionViewDataSource
和UICollectionViewDelegate
当我们注册多个时,复杂性会增加UICollectionViewCell
,它们需要以自己独特的方式配置。If you've been working with iOS for a while, I'm pretty sure you have witnessed horrifying ( ) implementations offunc collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: indexPath) -> UICollectionViewCell
.
在Ok188bet金宝搏官网Cupid,我们一直在探索写出优雅、可维护和可测试的代码的新方法UICollectionView
代码。为了实现这个目标,我们将使用命令模式为了创建一个抽象,使我们能够在不知道单元格的具体类型的情况下对它们进行队列、配置和处理。
用户提要
在我们的示例项目,我们将把用户和广告一起显示在feed中。这是当今许多开发者所面临的普遍情况。

提要中显示的异构内容如下:
- 常规用户将使用快照细胞(见绕口令).它有一个背景图像,头像和用户名的标签。
- 特性用户将使用圆形细胞(见书呆子).它只有一个头像和用户名的标签。
- 广告将与我们的用户交织在一起。对于MVP,我们只会包括图片广告.
让我们开始工作☺️!!
分离细胞
要解耦集合视图单元格的配置和数据源/委托,我们需要使用下面详述的抽象。
命令模式
这种行为设计模式将请求封装到一个对象中,以便将具体实现与调用者解耦。
此模式是简化单元配置、取消或任何其他特定请求的关键。
协议CollectionViewCellCommand {
函数执行(细胞:UICollectionViewCell)
}
考虑到UICollectionViewCell
S是可重用的,命令需要接受一个单元格作为参数。
视图模型
的CollectionViewCellViewModel
包含出队、显示和与我们的单元格交互所需的所有信息。
struct CollectionViewCellViewModel {
让标识符:字符串
让大小:CGSize
let命令:[CollectionViewCellCommandKey: CollectionViewCellCommand]
}
enum CollectionViewCellCommandKey {
情况下配置
情况下取消
病例选择
//你可以添加更多的案例来处理取消选择或任何其他交互
}
所有关于行动的具体细节都在命令
,它允许我们的视图模型被用于任何UICollectionView
与任何类型的基础模型
(低耦合不是很酷吗?)这意味着视图模型是独立的任何类或行动.
异构数据是条件分支语句的主要驱动因素,因为由于业务需求或仅仅是数据不兼容,每种情况都需要以不同的方式处理。CollectionViewCellViewModel
成为事实上的标准表示任何数据或行为需要通过可视化表示UICollectionView
.
命令使用方法:
它们真的很容易使用!CollectionViewCellCommand
可以在任何上下文中执行,只要传入适当的参数。方法中指定的方法UICollectionViewDataSource
或UICollectionViewDelegate
协议。
配置命令
的标识符
的CollectionViewCellModel
用于将适当的单元格从UICollectionView
.视图模型中的特定命令可以通过使用CollectionViewCellCommandKey
,在这种情况下.configuration
关键。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: indexPath) -> UICollectionViewCell {let viewModel = viewModels[indexPath。// cell = collectionView.dequeueReusableCell(withReuseIdentifier: viewModel.); // cell = collectionView.dequeueReusableCell(withReuseIdentifier: viewModel.)viewModel.commands[.configuration]?. execute (cell: cell) return cell}
请注意,可选的链接安全处理失踪案命令
为.configuration
关键。
选择命令
选择命令的过程包括获取视图模型
对于一个IndexPath
然后使用合适的CollectionViewCellCommandKey
.的.selection
键对应didSelectItemAt
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: indexPath) {
让cell = collectionView。cellForItem(at: indexPath) else {
返回
}
let viewModel = viewModels[indexPath.item]
viewModel.commands [.selection] ? .perform(细胞:细胞)
}
宏命令
在示例项目中,当用户单元格被选中时,我们推送一个视图控制器。如果我们也想要跟踪一个分析事件,我们可以把它们放在一个命令中,但这将打破[单一责任原则](https://en.wikipedia.org/wiki/SOLID_ (object-oriented_design).为了避免这个陷阱,我们可以创建一个宏命令包含多个命令的数组。
struct CollectionViewCellMacroCommand: CollectionViewCellCommand {
private let命令:[CollectionViewCellCommand]
init(命令:[CollectionViewCellCommand]) {
self.commands =命令
}
func执行(cell: UICollectionViewCell) {
命令。forEach{$ 0。执行(细胞:细胞)}
}
}
用户快照细胞
一个工厂作为创建视图模型的理想抽象快照细胞.在其中,我们可以指定大小
和标识符
和其他细胞一样命令
.
struct UserSnapshotCollectionViewCellViewModelFactory {
func create(user: user) -> CollectionViewCellViewModel {
let size = CGSize(width: 300, height: 200)
let configurationCommand = UserSnapshotCollectionViewCellConfigurationCommand(user: user, imageNetworkManager: imageNetworkManager ())
let命令:[CollectionViewCellCommandKey: CollectionViewCellCommand] = [
.configuration: configurationCommand
//附加的(CommandKey, Command)键值对可以在这里添加,以解决不同的场景(选择,取消选择,等等)
]
返回CollectionViewCellViewModel(标识符:“UserSnapshotCollectionViewCell”,大小:大小,命令:命令)
}
}
配置用户快照细胞,我们需要创建一个类型,坚持CollectionViewCellCommand
协议。命令通常用状态需要执行请求,这意味着我们需要将所有依赖注入到它们中。
⚠️==考虑潜在的保留周期时注射对象进命令.记住要使用弱
适当时==⚠️
struct UserSnapshotCollectionViewCellConfigurationCommand: CollectionViewCellCommand {
//执行命令所需的内部状态
private let user:用户
private let imageNetworkManager: ImageNetworkManagerProtocol
init(user: user, imageNetworkManager: ImageNetworkManagerProtocol = imageNetworkManager ()) {
自我。用户=用户
自我。imageNetworkManager = imageNetworkManager
}
//这就是奇迹发生的地方
func执行(cell: UICollectionViewCell) {
守卫让cell = cell as?其他UserSnapshotCollectionViewCell {
返回
}
cell.usernameLabel.text = user.username
_ = imageNetworkManager。请求(url: user.avatarUrl){(图像)在
cell.avatarImageView.image =图像
}
如果让backgrounddurl = user。backgroundUrl {
_ = imageNetworkManager。请求(url: backgrounddurl){(图像)在
cell.backgroundImageView.image =图像
}
}
}
}
配置命令请求图像avatarImageView
和backgroundImageView
通过imageNetworkManager
.它还会更新usernameLabel
.
我们现在可以使用工厂在UsersViewController
来变换用户CollectionViewCellViewModel
.
类UsersViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@IBOutlet弱var collectionView: UICollectionView!{
didSet {
collectionView.delegate =自我
collectionView。数据Source = self
}
}
var viewModels = [CollectionViewCellViewModel]()
覆盖func viewDidLoad() {
super.viewDidLoad ()
setupViewModels ()
}
func setupViewModels () {
let userFactory = UserSnapshotCollectionViewCellViewModelFactory()
让userViewModels = User.all.map(userFactory.create)
视图模型。追加(contentsOf userViewModels):
}

广告单元
为了表示广告,我们将使用结构体
包含一个contentUrl
和一个clickthroughUrl
.
struct广告{
让id: Int
让contentUrl: URL
让clickthroughUrl: URL
让类型:AdvertisementType
/ /测试数据
静态var all:[广告]{
返回(
广告(id: 1, contentUrl: URL(字符串:“https://cdn.okccdn.com/media/img/hub/mediakit/okcupid_darkbg.188bet金宝搏官网png”)!,点击通过URL: URL(字符串:“https://okcupid.com/h188bet金宝搏官网ome”)!类型:.image)
]
}
}
enum AdvertisementType {
案例图片
案例视频
情况下音频
}
图像广告将由视觉上的AdvertisementCollectionViewCell
,其中只包含一个UIImageView
.单击此单元格时,单击通过url将由UIApplication
.的SelectionCommand
要求UIApplication
和广告
来执行。
进口UIKit
struct AdvertisementCollectionViewCellSelectionCommand: CollectionViewCellCommand {
private let application: UIApplication
私人出租广告:广告
init(advertisement: advertisement, application: UIApplication) {
自我。应用=应用
自我。广告=广告
}
func执行(cell: UICollectionViewCell) {
如果application.canOpenURL (advertisement.clickthroughUrl) {
application.open(广告。clickthroughUrl,options: [:], completionHandler: nil)
}
}
}
的AdvertisementCollectionViewCellViewModelFactory
是负责改造的一个广告
成一个CollectionViewCellViewModel
.就像在用户
->视图模型
例中,工厂定义大小
,标识符
和支持命令
.
struct AdvertisementCollectionViewCellViewModelFactory {
func create(advertisement: advertisement, application: UIApplication) -> CollectionViewCellViewModel {
let size = CGSize(width: 220, height: 220)
let configurationCommand = AdvertisementCollectionViewCellConfigurationCommand(广告:广告,imageNetworkManager: imageNetworkManager ())
let selectionCommand = AdvertisementCollectionViewCellSelectionCommand(广告:广告,应用:应用)
let命令:[CollectionViewCellCommandKey: CollectionViewCellCommand] = [
.configuration: configurationCommand,
.selection: selectionCommand
]
返回CollectionViewCellViewModel(标识符:"AdvertisementCollectionViewCell",大小:大小,命令:命令)
}
}
将这些更改合并到UsersViewController
,我们可以修改setupViewModels ()
函数通过添加几行:
let advertisementFactory = AdvertisementCollectionViewCellViewModelFactory()
let ads = advertising .all.map{广告工厂。/ /创建一个应用程序
视图模型。insert(contentsOf: ads, at: viewModels.)数/ 2)

结论
通过应用命令模式抽象,通过将每个组件分离到服务于单一的目的.拥有只做一件事的类可以减少软件工程师对其进行推理的脑力负担,对于那些不熟悉代码库的人来说更是如此。由于低耦合,它还有助于包含单元测试模型
,视图
和控制器
层。
您可能已经注意到,添加不同类型的细胞几乎没有改变ui
.的命令
模式也为我们打开了创造的大门可撤销的操作,如果集合视图拥有支持清除挂起更改的可修改输入字段的单元格,这将非常方便。
闭包正变得越来越流行,函数式编程的采用比以往任何时候都要高。然而,命令是回调函数的面向对象替换,它们是可以操作和扩展的第一类对象。
这就是UserViewController
在应用了所有的更改之后。
进口UIKit
类UsersViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@IBOutlet弱var collectionView: UICollectionView!{
didSet {
collectionView.delegate =自我
collectionView。数据Source = self
}
}
var viewModels = [CollectionViewCellViewModel]()
覆盖func viewDidLoad() {
super.viewDidLoad ()
setupViewModels ()
}
func setupViewModels () {
let userFactory = UsersCollectionViewCellViewModelFactory()
let advertisementFactory = AdvertisementCollectionViewCellViewModelFactory()
让userViewModels = User.all.map {userFactory。create(user: $0, viewController: self)}
视图模型。追加(contentsOf userViewModels):
let ads = advertising .all.map{广告工厂。/ /创建一个应用程序
视图模型。insert(contentsOf: ads, at: viewModels.)数/ 2)
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
返回viewModels.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: indexPath) -> UICollectionViewCell {
let viewModel = viewModels[indexPath.item]
// cell根据绑定到视图模型的标识符退出队列
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: viewModel.);标识符:indexPath)
viewModel.commands [.configuration] ? .perform(细胞:细胞)
返回单元格
}
/ /马克:UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: indexPath) {
让cell = collectionView。cellForItem(at: indexPath) else {
返回
}
let viewModel = viewModels[indexPath.item]
viewModel.commands [.selection] ? .perform(细胞:细胞)
}
func collectionView(_ collectionView: UICollectionView, didenddisplays cell: UICollectionViewCell, forItemAt indexPath: indexPath) {
let viewModel = viewModels[indexPath.item]
viewModel.commands [.cancellation] ? .perform(细胞:细胞)
}
func collectionView(_ collectionView: UICollectionView,布局collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: indexPath) -> CGSize {
//视图模型定义了合适的大小
返回视图模型indexPath.item .size
}
}
你怎么看命令模式?你会在下一个项目中使用它吗?加入下面的对话reddit !
188bet金宝搏官网OkCupid正在招聘iOS用户点击这里了解更多
最初发表在https://tech.188bet金宝搏官网okcupid.com2017年10月23日。