应用一 - 代码“逻辑汇总”:
很多实际场景下,由于业务复杂程度,可能会将独立的业务拆分至不同的类中实现,单独的Class来维护独立业务各自的代码逻辑,需要将分散至各分类的代码逻辑判断汇总至一处来判断;对于Swift这门面向协议的语言来说,也是可以处理得十分的优雅
还是以交易页面来做栗子:
用户输入界面
class userInputViewController: UIViewController, CCBATradeVerifyProtocol {
.....
func doVerify() -> Bool {
//验证用户的输入是否通过合法校验
return true
}
}
//用户的账户信息class
class UserAccountController: UIViewController, CCBATradeVerifyProtocol {
......
func doVerify() -> Bool {
//验证资金是否满足条件
return true
}
}
//股票信息class
class StockMarketViewController: UIViewController, CCBATradeVerifyProtocol {
......
func doVerify() -> Bool {
//验证股票数据是否正常
return true
}
}
//汇总的根页面
class TradeRootViewController: CCBATradeVerifyProtocol {
......
// 下单接口调用逻辑
func doRequestTradeOrder() {
// 如果条件不满足,则不发送请求至后台
guard getVerifyResult() else { return }
/* 提交订单信息至交易所 */
}
//交易下单的前提条件是否全部满足(账户、股票、用户输入的验证条件分散在三个不同的class中)
func getVerifyResult() -> Bool {
var isVerify: Bool = true
// 遍历所有实现 CCBATradeVerifyProtocol协议的子class 来“汇总”所有的条件
for item in authProcessors {
if !item.doVerify() {
isVerify = false
break
} else { /* do nothing */ }
}
return isVerify
}
var authProcessors: [CCBATradeVerifyProtocol] = []
}
应用二 - 代码复用:
所有的界面具有同一种功能,且功能实现的逻辑代码完全一致,以 protocol 进行代理,并用作接口的抽象(通过extension Protocol来对目标方法的实现)进行代码复用,对于拥有这些功能的类(如view 或者 viewController )直接实现该protocol,便自动拥有此功能
栗子:数据缓存
以protocol抽象一个文件缓存功能逻辑
// 文档抽象类
protocol DocumentAbstract: class {
/// 获取当前路径下的所有的子路径,并按照文件夹 --> 图片 --> 视频排序
/// - Parameter path: 当前路径
func getTotalCacheModel(at path: String) -> [FCFileBaseModel]?
/// 获取用于展示用的文件夹类型的item
/// - Parameter totalModels: 传入原数据模型组队列
func getDocItems(_ totalModels: [FCFileBaseModel]) -> [YFCollectionItem]
/// 获取媒体类Item
/// - Parameter totalModels: 传入原数据模型组队列
func getMediaItems(_ totalModels: [FCFileBaseModel]) -> [YFCollectionItem]
/// 获取占位的item
func getPlaceHolderItem() -> YFCollectionItem
//长按的工具
func showTools(_ model: FCFileBaseModel)
}
拓展DocumentAbstract中的协议方法(即默认实现)
extension FCDocumentFileAbstract {
func getTotalCacheModel(at path: String) -> [FCFileBaseModel]? {
let subDirectorys = YFFileManager.shared.subDicrsOfDirectory(at: path)
guard !subDirectorys.isEmpty else {
return nil
}
//重新排序,文件夹 --> 图片 --> 视频
let totalModels: [FCFileBaseModel] = subDirectorys.compactMap { (title) -> FCFileBaseModel in
let components = title.components(separatedBy: ".")
var category: FCFileCategory = .doc
components.forEach({ (component) in
let mediaMaps: [String: FCFileCategory] = ["jpeg": .image,
"png": .image,
"heic": .image,
"mp4": .video,
"mov": .video,
"pdf": .pdf,
"txt": .txt]
category = mediaMaps[component.lowercased()] ?? .unknown
})
let cachePath = path + "/\(title)"
return FCFileBaseModel(path: cachePath, category: category, isShowDelete: false, title: title)
}.sorted { (model0, model1) -> Bool in
return model0.category.rawValue < model1.category.rawValue
}
return totalModels
}
//MARK: - 获取文件类item
func getDocItems(_ totalModels: [FCFileBaseModel]) -> [YFCollectionItem] {
let width: CGFloat = UIScreen.main.bounds.width / 2.0 - 20.0
let docItems: [YFCollectionItem] = totalModels.filter {
$0.category == .doc
}.compactMap { (subModel) -> YFCollectionItem in
let cellItem: YFCollectionItem = (kFCFileDocumentFolderCollectionViewCell, CGSize(width: width, height: 86.0), subModel, { _ in
let fileVC = FCDocumentBaseCollectionViewController(subModel)
fileVC.isRootVC = false
YFApp.UI.pushViewController(fileVC, animated: true)
})
return cellItem
}
return docItems
}
//MARK: - 获取媒体类Item
func getMediaItems(_ totalModels: [FCFileBaseModel]) -> [YFCollectionItem] {
let itemSize = CGSize(width: 68.0, height: 68.0)
let mediaModels: [FCFileBaseModel] = totalModels.filter { $0.category != .doc }
let mediaItems: [YFCollectionItem] = mediaModels.compactMap { (subModel) -> YFCollectionItem in
subModel.clipClosure = { [weak self] in
guard let strongSelf = self else { return }
let category = subModel.category
if category == .image {
strongSelf.openImage(model: subModel, total: mediaModels)
} else if category == .video {
let fileurl = URL(fileURLWithPath: subModel.path)
let player = YFPlayerViewController(url: fileurl)
player.play()
} else if category == .pdf {
let vc = YFQLPreviewController(path: subModel.path, title: subModel.title)
YFApp.UI.present(vc, animated: true, completion: nil)
} else if category == .txt {
let vc = FCTxtReaderViewController(title: subModel.displayTitle, path: subModel.path, isEditable: true)
let nav = UINavigationController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
YFApp.UI.present(nav, animated: true, completion: nil)
} else { /* do nothing */ }
}
subModel.longPressClosure = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.showTools(subModel)
}
let cellItem: YFCollectionItem = (kZWFileDocumentCollectionCell, itemSize, subModel, nil)
return cellItem
}
return mediaItems
}
func getPlaceHolderItem() -> YFCollectionItem {
let width: CGFloat = UIScreen.main.bounds.width
let cellItem: YFCollectionItem = (kFCPlaceholderCollectionViewCell, CGSize(width: width, height: 190.0), nil, nil)
return cellItem
}
private func openImage(model: FCFileBaseModel, total: [FCFileBaseModel]) {
var selected: Int = 0
let filterModels = total.filter { $0.category == .image }
//仅仅轮播图片
for (index, subModel) in filterModels.enumerated() {
if subModel == model {
selected = index
break
}
}
let photos: [SKPhoto] = filterModels.compactMap { (model) -> SKPhoto in
return SKPhoto.photoWithImage(model.displayImage)
}
let browser = SKPhotoBrowser(photos: photos)
browser.initializePageIndex(selected)
YFApp.UI.present(browser, animated: true, completion: nil)
}
}
class AnyYourClass: FCDocumentFileAbstract {}
AnyYourClass本身已默认实现FCDocumentFileAbstract相关的方法,只需要直接调用即可
应用三 - 约束功能
如目录中的其他两篇文章 《数据驱动之 UICollectionViewController》和《数据驱动之 UITableViewController》中对 用来展示用的cell必须要实现特定的Protocol才能用来注册tableView/collectionView, 从而所有的cell都具有相同的协议方法,基类在渲染cell时仅需在统一代码块中调用特定的方法,无需子类重复实现!
网友评论