From 1d5b631295528f7801c36f391aa7f0c4e04be040 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 20 May 2024 11:45:33 -0500 Subject: [PATCH 01/19] fixed issue with the dropdown not showing Signed-off-by: Matt Bruce --- VDS/Components/DropdownSelect/DropdownSelect.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 9c30ea03..944171f1 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -63,9 +63,6 @@ open class DropdownSelect: EntryFieldBase { /// Array of options to show open var options: [DropdownOptionModel] = [] { didSet { setNeedsUpdate() }} - /// A callback when the selected option changes. Passes parameters (option). - open var onItemSelected: ((Int, DropdownOptionModel) -> Void)? - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -177,6 +174,7 @@ open class DropdownSelect: EntryFieldBase { fieldStackView .publisher(for: UITapGestureRecognizer()) .sink { [weak self] _ in + print("touch occured") self?.launchPicker() } .store(in: &subscribers) @@ -355,6 +353,7 @@ open class DropdownSelect: EntryFieldBase { open override var canBecomeFirstResponder: Bool { true } open override func resignFirstResponder() -> Bool { + print("resign occurred") if dropdownField.isFirstResponder { dropdownField.resignFirstResponder() } @@ -375,7 +374,6 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { dropdownField.resignFirstResponder() } optionsPicker.isHidden = !optionsPicker.isHidden - setNeedsUpdate() } public func numberOfComponents(in pickerView: UIPickerView) -> Int { @@ -396,6 +394,5 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { selectId = row updateSelectedOptionLabel(option: options[row]) sendActions(for: .valueChanged) - self.onItemSelected?(row, options[row]) } } From 9d19464b57be978d652fe9d98cb9977b01427c17 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 20 May 2024 12:17:25 -0500 Subject: [PATCH 02/19] fixed issues with dropdown / inputField Signed-off-by: Matt Bruce --- VDS/Components/DropdownSelect/DropdownSelect.swift | 14 ++------------ VDS/Components/TextFields/EntryFieldBase.swift | 2 +- .../TextFields/InputField/InputField.swift | 13 +------------ 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 944171f1..e2217f04 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -171,10 +171,9 @@ open class DropdownSelect: EntryFieldBase { }() // tap gesture - fieldStackView + containerView .publisher(for: UITapGestureRecognizer()) .sink { [weak self] _ in - print("touch occured") self?.launchPicker() } .store(in: &subscribers) @@ -349,16 +348,6 @@ open class DropdownSelect: EntryFieldBase { titleLabelWidthConstraint?.constant = containerView.frame.width titleLabelWidthConstraint?.isActive = helperTextPlacement == .right } - - open override var canBecomeFirstResponder: Bool { true } - - open override func resignFirstResponder() -> Bool { - print("resign occurred") - if dropdownField.isFirstResponder { - dropdownField.resignFirstResponder() - } - return super.resignFirstResponder() - } } //-------------------------------------------------- @@ -374,6 +363,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { dropdownField.resignFirstResponder() } optionsPicker.isHidden = !optionsPicker.isHidden + updateContainerView() } public func numberOfComponents(in pickerView: UIPickerView) -> Int { diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index c7cd766c..f766fafd 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -307,7 +307,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - private func updateContainerView() { + internal func updateContainerView() { containerView.backgroundColor = backgroundColorConfiguration.getColor(self) containerView.layer.borderColor = isReadOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor containerView.layer.borderWidth = VDSFormControls.borderWidth diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 1886a9ab..fbb57f2b 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -323,21 +323,12 @@ open class InputField: EntryFieldBase { titleLabelWidthConstraint?.constant = containerView.frame.width titleLabelWidthConstraint?.isActive = helperTextPlacement == .right } - - open override var canBecomeFirstResponder: Bool { true } - - open override func resignFirstResponder() -> Bool { - if textField.isFirstResponder { - textField.resignFirstResponder() - } - return super.resignFirstResponder() - } } extension InputField: UITextFieldDelegate { public func textFieldDidBeginEditing(_ textField: UITextField) { fieldType.handler().textFieldDidBeginEditing(self, textField: textField) - setNeedsUpdate() + updateContainerView() } public func textFieldDidEndEditing(_ textField: UITextField) { @@ -349,8 +340,6 @@ extension InputField: UITextFieldDelegate { fieldType.handler().textFieldDidChangeSelection(self, textField: textField) if fieldType.handler().validateOnChange { validate() - } else { - setNeedsUpdate() } sendActions(for: .valueChanged) } From 675fbf8693835f11f425e5ecc07d65d12ac991d6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 21 May 2024 09:44:23 -0500 Subject: [PATCH 03/19] fixed keyboard issue Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 37 ++++++++++++++----- .../TextFields/InputField/TextField.swift | 22 +++++------ .../TextFields/TextArea/TextView.swift | 24 ++++++------ 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index e2217f04..a4a7c741 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -158,16 +158,17 @@ open class DropdownSelect: EntryFieldBase { optionsPicker.isHidden = true dropdownField.inputView = optionsPicker dropdownField.inputAccessoryView = { - let inputToolbar = UIToolbar().with { - $0.barStyle = .default - $0.isTranslucent = true - $0.items=[ - UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil), - UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: self, action: #selector(pickerDoneClicked)) - ] - } - inputToolbar.sizeToFit() - return inputToolbar + let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))) + accessView.backgroundColor = .white + accessView.addBorder(side: .top, width: 1, color: .lightGray) + let done = UIButton(type: .system) + done.setTitle("Done", for: .normal) + done.translatesAutoresizingMaskIntoConstraints = false + done.addTarget(self, action: #selector(pickerDoneClicked), for: .touchUpInside) + accessView.addSubview(done) + done.pinCenterY() + .pinTrailing(16) + return accessView }() // tap gesture @@ -348,6 +349,22 @@ open class DropdownSelect: EntryFieldBase { titleLabelWidthConstraint?.constant = containerView.frame.width titleLabelWidthConstraint?.isActive = helperTextPlacement == .right } + + open override var canBecomeFirstResponder: Bool { + return dropdownField.canBecomeFirstResponder + } + + open override func becomeFirstResponder() -> Bool { + return dropdownField.becomeFirstResponder() + } + + open override var canResignFirstResponder: Bool { + return dropdownField.canResignFirstResponder + } + + open override func resignFirstResponder() -> Bool { + return dropdownField.resignFirstResponder() + } } //-------------------------------------------------- diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index 933b8f47..182c1660 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -55,17 +55,17 @@ open class TextField: UITextField { } open func initialSetup() { - let doneToolbar: UIToolbar = UIToolbar() - doneToolbar.translatesAutoresizingMaskIntoConstraints = false - doneToolbar.barStyle = .default - - let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction)) - done.accessibilityHint = "Double tap to finish editing." - doneToolbar.items = [flexSpace, done] - doneToolbar.sizeToFit() - - inputAccessoryView = doneToolbar + let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))) + accessView.backgroundColor = .white + accessView.addBorder(side: .top, width: 1, color: .lightGray) + let done = UIButton(type: .system) + done.setTitle("Done", for: .normal) + done.translatesAutoresizingMaskIntoConstraints = false + done.addTarget(self, action: #selector(doneButtonAction), for: .touchUpInside) + accessView.addSubview(done) + done.pinCenterY() + .pinTrailing(16) + inputAccessoryView = accessView } @objc func doneButtonAction() { diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index efe8164b..b1faae53 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -104,19 +104,17 @@ open class TextView: UITextView, ViewProtocol { open func setup() { - translatesAutoresizingMaskIntoConstraints = false - let doneToolbar: UIToolbar = UIToolbar() - doneToolbar.translatesAutoresizingMaskIntoConstraints = false - doneToolbar.barStyle = .default - - let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction)) - done.accessibilityHint = "Double tap to finish editing." - doneToolbar.items = [flexSpace, done] - doneToolbar.sizeToFit() - - inputAccessoryView = doneToolbar - + let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))) + accessView.backgroundColor = .white + accessView.addBorder(side: .top, width: 1, color: .lightGray) + let done = UIButton(type: .system) + done.setTitle("Done", for: .normal) + done.translatesAutoresizingMaskIntoConstraints = false + done.addTarget(self, action: #selector(doneButtonAction), for: .touchUpInside) + accessView.addSubview(done) + done.pinCenterY() + .pinTrailing(16) + inputAccessoryView = accessView } @objc func doneButtonAction() { From a4d9fd0ccc0b05393601397a6f2ac3aa0963993b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 21 May 2024 09:49:56 -0500 Subject: [PATCH 04/19] updated responder methods Signed-off-by: Matt Bruce --- .../TextFields/InputField/InputField.swift | 16 ++++++++++++++++ .../TextFields/TextArea/TextArea.swift | 19 +++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index fbb57f2b..60adf47a 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -323,6 +323,22 @@ open class InputField: EntryFieldBase { titleLabelWidthConstraint?.constant = containerView.frame.width titleLabelWidthConstraint?.isActive = helperTextPlacement == .right } + + open override var canBecomeFirstResponder: Bool { + return textField.canBecomeFirstResponder + } + + open override func becomeFirstResponder() -> Bool { + return textField.becomeFirstResponder() + } + + open override var canResignFirstResponder: Bool { + return textField.canResignFirstResponder + } + + open override func resignFirstResponder() -> Bool { + return textField.resignFirstResponder() + } } extension InputField: UITextFieldDelegate { diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index eccd228e..2333a17f 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -253,13 +253,20 @@ open class TextArea: EntryFieldBase { } - open override var canBecomeFirstResponder: Bool { true } - + open override var canBecomeFirstResponder: Bool { + return textView.canBecomeFirstResponder + } + + open override func becomeFirstResponder() -> Bool { + return textView.becomeFirstResponder() + } + + open override var canResignFirstResponder: Bool { + return textView.canResignFirstResponder + } + open override func resignFirstResponder() -> Bool { - if textView.isFirstResponder { - textView.resignFirstResponder() - } - return super.resignFirstResponder() + return textView.resignFirstResponder() } //-------------------------------------------------- From 522624b403f1076561ae08d1e6636da6b95c371c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 21 May 2024 14:05:37 -0500 Subject: [PATCH 05/19] initial fix Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 41 +-- .../TextFields/EntryFieldBase.swift | 273 ++++++++++-------- .../TextFields/InputField/InputField.swift | 42 +-- 3 files changed, 161 insertions(+), 195 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index a4a7c741..ac5cb29b 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -111,16 +111,12 @@ open class DropdownSelect: EntryFieldBase { $0.font = TextStyle.bodyLarge.font } - /// Determines the placement of the helper text. - open var helperTextPlacement: HelperTextPlacement = .bottom { didSet { setNeedsUpdate() } } - open var optionsPicker = UIPickerView() //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- internal var inlineWidthConstraint: NSLayoutConstraint? - internal var titleLabelWidthConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Configuration Properties @@ -133,13 +129,8 @@ open class DropdownSelect: EntryFieldBase { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { - super.setup() - widthConstraint?.activate() + super.setup() - titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) - titleLabel.setContentHuggingPriority(.required, for: .horizontal) - titleLabelWidthConstraint = titleLabel.width(constant: 0) - fieldStackView.isAccessibilityElement = true fieldStackView.accessibilityLabel = "Dropdown Select" inlineDisplayLabel.isAccessibilityElement = true @@ -285,28 +276,6 @@ open class DropdownSelect: EntryFieldBase { statusIcon.color = iconColorConfiguration.getColor(self) } - open override func updateHelperLabel(){ - //remove first - secondaryStackView.removeFromSuperview() - helperLabel.removeFromSuperview() - - super.updateHelperLabel() - - //set the helper label position - if helperText != nil { - if helperTextPlacement == .right { - horizontalStackView.addArrangedSubview(secondaryStackView) - horizontalStackView.addArrangedSubview(helperLabel) - primaryStackView.addArrangedSubview(horizontalStackView) - } else { - bottomContainerStackView.addArrangedSubview(helperLabel) - primaryStackView.addArrangedSubview(secondaryStackView) - } - } else { - primaryStackView.addArrangedSubview(secondaryStackView) - } - } - open override func updateAccessibility() { super.updateAccessibility() let selectedOption = selectedOptionLabel.text ?? "" @@ -343,13 +312,7 @@ open class DropdownSelect: EntryFieldBase { setNeedsUpdate() UIAccessibility.post(notification: .layoutChanged, argument: fieldStackView) } - - open override func layoutSubviews() { - super.layoutSubviews() - titleLabelWidthConstraint?.constant = containerView.frame.width - titleLabelWidthConstraint?.isActive = helperTextPlacement == .right - } - + open override var canBecomeFirstResponder: Bool { return dropdownField.canBecomeFirstResponder } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index f766fafd..631cf029 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -36,38 +36,41 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { public enum HelperTextPlacement: String, CaseIterable { case bottom, right } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var primaryStackView: UIStackView = { - return UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .vertical - $0.distribution = .fill - $0.alignment = .leading - } - }() - - /// This is the veritcal stack view that has 2 rows, the containerView and the return view - /// of the getBottomContainer() method, by default returns the bottomContainerStackView. - internal let secondaryStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false + internal let mainStackView = UIStackView().with { $0.axis = .vertical - $0.distribution = .fill + $0.alignment = .fill + $0.spacing = 8 + $0.translatesAutoresizingMaskIntoConstraints = false } - /// This is the view that will be wrapped with the border for userInteraction. - /// The only subview of this view is the fieldStackView - internal var containerView: UIView = { - return UIView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - }() + internal let contentStackView = UIStackView().with { + $0.axis = .vertical + $0.alignment = .fill + $0.distribution = .fill + $0.spacing = 8 + $0.translatesAutoresizingMaskIntoConstraints = false + } + + /// only used for helperTextPosition == .right + internal let row1StackView = UIStackView().with { + $0.axis = .horizontal + $0.spacing = 8 + $0.alignment = .top + $0.distribution = .fillEqually + } + + /// only used for helperTextPosition == .right + internal let row2StackView = UIStackView().with { + $0.axis = .horizontal + $0.spacing = 8 + $0.alignment = .top + $0.distribution = .fillEqually + } - /// This is a horizontal Stack View that is placed inside the containterView (bordered view) - /// The first arrangedView will be the view from getFieldContainer() - /// The second view is the statusIcon. internal var fieldStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -86,9 +89,18 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.spacing = VDSLayout.space2X } }() + + /// This is the view that will be wrapped with the border for userInteraction. + /// The only subview of this view is the fieldStackView + internal var containerView: UIView = { + return UIView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + }() + + /// This is set by a local method. + internal var bottomContainerView: UIView! - open var rules = [AnyRule]() - //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- @@ -136,7 +148,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var onChangeSubscriber: AnyCancellable? + open var onChangeSubscriber: AnyCancellable? open var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) @@ -199,36 +211,34 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open var isRequired: Bool = false { didSet { setNeedsUpdate() } } open var isReadOnly: Bool = false { didSet { setNeedsUpdate() } } - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - internal var heightConstraint: NSLayoutConstraint? - internal var widthConstraint: NSLayoutConstraint? - + + open var helperTextPlacement: HelperTextPlacement = .bottom { + didSet { + updateHelperTextPosition() + } + } + + open var rules = [AnyRule]() + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - - isAccessibilityElement = false - addSubview(primaryStackView) - //create the wrapping view - heightConstraint = containerView.heightGreaterThanEqualTo(constant: containerSize.height) - widthConstraint = containerView.width(constant: frame.size.width) - - secondaryStackView.addArrangedSubview(containerView) - secondaryStackView.setCustomSpacing(8, after: containerView) + // Add mainStackView to the view + addSubview(mainStackView) + + mainStackView.pinToSuperView() //add ContainerStackView //this is the horizontal stack that contains - //the left, InputContainer, Icons, Buttons + //InputContainer, Icons, Buttons containerView.addSubview(fieldStackView) fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X)) - + let fieldContainerView = getFieldContainer() fieldContainerView.translatesAutoresizingMaskIntoConstraints = false @@ -239,29 +249,33 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //get the container this is what show helper text, error text //can include other for character count, max length - let bottomContainer = getBottomContainer() + bottomContainerView = getBottomContainer() //this is the vertical stack that contains error text, helper text bottomContainerStackView.addArrangedSubview(errorLabel) bottomContainerStackView.addArrangedSubview(helperLabel) - primaryStackView.addArrangedSubview(titleLabel) - primaryStackView.addArrangedSubview(secondaryStackView) - secondaryStackView.addArrangedSubview(bottomContainer) - - primaryStackView.setCustomSpacing(4, after: titleLabel) - - primaryStackView - .pinTop() - .pinLeading() - .pinTrailing(0, .defaultHigh) - .pinBottom(0, .defaultHigh) - - titleLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() - errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() - helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable() + // Add arranged subviews to textFieldStackView + contentStackView.addArrangedSubview(containerView) + contentStackView.addArrangedSubview(bottomContainerView) + + // Add arranged subviews to mainStackView + mainStackView.addArrangedSubview(titleLabel) + mainStackView.addArrangedSubview(contentStackView) + + // Initial position of the helper label + updateHelperTextPosition() } - + + /// Updates the UI + open override func updateView() { + super.updateView() + updateContainerView() + updateTitleLabel() + updateErrorLabel() + updateHelperLabel() + } + /// Resets to default settings. open override func reset() { super.reset() @@ -284,48 +298,12 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { defaultValue = nil isRequired = false isReadOnly = false - onChange = nil + onChange = nil } - - /// Used to make changes to the View based off a change events or from local properties. - open override func updateView() { - super.updateView() - updateContainerView() - updateTitleLabel() - updateErrorLabel() - updateHelperLabel() - updateContainerWidth() - } - - open func validate(){ - updateRules() - validator = FormFieldValidator(field: self, rules: rules) - validator?.validate() - setNeedsUpdate() - } - - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- - internal func updateContainerView() { - containerView.backgroundColor = backgroundColorConfiguration.getColor(self) - containerView.layer.borderColor = isReadOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor - containerView.layer.borderWidth = VDSFormControls.borderWidth - containerView.layer.cornerRadius = VDSFormControls.borderRadius - } - + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- - open func updateContainerWidth() { - if let width, width > minWidth && width < maxWidth { - widthConstraint?.constant = width - } else { - widthConstraint?.constant = maxWidth >= minWidth ? maxWidth : minWidth - } - widthConstraint?.activate() - } - /// Container for the area in which the user interacts. open func getFieldContainer() -> UIView { fatalError("Subclass must return the view that contains the field/view the user will interact with.") @@ -335,22 +313,14 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open func getBottomContainer() -> UIView { return bottomContainerStackView } - - internal func updateRules() { - rules.removeAll() - if self.isRequired { - let rule = RequiredRule() - if let errorText, !errorText.isEmpty { - rule.errorMessage = errorText - } else if let labelText{ - rule.errorMessage = "You must enter a \(labelText)" - } else { - rule.errorMessage = "You must enter a value" - } - rules.append(.init(rule)) - } + + open func validate(){ + updateRules() + validator = FormFieldValidator(field: self, rules: rules) + validator?.validate() + setNeedsUpdate() } - + open func updateTitleLabel() { //update the local vars for the label since we no @@ -380,7 +350,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { titleLabel.surface = surface titleLabel.isEnabled = isEnabled } - + open func updateErrorLabel(){ if showError, let errorText { errorLabel.text = errorText @@ -416,4 +386,73 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { helperLabel.isHidden = true } } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + internal func updateRules() { + rules.removeAll() + if self.isRequired { + let rule = RequiredRule() + if let errorText, !errorText.isEmpty { + rule.errorMessage = errorText + } else if let labelText{ + rule.errorMessage = "You must enter a \(labelText)" + } else { + rule.errorMessage = "You must enter a value" + } + rules.append(.init(rule)) + } + } + + internal func updateContainerView() { + containerView.backgroundColor = backgroundColorConfiguration.getColor(self) + containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + containerView.layer.borderWidth = VDSFormControls.borderWidth + containerView.layer.cornerRadius = VDSFormControls.borderRadius + } + + internal func updateHelperTextPosition() { + + titleLabel.removeFromSuperview() + helperLabel.removeFromSuperview() + + contentStackView.removeFromSuperview() + mainStackView.removeArrangedSubviews() + + //rows for helper-right + row1StackView.removeArrangedSubviews() + row2StackView.removeArrangedSubviews() + row1StackView.removeFromSuperview() + row2StackView.removeFromSuperview() + + switch helperTextPlacement { + case .bottom: + //add helper back into the contentView + bottomContainerStackView.addArrangedSubview(helperLabel) + mainStackView.addArrangedSubview(titleLabel) + mainStackView.addArrangedSubview(contentStackView) + + case .right: + //first row + row1StackView.addArrangedSubview(titleLabel) + //add spacer + row1StackView.addArrangedSubview(UIView()) + + //second row + row2StackView.addArrangedSubview(contentStackView) + //add under spacer + row2StackView.addArrangedSubview(helperLabel) + + //add 2 rows to vertical stack to create the grid + mainStackView.addArrangedSubview(row1StackView) + mainStackView.addArrangedSubview(row2StackView) + } + } +} + +extension UIStackView { + public func removeArrangedSubviews() { + arrangedSubviews.forEach { removeArrangedSubview($0) } + } } diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 60adf47a..737404d4 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -34,7 +34,6 @@ open class InputField: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var titleLabelWidthConstraint: NSLayoutConstraint? internal override var minWidth: CGFloat { fieldType.handler().minWidth } internal override var maxWidth: CGFloat { let frameWidth = frame.size.width @@ -172,25 +171,17 @@ open class InputField: EntryFieldBase { /// If given, this will be shown if showSuccess if true. open var successText: String? { didSet { setNeedsUpdate() } } - - /// Determines the placement of the helper text. - open var helperTextPlacement: HelperTextPlacement = .bottom { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { - super.setup() - - titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) - titleLabel.setContentHuggingPriority(.required, for: .horizontal) - titleLabelWidthConstraint = titleLabel.width(constant: 0) - + super.setup() textField.heightAnchor.constraint(equalToConstant: 20).isActive = true textField.delegate = self - primaryStackView.addArrangedSubview(successLabel) - primaryStackView.setCustomSpacing(8, after: successLabel) + mainStackView.addArrangedSubview(successLabel) + mainStackView.setCustomSpacing(8, after: successLabel) fieldStackView.addArrangedSubview(actionTextLink) @@ -262,27 +253,6 @@ open class InputField: EntryFieldBase { } } - open override func updateHelperLabel(){ - //remove first - secondaryStackView.removeFromSuperview() - helperLabel.removeFromSuperview() - - super.updateHelperLabel() - - //set the helper label position - if helperText != nil { - if helperTextPlacement == .right { - horizontalStackView.addArrangedSubview(secondaryStackView) - horizontalStackView.addArrangedSubview(helperLabel) - primaryStackView.addArrangedSubview(horizontalStackView) - } else { - bottomContainerStackView.addArrangedSubview(helperLabel) - primaryStackView.addArrangedSubview(secondaryStackView) - } - } else { - primaryStackView.addArrangedSubview(secondaryStackView) - } - } override func updateRules() { super.updateRules() @@ -318,12 +288,6 @@ open class InputField: EntryFieldBase { set { super.accessibilityElements = newValue } } - open override func layoutSubviews() { - super.layoutSubviews() - titleLabelWidthConstraint?.constant = containerView.frame.width - titleLabelWidthConstraint?.isActive = helperTextPlacement == .right - } - open override var canBecomeFirstResponder: Bool { return textField.canBecomeFirstResponder } From e70d160619bf3182932a22e9e5264bdefbf49827 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 21 May 2024 14:44:20 -0500 Subject: [PATCH 06/19] more refactoring Signed-off-by: Matt Bruce --- .../TextFields/InputField/InputField.swift | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 737404d4..70842973 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -97,16 +97,7 @@ open class InputField: EntryFieldBase { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) }.eraseToAnyColorable() - - open var leftImageView = UIImageView().with { - $0.height(20) - $0.width(32) - $0.isAccessibilityElement = false - $0.translatesAutoresizingMaskIntoConstraints = false - $0.contentMode = .scaleAspectFill - $0.clipsToBounds = true - } - + open var actionTextLink = TextLink().with { $0.contentEdgeInsets = .top(-2) } open var actionTextLinkModel: TextLinkModel? { didSet { setNeedsUpdate() } } @@ -177,11 +168,10 @@ open class InputField: EntryFieldBase { //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { - super.setup() + super.setup() textField.heightAnchor.constraint(equalToConstant: 20).isActive = true textField.delegate = self - mainStackView.addArrangedSubview(successLabel) - mainStackView.setCustomSpacing(8, after: successLabel) + bottomContainerStackView.insertArrangedSubview(successLabel, at: 0) fieldStackView.addArrangedSubview(actionTextLink) @@ -194,15 +184,7 @@ open class InputField: EntryFieldBase { } open override func getFieldContainer() -> UIView { - // stackview for controls in EntryFieldBase.controlContainerView - let stackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .horizontal - $0.spacing = VDSLayout.space3X - } - stackView.addArrangedSubview(leftImageView) - stackView.addArrangedSubview(textField) - return stackView + return textField } /// Resets to default settings. @@ -322,6 +304,7 @@ extension InputField: UITextFieldDelegate { validate() } sendActions(for: .valueChanged) + setNeedsUpdate() } public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { From 3bc2b18962188ee3b37b6e80a96c5872af8632b7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 21 May 2024 14:44:44 -0500 Subject: [PATCH 07/19] updated field types Signed-off-by: Matt Bruce --- .../InputField/FieldTypes/CreditCard.swift | 45 ++++++++++++++----- .../InputField/FieldTypes/FieldType.swift | 12 ++--- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/VDS/Components/TextFields/InputField/FieldTypes/CreditCard.swift b/VDS/Components/TextFields/InputField/FieldTypes/CreditCard.swift index 7137d895..43fd6309 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/CreditCard.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/CreditCard.swift @@ -20,7 +20,7 @@ extension InputField { case jcb case unionPay - func imageName(surface: Surface) -> String { + public func imageName(surface: Surface) -> String { func getImageName(_ surface: Surface, name: String) -> String { return surface == .light ? name : "\(name)-inverted" } @@ -98,12 +98,35 @@ extension InputField { self.keyboardType = .numberPad } - override func updateView(_ inputField: InputField) { - minWidth = 288.0 - leftImageName = inputField.cardType.imageName(surface: inputField.surface) - super.updateView(inputField) + fileprivate func updateLeftImage(_ inputField: InputField) { + let imageName = inputField.cardType.imageName(surface: inputField.surface) + creditCardImageView.image = BundleManager.shared.image(for: imageName) } + override func updateView(_ inputField: InputField) { + minWidth = 288.0 + super.updateView(inputField) + + // Set the UIImageView as the left view of the UITextField + let iconContainerView: UIView = UIView() + iconContainerView.addSubview(creditCardImageView) + creditCardImageView.pinToSuperView(.init(top: 0, left: 0, bottom: 0, right: 10)) + + inputField.textField.leftView = iconContainerView + inputField.textField.leftViewMode = .always + + updateLeftImage(inputField) + } + + internal var creditCardImageView = UIImageView().with { + $0.height(20) + $0.width(32) + $0.isAccessibilityElement = false + $0.translatesAutoresizingMaskIntoConstraints = false + $0.contentMode = .scaleAspectFill + $0.clipsToBounds = true + } + override func appendRules(_ inputField: InputField) { if let text = inputField.textField.text, text.count > 0 { let rule = CharacterCountRule().copyWith { @@ -120,6 +143,8 @@ extension InputField { value = nil inputField.cardType = .generic textField.text = "" + inputField.validate() + updateLeftImage(inputField) } override func textFieldDidEndEditing(_ inputField: InputField, textField: UITextField) { @@ -183,14 +208,12 @@ extension InputField { } internal func updateCardTypeIcon(_ inputField: InputField, rawNumber: String) { - defer { inputField.setNeedsUpdate() } - - guard rawNumber.count >= 4 else { + if rawNumber.count >= 4 { + inputField.cardType = CreditCardType.from(cardNumber: rawNumber) + } else { inputField.cardType = .generic - return } - - inputField.cardType = CreditCardType.from(cardNumber: rawNumber) + updateLeftImage(inputField) } internal func maskCreditCardNumber(_ cardType: CreditCardType, number: String) -> String { diff --git a/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift b/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift index ebecb17d..f16f8c51 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift @@ -39,7 +39,6 @@ extension InputField { class FieldTypeHandler: NSObject { var keyboardType: UIKeyboardType var minWidth: CGFloat = 40.0 - var leftImageName: String? var actionModel: TextLinkModel? var toolTipModel: Tooltip.TooltipModel? var isSecureTextEntry = false @@ -51,7 +50,7 @@ extension InputField { keyboardType = .default super.init() } - + func updateView(_ inputField: InputField) { //keyboard @@ -59,15 +58,10 @@ extension InputField { //textField inputField.textField.isSecureTextEntry = isSecureTextEntry - - //leftIcon - if let leftImageName { - inputField.leftImageView.image = BundleManager.shared.image(for: leftImageName) - } - inputField.leftImageView.isHidden = leftImageName == nil //actionLink inputField.actionTextLink.surface = inputField.surface + inputField.actionTextLink.isEnabled = inputField.isEnabled if let actionModel { inputField.actionTextLink.text = actionModel.text inputField.actionTextLink.onClick = { _ in @@ -88,6 +82,8 @@ extension InputField { if let toolTipModel { inputField.tooltipModel = toolTipModel } + + inputField.textField.leftView = nil } func appendRules(_ inputField: InputField) {} From dc4db4816db5049189e04050755276de60188948 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 08:45:31 -0500 Subject: [PATCH 08/19] pushed out fix for using surface color config Signed-off-by: Matt Bruce --- VDS/Components/Icon/Icon.swift | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/VDS/Components/Icon/Icon.swift b/VDS/Components/Icon/Icon.swift index 09c1ffec..e5069f8f 100644 --- a/VDS/Components/Icon/Icon.swift +++ b/VDS/Components/Icon/Icon.swift @@ -56,10 +56,12 @@ open class Icon: View { if let hex = color.hexString, !UIColor.isVDSColor(color: color) { print("icon.color is not a VDSColor. Hex: \(hex) is not a supported color") } - setNeedsUpdate() + colorConfiguration = SurfaceColorConfiguration(color, color) } } + open var colorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) { didSet { setNeedsUpdate() } } + /// Size of the icon. open var size: Size = .medium { didSet { setNeedsUpdate() } } @@ -98,15 +100,8 @@ open class Icon: View { open override func updateView() { super.updateView() //get the color for the image - var imageColor = color - - //ensure the correct color for white/black colors - if surface == .dark && color == VDSColor.paletteBlack { - imageColor = VDSColor.elementsPrimaryOndark - } else if surface == .light && color == VDSColor.paletteBlack { - imageColor = VDSColor.elementsPrimaryOnlight - } - + let imageColor = colorConfiguration.getColor(surface) + //get the image name //set the image if let name, let image = UIImage.image(for: name, color: imageColor) { From 8c3bce746e48a2951e24c9c52960bf16740dd5f6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 15:33:30 -0500 Subject: [PATCH 09/19] refactored models Signed-off-by: Matt Bruce --- VDS/Components/Tilelet/Tilelet.swift | 4 +-- .../Tilelet/TileletIconModels.swift | 32 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 98ce49aa..104d3551 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -532,7 +532,7 @@ open class Tilelet: TileContainerBase { var showIconContainerView = false if let descriptiveIconModel { descriptiveIcon.name = descriptiveIconModel.name - descriptiveIcon.color = descriptiveIconModel.color + descriptiveIcon.colorConfiguration = descriptiveIconModel.colorConfiguration descriptiveIcon.size = descriptiveIconModel.size descriptiveIcon.surface = backgroundColorSurface descriptiveIcon.accessibilityLabel = descriptiveIconModel.accessibleText @@ -541,7 +541,7 @@ open class Tilelet: TileContainerBase { if let directionalIconModel { directionalIcon.name = directionalIconModel.iconType.iconName - directionalIcon.color = directionalIconModel.color + directionalIcon.colorConfiguration = directionalIconModel.colorConfiguration directionalIcon.size = directionalIconModel.size directionalIcon.surface = backgroundColorSurface directionalIcon.accessibilityLabel = directionalIconModel.accessibleText diff --git a/VDS/Components/Tilelet/TileletIconModels.swift b/VDS/Components/Tilelet/TileletIconModels.swift index 788a2155..30048a3e 100644 --- a/VDS/Components/Tilelet/TileletIconModels.swift +++ b/VDS/Components/Tilelet/TileletIconModels.swift @@ -17,23 +17,23 @@ extension Tilelet { public var name: Icon.Name /// Color of the icon. - public var color: UIColor + public var colorConfiguration: SurfaceColorConfiguration /// Enum for a preset height and width for the icon. public var size: Icon.Size /// Accessible Text for the Icon public var accessibleText: String - - /// Current Surface and this is used to pass down to child objects that implement Surfacable - public var surface: Surface - public init(name: Icon.Name = .multipleDocuments, color: UIColor = VDSColor.paletteBlack, size: Icon.Size = .medium, accessibleText: String? = nil, surface: Surface = .dark) { + public init(name: Icon.Name = .multipleDocuments, + colorConfiguration: SurfaceColorConfiguration = .init(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark), + size: Icon.Size = .medium, + accessibleText: String? = nil) { + self.name = name - self.color = color + self.colorConfiguration = colorConfiguration self.accessibleText = accessibleText ?? name.rawValue self.size = size - self.surface = surface } } @@ -49,8 +49,8 @@ extension Tilelet { } /// Color of the icon. - public var color: UIColor - + public var colorConfiguration: SurfaceColorConfiguration + /// Accessible Text for the Icon public var accessibleText: String @@ -59,16 +59,16 @@ extension Tilelet { /// Enum for a preset height and width for the icon. public var size: Icon.Size - - /// Current Surface and this is used to pass down to child objects that implement Surfacable - public var surface: Surface - - public init(iconType: IconType = .rightArrow, color: UIColor = VDSColor.paletteBlack, size: Icon.Size = .medium, accessibleText: String? = nil, surface: Surface = .dark) { + + public init(iconType: IconType = .rightArrow, + colorConfiguration: SurfaceColorConfiguration = .init(.black, .white), + size: Icon.Size = .medium, + accessibleText: String? = nil) { + self.iconType = iconType - self.color = color + self.colorConfiguration = colorConfiguration self.accessibleText = accessibleText ?? iconType.iconName.rawValue self.size = size - self.surface = surface } } } From 1d1bab13d07048c81f9c41a2b7eb72e48ab3f098 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 15:34:43 -0500 Subject: [PATCH 10/19] udpated spacing Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 631cf029..02767152 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -58,7 +58,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// only used for helperTextPosition == .right internal let row1StackView = UIStackView().with { $0.axis = .horizontal - $0.spacing = 8 + $0.spacing = VDSLayout.space3X $0.alignment = .top $0.distribution = .fillEqually } @@ -66,7 +66,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// only used for helperTextPosition == .right internal let row2StackView = UIStackView().with { $0.axis = .horizontal - $0.spacing = 8 + $0.spacing = VDSLayout.space3X $0.alignment = .top $0.distribution = .fillEqually } From ad1d2bf945789da94436845fcb8c8005fe6932f9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 15:45:14 -0500 Subject: [PATCH 11/19] updated models Signed-off-by: Matt Bruce --- VDS/Components/Tilelet/TileletIconModels.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Tilelet/TileletIconModels.swift b/VDS/Components/Tilelet/TileletIconModels.swift index 30048a3e..1772f817 100644 --- a/VDS/Components/Tilelet/TileletIconModels.swift +++ b/VDS/Components/Tilelet/TileletIconModels.swift @@ -61,7 +61,7 @@ extension Tilelet { public var size: Icon.Size public init(iconType: IconType = .rightArrow, - colorConfiguration: SurfaceColorConfiguration = .init(.black, .white), + colorConfiguration: SurfaceColorConfiguration = .init(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark), size: Icon.Size = .medium, accessibleText: String? = nil) { From 2e2beee8c37c22453dab23ae824837f63290ba48 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 16:28:30 -0500 Subject: [PATCH 12/19] fixed bug in word wrapping Signed-off-by: Matt Bruce --- VDS/Components/DropdownSelect/DropdownSelect.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index ac5cb29b..cf3539fb 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -93,7 +93,7 @@ open class DropdownSelect: EntryFieldBase { $0.setContentCompressionResistancePriority(.required, for: .horizontal) $0.textAlignment = .left $0.textStyle = .boldBodyLarge - $0.lineBreakMode = .byCharWrapping + $0.numberOfLines = 1 $0.sizeToFit() } @@ -102,7 +102,7 @@ open class DropdownSelect: EntryFieldBase { $0.setContentCompressionResistancePriority(.required, for: .horizontal) $0.textAlignment = .left $0.textStyle = .bodyLarge - $0.lineBreakMode = .byCharWrapping + $0.numberOfLines = 1 } open var dropdownField = UITextField().with { From 44323a0a847cc58284f8ae0d85641b420da0d0f0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 16:28:44 -0500 Subject: [PATCH 13/19] fixed width issues reset label color configs Signed-off-by: Matt Bruce --- .../TextFields/EntryFieldBase.swift | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 02767152..2c2435be 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -101,6 +101,13 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// This is set by a local method. internal var bottomContainerView: UIView! + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + internal var widthConstraint: NSLayoutConstraint? + internal var trailingEqualsConstraint: NSLayoutConstraint? + internal var trailingLessThanEqualsConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- @@ -228,11 +235,27 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open override func setup() { super.setup() + let layoutGuide = UILayoutGuide() + addLayoutGuide(layoutGuide) + layoutGuide + .pinTop() + .pinLeading() + .pinBottom() + + trailingEqualsConstraint = layoutGuide.pinTrailing(anchor: trailingAnchor) + + // width constraints + trailingLessThanEqualsConstraint = layoutGuide.pinTrailingLessThanOrEqualTo(anchor: trailingAnchor)?.deactivate() + widthConstraint = layoutGuide.widthAnchor.constraint(equalToConstant: 0).deactivate() + // Add mainStackView to the view addSubview(mainStackView) - - mainStackView.pinToSuperView() - + + mainStackView.pinTop(anchor: layoutGuide.topAnchor) + mainStackView.pinLeading(anchor: layoutGuide.leadingAnchor) + mainStackView.pinBottom(anchor: layoutGuide.bottomAnchor) + mainStackView.pinTrailing(anchor: layoutGuide.trailingAnchor) + //add ContainerStackView //this is the horizontal stack that contains //InputContainer, Icons, Buttons @@ -265,12 +288,18 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { // Initial position of the helper label updateHelperTextPosition() + + // colorconfigs + titleLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable() } /// Updates the UI open override func updateView() { super.updateView() updateContainerView() + updateContainerWidth() updateTitleLabel() updateErrorLabel() updateHelperLabel() @@ -411,7 +440,21 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { containerView.layer.borderWidth = VDSFormControls.borderWidth containerView.layer.cornerRadius = VDSFormControls.borderRadius } - + + internal func updateContainerWidth() { + widthConstraint?.deactivate() + trailingLessThanEqualsConstraint?.deactivate() + trailingEqualsConstraint?.deactivate() + + if let width, width >= minWidth, width <= maxWidth { + widthConstraint?.constant = width + widthConstraint?.activate() + trailingLessThanEqualsConstraint?.activate() + } else { + trailingEqualsConstraint?.activate() + } + } + internal func updateHelperTextPosition() { titleLabel.removeFromSuperview() From 4ba2b4e744352c499bb5e1ca407dc9b84b366cef Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 22 May 2024 16:54:33 -0500 Subject: [PATCH 14/19] updated tilelet directional icon model Signed-off-by: Matt Bruce --- VDS/Components/Tilelet/Tilelet.swift | 2 +- VDS/Components/Tilelet/TileletIconModels.swift | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 104d3551..5dcd5a09 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -542,7 +542,7 @@ open class Tilelet: TileContainerBase { if let directionalIconModel { directionalIcon.name = directionalIconModel.iconType.iconName directionalIcon.colorConfiguration = directionalIconModel.colorConfiguration - directionalIcon.size = directionalIconModel.size + directionalIcon.size = directionalIconModel.size.value directionalIcon.surface = backgroundColorSurface directionalIcon.accessibilityLabel = directionalIconModel.accessibleText showIconContainerView = true diff --git a/VDS/Components/Tilelet/TileletIconModels.swift b/VDS/Components/Tilelet/TileletIconModels.swift index 1772f817..7fc408a3 100644 --- a/VDS/Components/Tilelet/TileletIconModels.swift +++ b/VDS/Components/Tilelet/TileletIconModels.swift @@ -39,7 +39,7 @@ extension Tilelet { /// Model that represents the options available for the directional icon. public struct DirectionalIcon { - public enum IconType { + public enum IconType: String, CaseIterable { case rightArrow case externalLink @@ -48,6 +48,14 @@ extension Tilelet { } } + public enum IconSize: String, EnumSubset { + case small + case medium + case large + + public var defaultValue: Icon.Size { .medium } + } + /// Color of the icon. public var colorConfiguration: SurfaceColorConfiguration @@ -58,11 +66,11 @@ extension Tilelet { public var iconType: IconType /// Enum for a preset height and width for the icon. - public var size: Icon.Size + public var size: IconSize public init(iconType: IconType = .rightArrow, colorConfiguration: SurfaceColorConfiguration = .init(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark), - size: Icon.Size = .medium, + size: IconSize = .medium, accessibleText: String? = nil) { self.iconType = iconType From 6ad1c0561e1359ddafad7d03f1926c89b11b0ea9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 23 May 2024 11:45:04 -0500 Subject: [PATCH 15/19] updated vars Signed-off-by: Matt Bruce --- .../TextFields/InputField/FieldTypes/SecurityCode.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/VDS/Components/TextFields/InputField/FieldTypes/SecurityCode.swift b/VDS/Components/TextFields/InputField/FieldTypes/SecurityCode.swift index cf60eb9a..d651058c 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/SecurityCode.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/SecurityCode.swift @@ -42,21 +42,21 @@ extension InputField { let surface = inputField.surface var contentView: UIView - var code3Label = Label().with { + let code3Label = Label().with { $0.text = "Most credit or debit cards have a 3-digit security code on the back." $0.isEnabled = true $0.surface = surface } - var code4Label = Label().with { + let code4Label = Label().with { $0.text = "American Express cards have a 4-digit code on the front." $0.isEnabled = true $0.surface = surface } - var code3ImageView = UIImageView().with { + let code3ImageView = UIImageView().with { $0.image = BundleManager.shared.image(for: "securityCode\(surface == .dark ? "-inverted": "")") } - var code4ImageView = UIImageView().with { + let code4ImageView = UIImageView().with { $0.image = BundleManager.shared.image(for: "securityCodeAmex\(surface == .dark ? "-inverted": "")") } @@ -84,7 +84,6 @@ extension InputField { } } - let title: String = inputField.cardType.rawValue switch inputField.cardType { case .amex: contentView = view4() From 13396d150182dba4b5a8c8ce603df11be4fa1da9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 23 May 2024 11:51:45 -0500 Subject: [PATCH 16/19] updated custom spacing Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 2c2435be..58328966 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -43,7 +43,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { internal let mainStackView = UIStackView().with { $0.axis = .vertical $0.alignment = .fill - $0.spacing = 8 + $0.spacing = VDSLayout.space1X $0.translatesAutoresizingMaskIntoConstraints = false } @@ -51,7 +51,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.axis = .vertical $0.alignment = .fill $0.distribution = .fill - $0.spacing = 8 + $0.spacing = VDSLayout.space2X $0.translatesAutoresizingMaskIntoConstraints = false } @@ -285,7 +285,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { // Add arranged subviews to mainStackView mainStackView.addArrangedSubview(titleLabel) mainStackView.addArrangedSubview(contentStackView) - + // Initial position of the helper label updateHelperTextPosition() From 7953751a89a8088816fd71a2a2bf32acb0a14ab5 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 23 May 2024 11:59:13 -0500 Subject: [PATCH 17/19] fixed optional text position Signed-off-by: Matt Bruce --- VDS/Components/DropdownSelect/DropdownSelect.swift | 4 ++-- VDS/Components/TextFields/EntryFieldBase.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index cf3539fb..f596f725 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -222,11 +222,11 @@ open class DropdownSelect: EntryFieldBase { updatedLabelText = showInlineLabel ? "" : updatedLabelText if let oldText = updatedLabelText, !isRequired, !oldText.hasSuffix("Optional") { - let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, + let optionColorAttr = ColorLabelAttribute(location: oldText.count + 1, length: 8, color: secondaryColorConfiguration.getColor(self)) - updatedLabelText = showInlineLabel ? "Optional" : "\(oldText) Optional" + updatedLabelText = showInlineLabel ? "Optional" : "\(oldText) Optional" attributes.append(optionColorAttr) } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 58328966..3f37205e 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -360,13 +360,13 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //dealing with the "Optional" addition to the text if let oldText = updatedLabelText, !isRequired, !oldText.hasSuffix("Optional") { if isEnabled { - let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, + let optionColorAttr = ColorLabelAttribute(location: oldText.count + 1, length: 8, color: VDSColor.elementsSecondaryOnlight) attributes.append(optionColorAttr) } - updatedLabelText = "\(oldText) Optional" + updatedLabelText = "\(oldText) Optional" } if let tooltipModel { From fc4206691b20bad09d785ba0fd75622bee6b1f01 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 23 May 2024 15:55:32 -0500 Subject: [PATCH 18/19] updated release notes Signed-off-by: Matt Bruce --- VDS/SupportingFiles/ReleaseNotes.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 461efee9..2a558285 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -1,3 +1,8 @@ +1.0.64 +---------------- +- CXTDT-555846 - DropdownSelect - Tooltip Icon Spacing +- CXTDT-555858 - DropdownSSelect - Disabled/required helper text + 1.0.63 ---------------- - CXTDT-555860 - Dropdown Select - Form Elements layout option missing From 55fd348ccc3899b4f842f0697ab4aec13b655056 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 23 May 2024 15:56:38 -0500 Subject: [PATCH 19/19] updated version Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index cca7c7f7..2f0fee7c 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -1509,7 +1509,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 63; + CURRENT_PROJECT_VERSION = 64; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1546,7 +1546,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 63; + CURRENT_PROJECT_VERSION = 64; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1;