Operators.swift 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. //
  2. // Operators.swift
  3. // O2Platform
  4. //
  5. // Created by FancyLou on 2018/7/23.
  6. // Copyright © 2018 zoneland. All rights reserved.
  7. //
  8. import RxSwift
  9. import RxCocoa
  10. import UIKit
  11. // Two way binding operator between control property and variable, that's all it takes {
  12. infix operator <-> : DefaultPrecedence
  13. func nonMarkedText(_ textInput: UITextInput) -> String? {
  14. let start = textInput.beginningOfDocument
  15. let end = textInput.endOfDocument
  16. guard let rangeAll = textInput.textRange(from: start, to: end),
  17. let text = textInput.text(in: rangeAll) else {
  18. return nil
  19. }
  20. guard let markedTextRange = textInput.markedTextRange else {
  21. return text
  22. }
  23. guard let startRange = textInput.textRange(from: start, to: markedTextRange.start),
  24. let endRange = textInput.textRange(from: markedTextRange.end, to: end) else {
  25. return text
  26. }
  27. return (textInput.text(in: startRange) ?? "") + (textInput.text(in: endRange) ?? "")
  28. }
  29. func <-> <Base: UITextInput>(textInput: TextInput<Base>, variable: Variable<String>) -> Disposable {
  30. let bindToUIDisposable = variable.asObservable()
  31. .bind(to: textInput.text)
  32. let bindToVariable = textInput.text
  33. .subscribe(onNext: { [weak base = textInput.base] n in
  34. guard let base = base else {
  35. return
  36. }
  37. let nonMarkedTextValue = nonMarkedText(base)
  38. /**
  39. In some cases `textInput.textRangeFromPosition(start, toPosition: end)` will return nil even though the underlying
  40. 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.
  41. The can be reproed easily if replace bottom code with
  42. if nonMarkedTextValue != variable.value {
  43. variable.value = nonMarkedTextValue ?? ""
  44. }
  45. and you hit "Done" button on keyboard.
  46. */
  47. if let nonMarkedTextValue = nonMarkedTextValue, nonMarkedTextValue != variable.value {
  48. variable.value = nonMarkedTextValue
  49. }
  50. }, onCompleted: {
  51. bindToUIDisposable.dispose()
  52. })
  53. return Disposables.create(bindToUIDisposable, bindToVariable)
  54. }
  55. func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable {
  56. if T.self == String.self {
  57. #if DEBUG
  58. 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" +
  59. "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" +
  60. "REMEDY: Just use `textField <-> variable` instead of `textField.rx.text <-> variable`.\n" +
  61. "Find out more here: https://github.com/ReactiveX/RxSwift/issues/649\n"
  62. )
  63. #endif
  64. }
  65. let bindToUIDisposable = variable.asObservable()
  66. .bind(to: property)
  67. let bindToVariable = property
  68. .subscribe(onNext: { n in
  69. variable.value = n
  70. }, onCompleted: {
  71. bindToUIDisposable.dispose()
  72. })
  73. return Disposables.create(bindToUIDisposable, bindToVariable)
  74. }