RxSwfit使用中遇到的一些问题与解决方案
2017-07-28 本文已影响97人
Lxyang
摘要
由于项目需重构,于是把旧项目中的oc与swift混编,全改为swift,并且项目中采用RxSwfit框架,函数响应式编程,经过半个月重构完,把开发过程中遇到的坑记录下来。
1.combineLatest入参不能超过8个的解决方案
项目中,由于添加银行卡的表单页面有超过8个的输入框,这时候用到combineLatest,由于它只能最多接收8个ObservableType类型参数,于是想到用它的combineLatest(Collection)的方式实现。但是总是会报一些莫名的错误,于是放弃,最后还是参数至多8个的方式,只是中间会把多个合并为一个,再去传参数.
viewModel代码如下:
9个输入框监听
/// 姓名输入声明
var nameInput = Variable<String>("")
/// 银行卡号输入声明
var cardNumInput = Variable<String>("")
/// 银行名输入声明
var bankNameInput = Variable<String>("")
/// 预留手机号输入声明
var phoneNumInput = Variable<String>("")
/// 开户地区输入声明
var districtInput = Variable<String>("")
/// 支行名输入声明
var branchInput = Variable<String>("")
/// 正面照
var frontImg = Variable<UIImage?>(nil)
/// 反面照
var backImg = Variable<UIImage?>(nil)
/// 是否同意协议 --默认同意
var isAgreeLLPay = Variable<Bool>(true)
/// 下一步按钮 输入
var nextBtnTap = PublishSubject<Void>()
3个输出
/// 输出
var nextBtnEnable: Observable<Bool>
var nextTapResult: Observable<FormValidateReuslt>?
/// 是否返回
var backResult: Observable<Bool>
为解决超过8个,把姓名、银行卡号、银行名、预留手机号四个的监听合并为一个
/// 姓名-卡号-银行名-手机 四合一
var nameCardBankPhone:Observable<(String, String, String, String)>
init() {
/// 四个输入监听合并为一,这样可作为一个返回元组的参数
nameCardBankPhone = Observable.combineLatest(nameInput.asObservable(),cardNumInput.asObservable(), bankNameInput.asObservable(), phoneNumInput.asObservable()) {
($0,$1,$2,$3)
}
/// 下一步按钮使能输出(解决不参超过8个输入的问题)
nextBtnEnable = Observable.combineLatest(nameCardBankPhone,
districtInput.asObservable(),
branchInput.asObservable(),
frontImg.asObservable(),
backImg.asObservable(),
isAgreeLLPay.asObservable(),
resultSelector: { (combileItem,district,branch,frontImge,backImge,isAgree) -> Bool in
/// combileItem这里是一个元组
let flag = !combileItem.0.isEmpty &&
!combileItem.1.isEmpty &&
!combileItem.2.isEmpty &&
!combileItem.3.isEmpty &&
!district.isEmpty &&
!branch.isEmpty &&
!(frontImge == nil) &&
!(backImge == nil) &&
(isAgree == true)
return flag
})
.distinctUntilChanged()
.shareReplay(1)
/// 导航栏上返回按钮二次确认框是否弹出监听
backResult = Observable.combineLatest(nameCardBankPhone,
districtInput.asObservable(),
branchInput.asObservable(),
frontImg.asObservable(),
backImg.asObservable(),
isAgreeLLPay.asObservable(),
resultSelector: { (combileItem,district,branch,frontImge,backImge,isAgree) -> Bool in
let flag = !combileItem.0.isEmpty ||
!combileItem.1.isEmpty ||
!combileItem.2.isEmpty ||
!combileItem.3.isEmpty ||
!district.isEmpty ||
!branch.isEmpty ||
!(frontImge == nil) ||
!(backImge == nil) ||
(isAgree != XYJAddBankCardVM.isAgreeDefault)
XYJAddBankCardVM.isAgreeDefault = isAgree
return flag
}).distinctUntilChanged()
.shareReplay(1)
/// 请求参数8合一监听
let questionAndSuggest = Observable.combineLatest(nameInput.asObservable(),
cardNumInput.asObservable(),
bankNameInput.asObservable(),
phoneNumInput.asObservable(),
districtInput.asObservable(),
branchInput.asObservable(),
frontImg.asObservable(),
backImg.asObservable()
) { ($0, $1, $2, $3 ,$4, $5 ,$6, $7) }
/// 点击下一步按钮
nextTapResult = nextBtnTap.asObservable().withLatestFrom(questionAndSuggest).map({ (custName, cardNum, bankName,phone,district,branch,frontImge,backImge) -> FormValidateReuslt in
return self.validateForm(custName, cardNum, bankName,phone,district,branch,frontImge!,backImge!)
}).shareReplay(1)
}
2.RxDataSource框,tableView的cell内自定义按钮点击会处理多次的问题
由于cell的重复利用,我们在cellForRow的方法中,直接根据cell拿到里面的按钮,然后去订阅这个按钮的点击,于是会出现来回滚动几次以后,点击按钮,会执行多里订阅事件。如下:
dataSource.configureCell = {
_,tableView,indexPath,msgItem in
let cell = tableView.dequeueReusableCell(withIdentifier: "XYJSystemMsgCell") as! XYJSystemMsgCell
cell.tag = indexPath.row
cell.msgItem = msgItem
cell.searchBtn.rx.tap.subscribe(onNext: { (model) in
print("cell里面按钮点击:\(model.time)")
}, onError: nil, onCompleted: nil, onDisposed: {}).addDisposableTo(self.disposeBag)
return cell
}
在这里,我们点击按钮,出打印多次"cell里面按钮点击:*****",这是由于cell循环利用,多次订阅所导致.
解决方案
资源管理包重复利用,在cell重利用的时候,对disposeBag清除一次以达到目的,cell内部实现如下:
/// cell内部按钮点击处理
var innerObserable : Observable<XYJSystemMsg>?
var disposeBag:DisposeBag?
override func awakeFromNib() {
super.awakeFromNib()
configePage()
layout()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configePage()
layout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var msgItem: XYJSystemMsg? {
didSet {
self.disposeBag = DisposeBag()
timeLabel.text = msgItem?.time
innerObserable = detailBtn.rx.tap.asObservable().map { () -> XYJSystemMsg in
return self.msgItem ?? XYJSystemMsg.init(time: "")
}.shareReplay(1)
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.disposeBag = nil
}
控制器中订阅如下:
/// 配置cell
dataSource.configureCell = { ,tableView,indexPath,msgItem in
let cell = tableView.dequeueReusableCell(withIdentifier: "XYJSystemMsgCell") as! XYJSystemMsgCell
cell.tag = indexPath.row
cell.msgItem = msgItem
cell.detailBtn.rx.tap
cell.innerObserable?.subscribe(onNext: { (model) in
print("cell里面按钮点击:\(model.time)")
}, onError: nil, onCompleted: nil, onDisposed: {}).addDisposableTo(cell.disposeBag!)
return cell
}
未完待续...