内容简介:一直在写代码,什么是理想中的代码图片是《斯坦福大学公开课:iOS7应用开发》中第一节课中的图片,讲述了三者之间的关系。按照交通规则来看,黄色是不可逾越的,也就是说model和view之间不可以存在任何联系,controller可以访问和持有model和view,反之则不行。
概览
一直在写代码,什么是理想中的代码
关于MVC
图片是《斯坦福大学公开课:iOS7应用开发》中第一节课中的图片,讲述了三者之间的关系。按照交通规则来看,黄色是不可逾越的,也就是说model和view之间不可以存在任何联系,controller可以访问和持有model和view,反之则不行。
那么问题来了,UICollectionViewCell,UITableViewCell属于什么呢?当然属于View,继承自它们的当然也属于View,为了方便,我们常常在某个cell中绑定一个model,然后在类里面根据model修改各个元素的值,我个人是不提倡这么做的。从单个页面来看,通常很便捷,但是从上面的图片来看,它已经违反了交通规则。
1、从单元测试来讲,提高了测试的耦合性,想要测试一个view必须先创建一个model
2、一旦绑定了一个类型的model将很难复用到其他界面相似的view,例如中国版的点赞列表、附近的人的列表、消息里面的关注列表,三者界面类似,数据model都是从user继承而来,又稍有不同,换句话说,需求可能是界面都不动,界面中的某个lable的text需要改变。另外如果一个view想要给其他业务或者其他团队或者开源出来给其他人用必须把model从cell中拿掉。这里可以举个开源的例子,com中聊天引用的一个项目JSQMessagesViewController 中的JSQMessagesCollectionViewCell
enum 枚举
enum Rank: Int { case ace = 1 case two, three, four, five, six, seven, eight, nine, ten case jack, queen, king func simpleDescription() -> String { switch self { case .ace: return "ace" case .jack: return "jack" case .queen: return "queen" case .king: return "king" default: return String(self.rawValue) } } } let ace = Rank.ace let aceRawValue = ace.rawValue
最初以为swift中的枚举只有以上添加了函数的操作,其实不然啊 看下面
/** This object gives specific change information about a collection. */ public enum CollectionChangeInformation: Equatable { /// This indicates that an element was updated at a specific index. case update(index: Int) /// This indicates that an element was deleted at a specific index. case delete(index: Int) /// This indicates that an element was inserted at a specific index. case insert(index: Int) } public func ==(lhs: CollectionChangeInformation, rhs: CollectionChangeInformation) -> Bool { switch (lhs, rhs) { case (.update(let l), .update(let r)): return l == r case (.delete(let l), .delete(let r)): return l == r case (.insert(let l), .insert(let r)): return l == r default: return false } }
惊不惊喜,意外不?在枚举的枚举值里update发现了参数这是associated values,你可以理解为这些参数保存在了枚举的变量里面(You can think of the associated values as behaving like stored properties of the enumeration case instance),而且还遵守了Equatable协议,意味着可以自己定义怎么判断两个枚举值是否相等。来看看在苹果官方sdk中说了啥
/// extension StreetAddress: Equatable { /// static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool { /// return /// lhs.number == rhs.number && /// lhs.street == rhs.street && /// lhs.unit == rhs.unit /// } /// } public protocol Equatable { /// Returns a Boolean value indicating whether two values are equal. /// /// Equality is the inverse of inequality. For any values `a` and `b`, /// `a == b` implies that `a != b` is `false`. /// /// - Parameters: /// - lhs: A value to compare. /// - rhs: Another value to compare. public static func ==(lhs: Self, rhs: Self) -> Bool } extension Equatable { /// Returns a Boolean value indicating whether two values are not equal. /// /// Inequality is the inverse of equality. For any values `a` and `b`, `a != b` /// implies that `a == b` is `false`. /// /// This is the default implementation of the not-equal-to operator (`!=`) /// for any type that conforms to `Equatable`. /// /// - Parameters: /// - lhs: A value to compare. /// - rhs: Another value to compare. public static func !=(lhs: Self, rhs: Self) -> Bool }
Functional Reactive Programming(以下简称FRP)是一种响应变化的编程范式
我们之前一直在使用ReactiveCocoa 【ReactiveCocoa (RAC) is a Cocoa framework inspired by Functional Reactive Programming】,这两天又接触了RxSwift、RxCocoa,发现他们很类似,各有所长。之所以在这里说是因为看到RxCocoa在UI方面更加强大
/** Binds sequences of elements to collection view items. - parameter cellIdentifier: Identifier used to dequeue cells. - parameter source: Observable sequence of items. - parameter configureCell: Transform between sequence elements and view cells. - parameter cellType: Type of table view cell. - returns: Disposable object that can be used to unbind. Example let items = Observable.just([ 1, 2, 3 ]) items .bind(to: collectionView.rx.items(cellIdentifier: "Cell", cellType: NumberCell.self)) { (row, element, cell) in cell.value?.text = "\(element) @ \(row)" } .disposed(by: disposeBag) collectionView.rx.itemSelected.subscribe({ [weak self] indexPath in // do something }).disposed(by: disposeBag) */
有没有很酷,哈哈 看起来不错哦上面例子中只是列举了最简单的一种看下面的复杂的
/** Binds sequences of elements to collection view items using a custom reactive data used to perform the transformation. - parameter dataSource: Data source used to transform elements to view cells. - parameter source: Observable sequence of items. - returns: Disposable object that can be used to unbind. Example let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, Double>>() let items = Observable.just([ SectionModel(model: "First section", items: [ 1.0, 2.0, 3.0 ]), SectionModel(model: "Second section", items: [ 1.0, 2.0, 3.0 ]), SectionModel(model: "Third section", items: [ 1.0, 2.0, 3.0 ]) ]) dataSource.configureCell = { (dataSource, cv, indexPath, element) in let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell cell.value?.text = "\(element) @ row \(indexPath.row)" return cell } items .bind(to: collectionView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) */
MVVM
关于MVVM的介绍 ReactiveViewModel ,目前我们项目采用以下设计
model :仅仅是数据模型
view :这里的view包含所有的view和viewcontroller,其中view(包含cell和自定义view)仅仅是view,不包含任何model,另外viewcontroller仅仅负责Layout、Animations、Device rotation、View and window transitions、Presenting loaded UI
viewModel:所有的viewmodel会被viewcontroller持有,这里面负责处理页数调用PXApiMannager获取数据,并且保存在ViewModel的property里面,如果需要为cell准备数据,在这里进行加工
完整数据获取及刷新流程如下:
viewController(订阅ViewModel的某个Signal,如果有输出数据刷新view,如果有错误提示错误)——————>viewModel(调用PXApiManager获取某个signal,进行map加工操作,将数据自身持有)—————>PXApiManager(创建signal,并且调用AFNetworking获取相应数据,如果有有数据则通过 Mantle 转化为model并向创建的signal抛出,如果有错误也抛出,这里也会进行接口异常记录,链接请求统一处理header,统一添加token信息等等)
IGListKit
IGListKit 是Facebook的又一神作, 这里是raywenderlich上的一篇教程 ,教你如何快速的在list中添加功能,而且滑动起来非常的流畅。IGListKit非常智能,会自动检查你数据中的变化,并流畅的更新UICollectionView 中对应改变数据的部分。
总体分为四步:
1、声明一个IGListCollectionView的变量,并且添加到当前viewcontroller当中,并且设置布局
// 1 let collectionView: IGListCollectionView = { // 2 let view = IGListCollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout()) // 3 view.backgroundColor = UIColor.black return view }()
2、声明一个IGListAdapter的变量,并且为之设置view和databsource
lazy var adapter: IGListAdapter = { return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 0) }() adapter.collectionView = collectionView adapter.dataSource = self
3、在datasource中设置数据、IGListSectionController及为空时显示的view,注意:warning:这里的IGListSectionController相当于上面我们提到的ViewModel,你可以在里面控制列表中的单元如何显示
// MARK: - IGListAdapterDataSource extension FeedViewController: IGListAdapterDataSource { func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] { var items: [IGListDiffable] = [wxScanner.currentWeather] items += loader.entries as [IGListDiffable] items += pathfinder.messages as [IGListDiffable] return items.sorted(by: { (left: Any, right: Any) -> Bool in if let left = left as? DateSortable, let right = right as? DateSortable { return left.date > right.date } return false }) } func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController { if object is Message { return MessageSectionController() } else if object is Weather { return WeatherSectionController() } else { return JournalSectionController() } } func emptyView(for listAdapter: IGListAdapter) -> UIView? { return nil } }
4、在IGListSectionController里面设置边距、Section里面有几个单元cell、每个cell又用的哪种类型,并用model填充cell
import IGListKit class MessageSectionController: IGListSectionController { var message: Message! override init() { super.init() inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0) } } // MARK: - IGListSectionType extension MessageSectionController: IGListSectionType { func numberOfItems() -> Int { return 1 } func sizeForItem(at index: Int) -> CGSize { guard let context = collectionContext else { return .zero } return MessageCell.cellSize(width: context.containerSize.width, text: message.text) } func cellForItem(at index: Int) -> UICollectionViewCell { let cell = collectionContext?.dequeueReusableCell(of: MessageCell.self, for: self, at: index) as! MessageCell cell.messageLabel.text = message.text cell.titleLabel.text = message.user.name.uppercased() return cell } func didUpdate(to object: Any) { message = object as? Message } func didSelectItem(at index: Int) {} }
但是看起来并没有Rxswift那么简单有没有,但是性能应该是没问题的。具体可以测试一下。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何制定理想IaaS协议
- Jupyter可能并非理想的Notebook
- 可串行化(Serializable):理想和现实
- 一个程序员的理想主义
- 【硬件虚拟化】远离kernel的理想乡
- AAAI 2019 提前看:融合质量不理想数据
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。