RxSwift - двусторонняя привязка для использования одного и того же экрана для редактирования и добавления модели.

Я могу связать UITextField.rx.text с Variable<T>. Это отлично работает, когда я нахожусь на экране «создания». Но, предположим, у меня есть уже созданная модель и я хочу редактировать ее на том же экране, как мне это сделать?

Я также много видел об инфиксных операторах, представленных оператором <->. Если у меня не настроен этот инфиксный оператор, мне всегда придется выполнять такой объем работы для достижения двусторонней привязки?

Вот мой код

struct TodoViewModel: CustomDebugStringConvertible {
    let title = Variable<String?>("Initial title")
    let description = Variable<String?>("")
    let dueDate = Variable<Date?>(Date())
    let done = Variable<Bool>(false)

    var debugDescription: String {
        get {
            return
                """
                // ======
                Title: \(self.title.value ?? "Nil")
                Description: \(self.description.value ?? "Nil")
                Due Date: \(String(describing: self.dueDate.value))
                Done: \(self.done.value)
                """
        }
    }
}

// ViewController
class AddTaskTableViewController: UITableViewController {
    @IBOutlet weak var labelTitle: UILabel!
    @IBOutlet weak var txtTitle: UITextField!
    @IBOutlet weak var txtDescription: UITextField!
    @IBOutlet weak var txtDate: UITextField!
    @IBOutlet weak var switchDone: UISwitch!

    var todo = TodoViewModel()
    var disposeBag = DisposeBag()
    let dateFormatter = DateFormatter()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dateFormatter.locale = Locale(identifier: "en-US")

        // Configuring reactivity
        // Binds the UITextField's text value to my Model
        // let _ = self.txtTitle.rx.text.bind(to: self.todo.title).disposed(by: self.disposeBag)

        // ⬆⬇ Makes more sense they both being a two-way binding. This way I can edit and add items using the same screen.

        // Binds my model's title to the UITextView's text
        // let _ = self.todo.title.asObservable().bind(to: self.txtTitle.rx.text)
        let _ = self.txtDate.rx.text.map{ strDate in
            return self.dateFormatter.date(from: strDate!)
        }.bind(to: self.todo.dueDate).disposed(by: self.disposeBag)
        let _ = self.txtDescription.rx.text.bind(to: self.todo.description).disposed(by: self.disposeBag)
        let _ = self.switchDone.rx.isOn.bind(to: self.todo.done).disposed(by: self.disposeBag)

        let _ = self.todo.title.asObservable().bind(to: self.labelTitle.rx.text).disposed(by: self.disposeBag)
    }
}

person juniorgarcia    schedule 30.11.2017    source источник


Ответы (1)


У меня возникнет соблазн иметь два инициализатора: один для создания нового элемента списка дел и один для редактирования существующего:

let title: Variable<String?>
let description: Variable<String?>
let dueDate: Variable<Date?>
let done: Variable<Bool>

init() {
    self.title = Variable<String?>("Initial title")
    self.description = Variable<String?>("")
    self.dueDate = Variable<Date?>(Date())
    self.done = Variable<Bool>(false)
}

init(todo: TodoModel) {
    self.title = Variable<String?>(todo.title)
    self.description = Variable<String?>(todo.description)
    self.dueDate = Variable<Date?>(todo.dueDate)
    self.done = Variable<Bool>(todo.done)
}

Как вы заметили, инфиксный оператор <-> связывает переменную со свойством элемента управления и подписывается на изменения свойства элемента управления для обновления переменной. Он также объединяет их одноразовые элементы, чтобы они имели одинаковое время жизни, и гарантирует, что все будет удалено после завершения подписки на свойство элемента управления. Вот источник:

func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable {
    let bindToUIDisposable = variable.asObservable()
        .bindTo(property)
    let bindToVariable = property
        .subscribe(onNext: { n in
            variable.value = n
        }, onCompleted:  {
            bindToUIDisposable.dispose()
        })

    return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable)
}

Если вы хотите избежать использования оператора <->, вам нужно будет сделать все это для каждой двусторонней привязки самостоятельно.

Надеюсь, это поможет.

person Paul    schedule 14.02.2018