废话开篇:最近写了一段 ApiCloud 混合开发 APP,说是混合开发,但是大部分的功能还是以 JS 为主,其实,ApiCloud 跨平台的开发只能算是工具,而不能算是一门语言。那么,JS 本身是运行在 JS 环境中的,普通页面的 JS 运行在 WKWebView 对应的环境里。当然,也可以初始化一个 JS 运行环境,让一些自定义代码运行在这个脱离 webView 的 JS 运行环境里。但是,不管是以什么方式运行 JS,如果不在同一个环境下,那么,该环境下的 JS 数据在不借助桥接的情况下是不能共享的,所以,就会有人好奇,原生开发下,详情页的数据变了,列表页面的对应UI如何同步修改?
一、JS模式下,详情页的数据变了,列表页面的对应UI如何同步修改?
JS 的运行及访问界限被 WKWebView 限制住了,如果不通过原生的多 WebView 之间的通信辅助是无法做到数据共享的。
二、iOS模式下,为什么详情页的数据变了,列表页面的对应UI可以同步修改?
原生的所有的值、对象、方法,都在同一个运行环境里,如果指针传的足够远,那么,程序将会在任何地方访问或调用某些对象或方法。
三、iOS模式下,如何做到详情页的数据变了,列表页面的对应UI可以同步修改?
实现方法其实有很多种:
1、代理回执;
2、闭包回执;
3、通知回执;
4、对象观察者回执;
上面所有方法的思路,其实都是通过“一根线”链接“两个点”,只是实现的方式不一样而已。
上面最好的方式还是通过对象观察者进行回执修改,这个就好比大家在过斑马线,每个人都盯着信号灯,一旦绿灯亮起,那么,大家就动起来;反之,其他的方式就类似于,张三看见绿灯亮了,告诉李四,李四告诉王五...
四、iOS模式下,简单实现
3.1 实现效果
详情页的修改“点赞”数,上一级的列表“点赞”展示数也随之变化
3.2 实现思路
利用 RxSwift 观察者进行绑定
将对象的“点赞”属性分别绑定到 Cell 上面的 UILable 和详情页里的 UILable 上。那么,不管在个代码下修改了“点赞”属性值,那么,绑定的UI都会作出更改。
3.3 代码展示
这里说一下 RxSwift 使用过程中的注意点:
新闻对象类
class NewsClass : NSObject, HandyJSON{
@objc dynamic var thumb : Int = 0
@objc dynamic var name : String?
@objc dynamic var url : String?
@objc dynamic var mineThumbStatus : Bool = false
required override init(){
}
//绑定数据
func rxBindContent(_ cell : WSLNewsListTableViewCell){
self.rx.observeWeakly(Int.self, "thumb").asObservable().bind(to: cell.rx.thumbLab).disposed(by: cell.disposeBag)
self.rx.observeWeakly(String.self, "name").asObservable().bind(to: cell.rx.nameLab).disposed(by: cell.disposeBag)
self.rx.observeWeakly(String.self, "url").asObservable().bind(to: cell.rx.coverImg).disposed(by: cell.disposeBag)
}
}
注意点1:对象需继承 NSObject,以沿用 rx 系列的扩展方法。
注意点2:对于用 RxSwift 的 rx.observeWeakly 观察的对象属性需要用 @objc dynamic 修饰,赋予属性动态特性。
注意点3:在绑定数据方法里,利用 bind 方法将观察序列绑定在了自定义 cell 的 rx 相关扩展下。
把赋值及其他相关的操作放在这里,外部进进行绑定
//MARK: -扩展cell的rx属性
extension Reactive where Base : WSLNewsListTableViewCell {
var thumbLab : Binder<Int?> {
return Binder(self.base){ cell ,content **in**
cell.thumbLab.text = "点赞数:" + String(describing: content!)
cell.thumbLab.sizeToFit()
}
}
var nameLab : Binder<String?> {
return Binder(self.base){ cell ,content in
cell.nameLab.text = content!
cell.nameLab.sizeToFit()
}
}
var coverImg : Binder<String?> {
return Binder(self.base) { cell , url in
cell.coverImgView.kf.setImage(with: URL(string: url ?? ""))
}
}
}
注意点4:这里的绑定方法是在 UITableViewDataSource 方法里,那么,重用机制自然会导致多次绑定。
cell 的绑定赋值操作
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : WSLNewsListTableViewCell = tableView.dequeueReusableCell(withIdentifier: "CELL", for: indexPath) as! WSLNewsListTableViewCell
//进行数据绑定
models?[indexPath.row].rxBindContent(cell)
//点击事件
cell.enterDetailBtn.rx.controlEvent(.touchUpInside).subscribe {[weak self] btn in
self?.pushDetailNewsVCSubject.onNext(self?.models?[indexPath.row])
}.disposed(by: cell.disposeBag)
return cell
}
model 的绑定事件
func rxBindContent(_ cell : WSLNewsListTableViewCell){
self.rx.observeWeakly(Int.self, "thumb").asObservable().bind(to: cell.rx.thumbLab).disposed(by: cell.disposeBag)
self.rx.observeWeakly(String.self, "name").asObservable().bind(to: cell.rx.nameLab).disposed(by: cell.disposeBag)
self.rx.observeWeakly(String.self, "url").asObservable().bind(to: cell.rx.coverImg).disposed(by: cell.disposeBag)
}
解决复用机制重复绑定数据的办法就是绑定数据的序列销毁关联 cell.disposeBag,在 cell 的 prepareForReuse 方法里进行 cell.disposeBag 的重置。
这里说一下 disposed 意义:意为销毁操作。在日常的开发中,会创建很多序列,但是,它们的生命周期交给谁处理呢?为了处理这个问题就出现了 disposed(by: disposeBag) 来辅助,把所有的序列关联到 disposeBag 上,那么,当 disposeBag 对象在某个时机被销毁的时候,同时销毁它关联的所有序列。
cell 复用前进行 disposeBag 重置
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
这样就保证了,屏幕上可见的 cell 才具有绑定效果。
注意点5:在详情页修改传入参数的属性值。
btn.rx.tap.subscribe {[weak self] _ in
self?.newsClass?.thumb += 1
}.disposed(by: disposeBag)
这样就完成了详情页修改属性,列表页面UI同步修改。
五、model 对象用 struct 类型的如何处理?
很遗憾,结构体属于值类型,在方法传入的时候会有一个副本。在逃逸闭包里,结构体更是会被复制出来,那么,如果不能操作原数据的话,扩展还有什么意义呢?
聪明的掘友会想到:是不是可以用 ReplaySubject 进行数据传递呢?因为 ReplaySubject 属于对象,即使结构体采用副本复制的方式传值,但是,对于对象类型的“属性”,复制的也是对象地址,那么,是不是可以进行直接 UI 绑定操作呢?答案是可以的! 但是这样的操作没有办法改变列表页的结构体数据,所以,不能进行数据同步,只能做到 UI 显示同步。
总结与思考:掘金大神很多,但是这篇小总结,显然不是给大神看的[抱拳][抱拳][抱拳]。但掘金里喜欢分享跟学习的小伙伴也很多,希望这篇小总结,对大家能起到帮助[抱拳][抱拳][抱拳]。