①、iOS-RxSwift基础控件的使用、RxSwift-Tableview的使用、RxSwift-SectionTableview结合RxDataSources使用、RxSwift 网络请求封装使用

RxSwift系列
①、RxSwift基础控件的使用、RxSwif-Tableview的使用、RxSwift-SectionTableview结合RxDataSources的使用、RxSwift 网络请求封装的使用
②、RxSwift函数式响应编程思想,RxSwift-KVO、Button、UITextField、ScrollView、手势、通知、定时器、网络请求的使用
③、iOS-RxSwift核心逻辑,RxSwift实战案例,Observable的继承链,Observable订阅的流程、OnNext的流程、AnonymousObservableSink业务逻辑用
④、RxSwift的Timer、Observable的创建、RxSwiftUI控件(UIDatePicker、UIButton、UISwitch、UISlider、UIStepper)在实际开发的使用

推荐网站
RxSwift官方文档
RxSwift-Github源码
RxSwift的操作符

Demo
Demo-①、RxSwift基础控件的使用、RxSwif-Tableview的使用、RxSwift-SectionTableview结合RxDataSources的使用、RxSwift 网络请求封装的使用
Demo-②、RxSwift函数式响应编程思想,RxSwift-KVO、Button、UITextField、ScrollView、手势、通知、定时器、网络请求的使用-1
Demo-②、RxSwift函数式响应编程思想,RxSwift-KVO、Button、UITextField、ScrollView、手势、通知、定时器、网络请求的使用-2-Observable

本篇文章 参照文献
1.BehaviorRelay 无法使用value 给BehaviorRelay扩展 不使用value属性 使用val属性


RxSwift初探

①、RxSwift 基本控件的使用

1.RAC和RxSwift对比

RAC
    // 1.创建信号
    @weakify(self);
       RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
           @strongify(self);
           NSLog(@"来了,网络请求");
           // 3.发送信息
           [subscriber sendNext:@"yuye"];
           // 4.销毁信号
           return [RACDisposable disposableWithBlock:^{
               NSLog(@"我们销毁了");
           }];
       }];
       // 2.订阅信号 --- 保存block
       [signal subscribeNext:^(id  _Nullable x) {
           NSLog(@"订阅到了 : %@",x);
       }];
    [signal subscribeError:^(NSError * _Nullable error) {
        NSLog(@"Error is %@",error);
    }];
RxSwift
func RxSwiftUse(){
        // RAC --- Signal
        //  1.产生序列 --- swift
        let observer = Observable<Any>.create { (ob) -> Disposable in
            //
            print(ob)
            // 3.发送
            ob.onNext("tenhow")
            ob.onError(MyError.errorA)
            ob.onCompleted()
            return Disposables.create()
        }
        
        // 订阅
        observer.subscribe { (signal) in
            print(signal)
        } onError: { error in
            print(error)
        } onCompleted: {
            print("完成了")
        } onDisposed: {
            print("销毁了")
        }.disposed(by: disposB)

    }

2.RxSwift 监听值的改变 以UITextField输出多少字符为例

@IBOutlet weak var textf: UITextField!
@IBOutlet weak var label: UILabel!

 func listenInput() {
        // orEmpty 可能是空值
        // asDriver(当司机) 生成响应的系列类型之后
        // throttle 只隔几秒之后 才会响应
        // map 进行函数预设
        // drive(开车) 卡车 就绑定到响应的控件里面去
        //
        let textInput = textf.rx.text.orEmpty.asDriver().throttle(RxTimeInterval.seconds(1))
            textInput.map { "输入:\($0.count)"
        }.drive(label.rx.text).disposed(by: disposB)
    }
2.1 RxSwift 监听值的改变绑定多个UI 进行关联
@IBOutlet weak var btn: UIButton!

textInput.map{$0.count > 5}.drive(btn.rx.isEnabled).disposed(by: disposB)

3.RxSwift 双向绑定 添加操作符的扩展

3.1 rxSwift4 定义变量是Variable RxSwift5.x 使用的是 BehaviorRelay或者BehaviorSubject .具体参考
        // rxSwift5 开始使用 BehaviorRelay
        let textOb = BehaviorRelay(value: "🔴")
        // <-> 操作符   <-> 是一个扩展 双向绑定
        _ = textf.rx.textInput <-> textOb

3.2 <->双向绑定操作符的扩展 BehaviorRelay不能直接使用value属性 具体参考BehaviorRelay添加扩展

在这里插入图片描述

Operators.swift
//
//  Operators.swift
//  RxExample
//
//  Created by Krunoslav Zaher on 12/6/15.
//  Copyright © 2015 Krunoslav Zaher. All rights reserved.
//

import RxSwift
import RxCocoa
import UIKit

// Two way binding operator between control property and variable, that's all it takes {

infix operator <-> : DefaultPrecedence

// rxSwift5 双向绑定 给 BehaviorRelay扩展 
extension BehaviorRelay {

    var val: Element {
        get { value }
        set { accept(newValue) }
    }
}

func nonMarkedText(_ textInput: UITextInput) -> String? {
    let start = textInput.beginningOfDocument
    let end = textInput.endOfDocument

    guard let rangeAll = textInput.textRange(from: start, to: end),
        let text = textInput.text(in: rangeAll) else {
            return nil
    }

    guard let markedTextRange = textInput.markedTextRange else {
        return text
    }

    guard let startRange = textInput.textRange(from: start, to: markedTextRange.start),
        let endRange = textInput.textRange(from: markedTextRange.end, to: end) else {
        return text
    }

    return (textInput.text(in: startRange) ?? "") + (textInput.text(in: endRange) ?? "")
}

func <-> <Base>(textInput: TextInput<Base>, variable: BehaviorRelay<String>) -> Disposable {
    let bindToUIDisposable = variable.asObservable()
        .bind(to: textInput.text)
    let bindToVariable = textInput.text
        .subscribe(onNext: { [weak base = textInput.base] n in
            guard let base = base else {
                return
            }

            let nonMarkedTextValue = nonMarkedText(base)

            /**
             In some cases `textInput.textRangeFromPosition(start, toPosition: end)` will return nil even though the underlying
             value is not nil. This appears to be an Apple bug. If it's not, and we are doing something wrong, please let us know.
             The can be reproed easily if replace bottom code with 
             
             if nonMarkedTextValue != variable.value {
                variable.value = nonMarkedTextValue ?? ""
             }

             and you hit "Done" button on keyboard.
             */
            if let nonMarkedTextValue = nonMarkedTextValue, nonMarkedTextValue != variable.value {
                variable.val = nonMarkedTextValue
            }
        }, onCompleted:  {
            bindToUIDisposable.dispose()
        })

    return Disposables.create(bindToUIDisposable, bindToVariable)
}

func <-> <T>(property: ControlProperty<T>, variable: BehaviorRelay<T>) -> Disposable {
    if T.self == String.self {
#if DEBUG
        fatalError("It is ok to delete this message, but this is here to warn that you are maybe trying to bind to some `rx.text` property directly to variable.\n" +
            "That will usually work ok, but for some languages that use IME, that simplistic method could cause unexpected issues because it will return intermediate results while text is being inputed.\n" +
            "REMEDY: Just use `textField <-> variable` instead of `textField.rx.text <-> variable`.\n" +
            "Find out more here: https://github.com/ReactiveX/RxSwift/issues/649\n"
            )
#endif
    }

    let bindToUIDisposable = variable.asObservable()
        .bind(to: property)
    let bindToVariable = property
        .subscribe(onNext: { n in
            variable.val = n
        }, onCompleted:  {
            bindToUIDisposable.dispose()
        })

    return Disposables.create(bindToUIDisposable, bindToVariable)
}

// }


4.RxSwift给UIbutton绑定点击事件

btn.rx.tap.subscribe(onNext:{ (ob) in
          print("点了");
       }).disposed(by: disposB)

②、RxSwift Tableview的使用

0.pod 安装 RxDataSource 用来操作tableview的

    pod 'RxDataSources'

1.RxSwift绑定tableview bind

1.Rx的just操作符 just 操作符将某一个元素转换为 Observable。
   func RxBindTableview(){
        tableView = UITableView(frame: self.view.bounds, style: .plain)
        tableView.register(MyTableViewCell.self, forCellReuseIdentifier: MyTableViewCell.description())
        self.view.addSubview(tableView)

        // just 操作符将某一个元素转换为 Observable。
        
        // 绑定
        items.bind(to: self.tableView.rx.items){(tb,row,model) -> UITableViewCell in
            let cell = tb.dequeueReusableCell(withIdentifier: MyTableViewCell.description()) as? MyTableViewCell
            cell?.titlteLabel?.text = model.despStr;
            cell?.nameLabel?.text = model.nameStr;
            return cell!
        }.disposed(by: disposeBag)

    }

2.RxSwift的tableview select选择操作

    func RxBindTableviewAction() {
        // 点击
        tableView.rx.modelSelected(DataModel.self).subscribe(onNext: { (model) in
            print(model)
        })
        .disposed(by: disposeBag)
        
        tableView.rx.itemSelected.subscribe(onNext: { [weak self] indexpath in
            print("点击了 \(indexpath)")
//            self?.navigationController?.pushViewController(SectionTableViewVC(), animated: true)
        })
            .disposed(by: disposeBag)
        




    }

3.RxSwift的tableview move移动操作

        // 移动
       tableView.isEditing = true
        tableView.rx.itemMoved.subscribe(onNext: { (soureceIndex,desIndex) in
            print("从\(soureceIndex)移动到 \(desIndex)")
        })
            .disposed(by: disposeBag)

4.RxSwift的tableview delete删除操作

        // 删除
        tableView.rx.itemDeleted.subscribe(onNext: { (indexPath) in
            print("删除+++\(indexPath)")
        })
            .disposed(by: disposeBag)

③、RxSwift SectionTableview结合RxDataSources的使用

1.创建section的tableview
    var tableView : UITableView!
        override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        AddSectionTableView()
    }
    func AddSectionTableView(){
        self.title = "分组显示的Table"
        self.view.backgroundColor = UIColor.orange
        tableView = UITableView(frame: self.view.bounds, style: .plain)
        tableView.register(SectionTableCell.self, forCellReuseIdentifier: SectionTableCell.description())
        self.view.addSubview(tableView)
    }

// cell
class SectionTableCell: UITableViewCell {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier: reuseIdentifier)
//        print(#function)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

2.使用RxTableViewSectionedReloadDataSource 创建 数据源
2.1 先创建本地SectionTableView的数据
RxDataModel
//
//  RxDataModel.swift
//  CSDN-RxSwift
//
//  Created by 李宇鸿 on 2022/2/21.
//

import UIKit
import RxSwift
import RxCocoa
import RxDataSources


struct DataModel {
    let despStr:String
    let nameStr:String
}

struct InfoViewModel {
    var arr = Array<DataModel>()
    init(){
        arr.append(DataModel(despStr: "first", nameStr: "宇夜"))
        arr.append(DataModel(despStr: "2", nameStr: "iOS"))
        arr.append(DataModel(despStr: "3", nameStr: "Android"))
        arr.append(DataModel(despStr: "4", nameStr: "Java"))
        arr.append(DataModel(despStr: "5", nameStr: "Python"))
    }
}


// section tableview DataS
struct SectionDataModel {
    
    let name: String
    let gitHubID: String
    var image: UIImage?
    
    init(name: String, gitHubID: String) {
        self.name = name
        self.gitHubID = gitHubID
        image = UIImage(named: gitHubID)
    }
}

extension SectionDataModel:IdentifiableType{
    typealias Identity = String
    var identity:Identity {return gitHubID}
}

class GithubData {
    let githubData = Observable.just([
        SectionModel(model: "A", items: [
            SectionDataModel(name: "Alex V Bush", gitHubID: "alexvbush"),
            SectionDataModel(name: "Andrew Breckenridge", gitHubID: "AndrewSB"),
            SectionDataModel(name: "Anton Efimenko", gitHubID: "reloni"),
            SectionDataModel(name: "Ash Furrow", gitHubID: "ashfurrow"),
            ]),
        SectionModel(model: "B", items: [
            SectionDataModel(name: "Ben Emdon", gitHubID: "BenEmdon"),
            SectionDataModel(name: "Bob Spryn", gitHubID: "sprynmr"),
            ]),
        SectionModel(model: "C", items: [
            SectionDataModel(name: "Carlos García", gitHubID: "carlosypunto"),
            SectionDataModel(name: "Cezary Kopacz", gitHubID: "CezaryKopacz"),
            SectionDataModel(name: "Chris Jimenez", gitHubID: "PiXeL16"),
            SectionDataModel(name: "Christian Tietze", gitHubID: "DivineDominion"),
            ]),
        SectionModel(model: "D", items: [
            SectionDataModel(name: "だいちろ", gitHubID: "mokumoku"),
            SectionDataModel(name: "David Alejandro", gitHubID: "davidlondono"),
            SectionDataModel(name: "David Paschich", gitHubID: "dpassage"),
            ]),
        SectionModel(model: "E", items: [
            SectionDataModel(name: "Esteban Torres", gitHubID: "esttorhe"),
            SectionDataModel(name: "Evgeny Aleksandrov", gitHubID: "ealeksandrov"),
            ]),
        SectionModel(model: "F", items: [
            SectionDataModel(name: "Florent Pillet", gitHubID: "fpillet"),
            SectionDataModel(name: "Francis Chong", gitHubID: "siuying"),
            ]),
        SectionModel(model: "G", items: [
            SectionDataModel(name: "Giorgos Tsiapaliokas", gitHubID: "terietor"),
            SectionDataModel(name: "Guy Kahlon", gitHubID: "GuyKahlon"),
            SectionDataModel(name: "Gwendal Roué", gitHubID: "groue"),
            ]),
        SectionModel(model: "H", items: [
            SectionDataModel(name: "Hiroshi Kimura", gitHubID: "muukii"),
            ]),
        SectionModel(model: "I", items: [
            SectionDataModel(name: "Ivan Bruel", gitHubID: "ivanbruel"),
            ]),
        SectionModel(model: "J", items: [
            SectionDataModel(name: "Jeon Suyeol", gitHubID: "devxoul"),
            SectionDataModel(name: "Jérôme Alves", gitHubID: "jegnux"),
            SectionDataModel(name: "Jesse Farless", gitHubID: "solidcell"),
            SectionDataModel(name: "Junior B.", gitHubID: "bontoJR"),
            SectionDataModel(name: "Justin Swart", gitHubID: "justinswart"),
            ]),
        SectionModel(model: "K", items: [
            SectionDataModel(name: "Karlo", gitHubID: "floskel"),
            SectionDataModel(name: "Krunoslav Zaher", gitHubID: "kzaher"),
            ]),
        SectionModel(model: "L", items: [
            SectionDataModel(name: "Laurin Brandner", gitHubID: "lbrndnr"),
            SectionDataModel(name: "Lee Sun-Hyoup", gitHubID: "kciter"),
            SectionDataModel(name: "Leo Picado", gitHubID: "leopic"),
            SectionDataModel(name: "Libor Huspenina", gitHubID: "libec"),
            SectionDataModel(name: "Lukas Lipka", gitHubID: "lipka"),
            SectionDataModel(name: "Łukasz Mróz", gitHubID: "sunshinejr"),
            ]),
        SectionModel(model: "M", items: [
            SectionDataModel(name: "Marin Todorov", gitHubID: "icanzilb"),
            SectionDataModel(name: "Maurício Hanika", gitHubID: "mAu888"),
            SectionDataModel(name: "Maximilian Alexander", gitHubID: "mbalex99"),
            ]),
        SectionModel(model: "N", items: [
            SectionDataModel(name: "Nathan Kot", gitHubID: "nathankot"),
            ]),
        SectionModel(model: "O", items: [
            SectionDataModel(name: "Orakaro", gitHubID: "DTVD"),
            SectionDataModel(name: "Orta", gitHubID: "orta"),
            ]),
        SectionModel(model: "P", items: [
            SectionDataModel(name: "Paweł Urbanek", gitHubID: "pawurb"),
            SectionDataModel(name: "Pedro Piñera Buendía", gitHubID: "pepibumur"),
            SectionDataModel(name: "PG Herveou", gitHubID: "pgherveou"),
            ]),
        SectionModel(model: "R", items: [
            SectionDataModel(name: "Rui Costa", gitHubID: "ruipfcosta"),
            SectionDataModel(name: "Ryo Fukuda", gitHubID: "yuzushioh"),
            ]),
        SectionModel(model: "S", items: [
            SectionDataModel(name: "Scott Gardner", gitHubID: "scotteg"),
            SectionDataModel(name: "Scott Hoyt", gitHubID: "scottrhoyt"),
            SectionDataModel(name: "Sendy Halim", gitHubID: "sendyhalim"),
            SectionDataModel(name: "Serg Dort", gitHubID: "sergdort"),
            SectionDataModel(name: "Shai Mishali", gitHubID: "freak4pc"),
            SectionDataModel(name: "Shams Ahmed", gitHubID: "shams-ahmed"),
            SectionDataModel(name: "Shenghan Chen", gitHubID: "zzdjk6"),
            SectionDataModel(name: "Shunki Tan", gitHubID: "milkit"),
            SectionDataModel(name: "Spiros Gerokostas", gitHubID: "sger"),
            SectionDataModel(name: "Stefano Mondino", gitHubID: "stefanomondino"),
            ]),
        SectionModel(model: "T", items: [
            SectionDataModel(name: "Thane Gill", gitHubID: "thanegill"),
            SectionDataModel(name: "Thomas Duplomb", gitHubID: "tomahh"),
            SectionDataModel(name: "Tomasz Pikć", gitHubID: "pikciu"),
            SectionDataModel(name: "Tony Arnold", gitHubID: "tonyarnold"),
            SectionDataModel(name: "Torsten Curdt", gitHubID: "tcurdt"),
            ]),
        SectionModel(model: "V", items: [
            SectionDataModel(name: "Vladimir Burdukov", gitHubID: "chipp"),
            ]),
        SectionModel(model: "W", items: [
            SectionDataModel(name: "Wolfgang Lutz", gitHubID: "Lutzifer"),
            ]),
        SectionModel(model: "X", items: [
            SectionDataModel(name: "xixi197 Nova", gitHubID: "xixi197"),
            ]),
        SectionModel(model: "Y", items: [
            SectionDataModel(name: "Yongha Yoo", gitHubID: "inkyfox"),
            SectionDataModel(name: "Yosuke Ishikawa", gitHubID: "ishkawa"),
            SectionDataModel(name: "Yury Korolev", gitHubID: "yury"),
            ]),
        ])
}


2.2 创建RxTableViewSectionedReloadDataSource类型的数据源
        let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,SectionDataModel>>(configureCell: { (ds,tb, indexPath, model) -> UITableViewCell in
            let cell = tb.dequeueReusableCell(withIdentifier: SectionTableCell.description(), for: indexPath) as? SectionTableCell
            cell?.imageView?.image = model.image
            cell?.textLabel?.text = model.name
            cell?.detailTextLabel?.text = model.gitHubID
            return cell!
        },titleForHeaderInSection:{(ds, index) -> String? in
            return ds.sectionModels[index].model //[self.dataArray[indexPath.section] title]//self.dataArray[indexPath.section][indexPath.row]
        })

2.3 将本地数据关联到数据源里面去
swift
    var dataS = GithubData()
  dataS.githubData.asDriver(onErrorJustReturn: [])
            .drive(tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)

RxSwift 总结 : 更多的是面向开发
cell 数值、配置、闭包、函数式


④、RxSwift 网络请求封装的使用

1.结合URSession使用RxSwift 进行请求 以 json格式返回

    let surStr = "https://www.douban.com/j/app/radio/channels" // 豆瓣广播渠道接口
    let disposeB = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        RxNetwork_json()
    }
    func RxNetwork_json(){
        let url = URL(string: surStr)
        
        URLSession.shared.rx.json(url: url!).subscribe(onNext: { (jsonData) in
            print("RxNetwork_json jsonData = \(jsonData)\n")

        },onError: { (error) in
            print("RxNetwork_json \(error)")
        }).disposed(by: disposeB)

    }
---
返回结果
2022-02-22 10:34:12.652000+0800 CSDN-RxSwift[48336:5068445] [boringssl] boringssl_metrics_log_metric_block_invoke(151) Failed to log metrics
curl -X GET 
"https://www.douban.com/j/app/radio/channels" -i -v
Success (485ms): Status 200
RxNetwork_json jsonData = {
    channels =     (
                {
            "abbr_en" = My;
            "channel_id" = 0;
            name = "\U79c1\U4eba\U5146\U8d6b";
            "name_en" = "Personal Radio";
            "seq_id" = 0;
        },
                {
            "abbr_en" = "";
            "channel_id" = 189;
            name = "\U62c9\U4e01";
            "name_en" = "";
            "seq_id" = 46;
        }
    );
}

2.结合URSession使用RxSwift 进行请求 以 data格式返回

    let surStr = "https://www.douban.com/j/app/radio/channels" // 豆瓣广播渠道接口
    let disposeB = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        RxNetwork_data()
    }

    func RxNetwork_data(){
        let url = URL(string: surStr)
        URLSession.shared.rx.data(request: URLRequest(url: url!))
            .subscribe(onNext:{ (data) in
                print("RxNetwork_data data = \(data)\n")

            }, onError: { (error) in
                print("RxNetwork_data \(error)")
            })
            .disposed(by: disposeB)

        
    }
---
返回结果
2022-02-22 10:34:12.924371+0800 CSDN-RxSwift[48336:5068439] [boringssl] boringssl_metrics_log_metric_block_invoke(151) Failed to log metrics
curl -X GET 
"https://www.douban.com/j/app/radio/channels" -i -v
Success (674ms): Status 200
RxNetwork_data data = 3661 bytes

3.结合URSession使用RxSwift 进行请求 以 reponse,data格式返回

    let surStr = "https://www.douban.com/j/app/radio/channels" // 豆瓣广播渠道接口
    let disposeB = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        RxNetwork_data()
    }

    func RxNetwork_response(){
        let url = URL(string: surStr)
        URLSession.shared.rx.response(request: URLRequest(url: url!))
            .subscribe(onNext: {(resp,data) in
                print("RxNetwork_response reponse = \(resp)\n")
                print("RxNetwork_response data = \(data)\n")

            },onError: {(error) in
            
                print("RxNetwork_response \(error)")

            })
            .disposed(by: disposeB)
        
    }
---
返回结果
curl -X GET 
"https://www.douban.com/j/app/radio/channels" -i -v
Success (699ms): Status 200
RxNetwork_response reponse = <NSHTTPURLResponse: 0x600002e644c0> { URL: https://www.douban.com/j/app/radio/channels } { Status Code: 200, Headers {
    "Cache-Control" =     (
        "must-revalidate, no-cache, private"
    );
    Connection =     (
        "keep-alive"
    );
    "Content-Encoding" =     (
        br
    );
    "Content-Type" =     (
        "application/json; charset=utf-8"
    );
    Date =     (
        "Tue, 22 Feb 2022 02:34:13 GMT"
    );
    Expires =     (
        "Sun, 1 Jan 2006 01:00:00 GMT"
    );
    "Keep-Alive" =     (
        "timeout=30"
    );
    Pragma =     (
        "no-cache"
    );
    Server =     (
        dae
    );
    "Strict-Transport-Security" =     (
        "max-age=15552000;"
    );
    "Transfer-Encoding" =     (
        Identity
    );
    "X-DAE-App" =     (
        fm
    );
    "X-DAE-Instance" =     (
        v1api
    );
    "X-DAE-Mountpoint" =     (
        True
    );
    "X-Douban-Mobileapp" =     (
        0
    );
    "X-Xss-Protection" =     (
        "1; mode=block"
    );
} }

RxNetwork_response data = 3661 bytes

2022-02-22 10:36:33.420631+0800 CSDN-RxSwift[48336:5068442] [ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke

4.使用RxSwift网络请求应用到搜索模块

1.SearchViewModel
import UIKit
import RxSwift
import RxCocoa
import RxDataSources

struct Reposity {
    let name: String
    let url:  String
}

class SearchViewModel: NSObject {
    // 搜索的文本
    let searchText = BehaviorRelay(value: "")
    // 搜索返回的数据
    // 外部观察 searchBar.text <-> searchText -> 请求searData
    var searchData : Driver<[Reposity]> {
        print("search")
        //  每0.5秒监听值得变化 在主线程执行。发送改变时 转换到请求网络函数里面
        return self.searchText.asObservable()
            .throttle(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
            .distinctUntilChanged()
            .flatMapLatest(SearchViewModel.ReponsFor)
            .asDriver(onErrorJustReturn: [])
    }
    
    // 请求网络
    static func ReponsFor(_ githubId:String) -> Observable<[Reposity]> {
        // 空判断
        print("请求")
        guard !githubId.isEmpty,let url = URL(string: "https://api.github.com/users/\(githubId)/repos") else {
            return Observable.just([])
            
        }
        print("请求操作")
        // retry https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/decision_tree/retry.html
        // retry 操作符将不会将 error 事件,传递给观察者,然而,它会从新订阅源 Observable,给这个 Observable 一个重试的机会,让它有机会不产生 error 事件。retry 总是对观察者发出 next 事件,即便源序列产生了一个 error 事件,所以这样可能会产生重复的元素(如上图所示)。
        // 请求在子线程操作
        return URLSession.shared.rx.json(url: url)
            .retry()
            .observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
            .map(SearchViewModel.parse)
            
        
    }
    // 请求的数据 格式化
    static func parse(json : Any) -> [Reposity] {
        // 空处理
        guard let items = json as? [[String:Any]] else { return [] }
//        guard let items = json as? [[String:Any]] else { return [] }
        var resps = [Reposity]() // 初始化模型数组
        items.forEach{ item in
            //空处理
            guard let name = item["name"] as? String,let url = item["html_url"] as? String else { return}
            // 添加数据
            resps.append(Reposity(name: name, url: url))
        }
        return resps
    }


}
2.RxNewSearchVC
1.创建TableviewSearchBarsearchResultsController
    let searchController = UISearchController(searchResultsController: nil)
    var searchBar: UISearchBar{return searchController.searchBar}
    var myTableView:UITableView!
    let reuserId = "cell"
    let disposeB = DisposeBag()
    var shouldShowSearchResults = false //是否显示搜索结果
    var vm = SearchViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        createTableViewAndSearchBar()
    }
        func createTableViewAndSearchBar() {
        self.title = "搜索界面"
        self.view.backgroundColor = UIColor.orange
        myTableView = UITableView(frame: self.view.bounds, style: .plain)
        myTableView.register(SectionTableCell.self, forCellReuseIdentifier: SectionTableCell.description())
        self.view.addSubview(myTableView)
        
        // searchVC 、 SearchBar
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchBar.placeholder = "请输入你要搜索的内容"
        searchBar.showsCancelButton = true
        myTableView.tableHeaderView = searchController.searchBar

    }
// cell
class SectionTableCell: UITableViewCell {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier: reuseIdentifier)
//        print(#function)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
2.将SearchViewModel和Tableview进行关联绑定
        // 双向绑定
        self.vm.searchData.drive(myTableView.rx.items) { (tb,row,model) -> UITableViewCell in
            let cell = tb.dequeueReusableCell(withIdentifier: SectionTableCell.description()) as? SectionTableCell
            cell?.textLabel?.text = model.name;
            cell?.detailTextLabel?.text = model.url;

            return cell!
        }.disposed(by: disposeB)

3.将SearchBar输入和SearchViewModel进行绑定
    //kvo
        // 将searchBar输入的值 绑定到VM里面的SearchText里面去
        searchBar.rx.text.orEmpty
            .bind(to: vm.searchText)
            .disposed(by: disposeB)

       myTableView.rx.didEndDecelerating
            .subscribe(onNext: { () in
                self.searchBar.endEditing(true)
            })
            .disposed(by: disposeB)

        // viewModel 检测值发生改变 响应到 title
        vm.searchData.asDriver()
            .map {[weak self] (resp) -> String in
                return "\(String(describing: self?.searchBar.text))有resp\(resp.count)"
        }
            .drive(navigationItem.rx.title)
            .disposed(by: disposeB)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇夜iOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值