updated for model binding

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2022-08-12 15:40:16 -05:00
parent 11ad080a69
commit f2fc171914
3 changed files with 67 additions and 73 deletions

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21179.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21169.4"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -487,7 +488,7 @@
<rect key="frame" x="0.0" y="115" width="382" height="671"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="Hmj-Yn-sy0">
<rect key="frame" x="16" y="20" width="350" height="254"/>
<rect key="frame" x="16" y="20" width="350" height="210"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="sp0-qf-Npb">
<rect key="frame" x="0.0" y="0.0" width="350" height="31"/>
@ -594,25 +595,6 @@
</switch>
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="eSa-mA-9Ph">
<rect key="frame" x="0.0" y="220" width="350" height="34"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Error Text" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9EW-kc-8cA">
<rect key="frame" x="0.0" y="0.0" width="175" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="baM-I1-jMF">
<rect key="frame" x="175" y="0.0" width="175" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<action selector="onErrorTextDidEnd:" destination="h42-mH-KC1" eventType="editingDidEnd" id="ImV-fK-60i"/>
</connections>
</textField>
</subviews>
</stackView>
</subviews>
</stackView>
</subviews>
@ -645,7 +627,6 @@
<outlet property="childTextField" destination="G5B-ka-c0V" id="weU-KZ-YAB"/>
<outlet property="componentContainerView" destination="jWf-eh-zqg" id="1KC-GB-6bt"/>
<outlet property="disabledSwitch" destination="9XX-VQ-7L8" id="z36-Tq-9ER"/>
<outlet property="errorTextField" destination="baM-I1-jMF" id="Yam-GG-fRI"/>
<outlet property="labelTextField" destination="J94-IQ-J53" id="lfR-Ez-6H2"/>
<outlet property="picker" destination="JpA-y5-M2q" id="hqU-6W-iK0"/>
<outlet property="showErrorSwitch" destination="Fm6-Ej-sVV" id="MiC-BD-a4I"/>

View File

@ -26,12 +26,11 @@ class RadioButtonViewController: UIViewController, StoryboardInitable {
@IBOutlet weak var labelTextField: UITextField!
@IBOutlet weak var childTextField: UITextField!
@IBOutlet weak var showErrorSwitch: UISwitch!
@IBOutlet weak var errorTextField: UITextField!
// var radioButton: RadioButton!
// var radioButton2: RadioButton!
var radioButtonGroup = RadioButtonGroup()
@Published public var model = DefaultRadioButtonGroupModel()
public var model = DefaultRadioButtonGroupModel()
public var subscribers = Set<AnyCancellable>()
override func viewDidLoad() {
@ -59,30 +58,33 @@ class RadioButtonViewController: UIViewController, StoryboardInitable {
labelTextField.text = model1.labelText
childTextField.text = model1.childText
showErrorSwitch.isOn = model1.hasError
errorTextField.text = model1.errorText
model.selectors = [model1, model2]
radioButtonGroup.set(with: model)
componentContainerView.addSubview(radioButtonGroup)
//create the subject
let modelSubject = CurrentValueSubject<DefaultRadioButtonGroupModel, Never>(model)
$model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { viewModel in
guard let selectedModel = viewModel.selectedModel else { return }
print("RadioButtonViewController selectedModel Id: \(selectedModel.id)")
//assign - this will auto overwrite any changes
modelSubject.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).assign(to: \.model, on: self).store(in: &subscribers)
//bind
radioButtonGroup.createBinding(with: modelSubject, storeIn: &subscribers)
//print out on subject changes
modelSubject.sink { [weak self] model in
self?.showErrorSwitch.isOn = model.hasError
print("RadioButtonViewController hasError: \(model.hasError)")
if let selectedModel = model.selectedModel {
print("RadioButtonViewController selectedModel Id: \(selectedModel.id)")
}
}.store(in: &subscribers)
radioButtonGroup.modelPublisher.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { viewModel in
guard let selectedModel = viewModel.selectedModel else { return }
print("radioButtonGroup.modelPublisher selectedModel Id: \(selectedModel.id)")
print("RadioButtonViewController selectedModel Id: \(self.model.selectedModel?.id)")
}.store(in: &subscribers)
// radioButtonGroup.modelSubject.sink { [weak self] model in
// print("RadioButtonViewController model: \(model.selectedModel?.id)")
// print("RadioButtonViewController local.model: \(self?.model.selectedModel?.id)")
// }.store(in: &subscribers)
radioButtonGroup.leadingAnchor.constraint(equalTo: componentContainerView.leadingAnchor, constant: 10).isActive = true
radioButtonGroup.topAnchor.constraint(equalTo: componentContainerView.topAnchor, constant: 20).isActive = true
radioButtonGroup.bottomAnchor.constraint(equalTo: componentContainerView.bottomAnchor, constant: -20).isActive = true
@ -114,11 +116,6 @@ class RadioButtonViewController: UIViewController, StoryboardInitable {
radioButtonGroup.hasError = sender.isOn
}
@IBAction func onErrorTextDidEnd(_ sender: UITextField) {
radioButton?.errorText = sender.text
sender.resignFirstResponder()
}
@IBAction func surfaceClick(_ sender: Any) {
pickerType = .surface
}

View File

@ -8,8 +8,9 @@
import Foundation
import UIKit
import Combine
import VDS
class User: ObservableObject {
struct User {
var firstName: String
var lastName: String
@ -19,20 +20,29 @@ class User: ObservableObject {
}
}
class TextFieldCell: UIView {
public protocol Bindable {
associatedtype ModelType
var model: ModelType { get set }
var subject: CurrentValueSubject<ModelType, Never>? { get set }
func createBinding(with subject: CurrentValueSubject<ModelType, Never>, storeIn subscriptions: inout Set<AnyCancellable>)
}
extension Bindable {
public func send() {
subject?.send(model)
}
}
class TextFieldBindingCell: UIView, Bindable {
//bindable
var model: User
var subject: CurrentValueSubject<User, Never>?
let firstNameTextField = TextField()
let lastNameTextField = TextField()
var subject: CurrentValueSubject<User, Never>
private var subscriptions = Set<AnyCancellable>()
var model: User
private func send() {
subject.send(model)
}
init(subject: CurrentValueSubject<User, Never>) {
self.model = subject.value
self.subject = subject
init(model: User) {
self.model = model
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
@ -54,6 +64,16 @@ class TextFieldCell: UIView {
lastNameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
lastNameTextField.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createBinding(with subject: CurrentValueSubject<User, Never>, storeIn subscriptions: inout Set<AnyCancellable>) {
self.model = subject.value
self.subject = subject
//add the logic
let firstNameSubject = CurrentValueSubject<String, Never>(model.firstName)
firstNameTextField.createBinding(with: firstNameSubject, storeIn: &subscriptions)
@ -71,18 +91,12 @@ class TextFieldCell: UIView {
}.store(in: &subscriptions)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@objcMembers class TestViewController: UIViewController, Initable {
var user = User(firstName: "Joe", lastName: "User")
var nameTextField: TextFieldCell!
var button = UIButton()
private var cancellables = Set<AnyCancellable>()
private var subscriptions = Set<AnyCancellable>()
required init() {
super.init(nibName: nil, bundle: nil)
@ -97,8 +111,12 @@ class TextFieldCell: UIView {
view.backgroundColor = .white
let subject = CurrentValueSubject<User, Never>(user)
subject.assign(to: \.user, on: self).store(in: &cancellables)
nameTextField = TextFieldCell(subject: subject)
subject.assign(to: \.user, on: self).store(in: &subscriptions)
let nameTextField = TextFieldBindingCell(model: user)
nameTextField.createBinding(with: subject, storeIn: &subscriptions)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
@ -120,21 +138,19 @@ class TextFieldCell: UIView {
}
}), for: .touchUpInside)
}
}
extension UITextField {
func textPublisher() -> AnyPublisher<String, Never> {
public func textPublisher() -> AnyPublisher<String, Never> {
NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: self)
.compactMap({ ($0.object as? UITextField)?.text })
.eraseToAnyPublisher()
}
func createBinding(with subject: CurrentValueSubject<String, Never>,
public func createBinding(with subject: CurrentValueSubject<String, Never>,
storeIn subscriptions: inout Set<AnyCancellable>) {
subject.sink { [weak self] (value) in