Двусторонняя привязка данных с использованием Combine - PullRequest
0 голосов
/ 06 апреля 2020

Я создаю пользовательский пошаговый режим многократного использования.

enter image description here

У меня все работает, и я использую Combine для наблюдения за изменениями значений. Однако я хотел бы улучшить его, используя двустороннюю привязку данных, но не уверен, что это возможно?

Вот мой текущий код:

class Stepper: UIView {
  override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
  }

  @Published var count = 0

  private let stackView = UIStackView()
  private let minusButton = BorderedRoundButton()
  private let plusButton = BorderedRoundButton()
  private let countLabel = UILabel()
  private var cancellables = Set<AnyCancellable>()

  func setup() {
    translatesAutoresizingMaskIntoConstraints = false
    stackView.translatesAutoresizingMaskIntoConstraints = false

    stackView.axis = .horizontal

    minusButton.setTitleColor(.primaryTint, for: .normal)
    plusButton.setTitleColor(.primaryTint, for: .normal)

    minusButton.setTitle("-", for: .normal)
    plusButton.setTitle("+", for: .normal)

    countLabel.text = "0"
    countLabel.textColor = .blackText
    countLabel.font = .preferredFont(forTextStyle: .body)
    countLabel.textAlignment = .center

    stackView.addArrangedSubview(minusButton)
    stackView.addArrangedSubview(countLabel)
    stackView.addArrangedSubview(plusButton)

    minusButton.tapPublisher.sink { [weak self] _ in
      self?.count -= 1
    }.store(in: &cancellables)

    plusButton.tapPublisher.sink { [weak self] _ in
      self?.count += 1
    }.store(in: &cancellables)

    $count.map { "\($0)" }.assign(to: \.text, on: countLabel).store(in: &cancellables)
    $count.map { $0 > 0 }.assign(to: \.isEnabled, on: minusButton).store(in: &cancellables)

    addSubview(stackView)

    NSLayoutConstraint.activate([
      minusButton.widthAnchor.constraint(equalToConstant: 38),
      minusButton.heightAnchor.constraint(equalToConstant: 38),

      plusButton.widthAnchor.constraint(equalToConstant: 38),
      plusButton.heightAnchor.constraint(equalToConstant: 38),

      countLabel.widthAnchor.constraint(equalToConstant: 44),

      stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
      stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
      stackView.topAnchor.constraint(equalTo: topAnchor),
      stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
    ])
  }
}

И я использую его вот так:

class PickerViewController: UIViewController {
  @IBOutlet private var stepper: Stepper!
  private var cancellables = Set<AnyCancellable>()

  @Published var count = 0

  override func viewDidLoad() {
    super.viewDidLoad()

    stepper.count = count

    stepper.$count.sink { [weak self] value in
      self?.count = value
    }.store(in: &cancellables)
  }
}

И это бит, который я хочу улучшить: теперь мне нужно оба установить начальное значение и затем наблюдать за изменениями. Я знаю, что это вряд ли много кода, но мне просто интересно, могу ли я сделать это в одну строку, с своего рода двусторонним связыванием данных? перестроил его версию, чтобы не импортировать SwiftUI), чтобы PickerViewController.count был единственным источником, и Stepper автоматически обновил бы его. Однако, тогда у меня проблема в том, что @Bindable сам по себе не наблюдается, поэтому эти строки больше не будут работать:

$count.map { "\($0)" }.assign(to: \.text, on: countLabel).store(in: &cancellables)
$count.map { $0 > 0 }.assign(to: \.isEnabled, on: minusButton).store(in: &cancellables)

Поэтому мой вопрос: могу ли я улучшить код для использования двустороннего связывания данных, или я должен просто придерживаться установки значения и затем наблюдать его отдельно?

...