diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 57dbaa5b..92743acd 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 //-------------------------------------------------- @@ -114,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 @@ -136,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 @@ -161,20 +149,21 @@ 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 - fieldStackView + containerView .publisher(for: UITapGestureRecognizer()) .sink { [weak self] _ in self?.launchPicker() @@ -287,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 ?? "" @@ -345,20 +312,21 @@ 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 } - - open override var canBecomeFirstResponder: Bool { true } - + + open override func becomeFirstResponder() -> Bool { + return dropdownField.becomeFirstResponder() + } + + open override var canResignFirstResponder: Bool { + return dropdownField.canResignFirstResponder + } + open override func resignFirstResponder() -> Bool { - if dropdownField.isFirstResponder { - dropdownField.resignFirstResponder() - } - return super.resignFirstResponder() + return dropdownField.resignFirstResponder() } } @@ -375,7 +343,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { dropdownField.resignFirstResponder() } optionsPicker.isHidden = !optionsPicker.isHidden - setNeedsUpdate() + updateContainerView() } public func numberOfComponents(in pickerView: UIPickerView) -> Int { @@ -396,6 +364,5 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { selectId = row updateSelectedOptionLabel(option: options[row]) sendActions(for: .valueChanged) - self.onItemSelected?(row, options[row]) } } diff --git a/VDS/Components/Icon/Icon.swift b/VDS/Components/Icon/Icon.swift index 644d9775..ac4a5818 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) { diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 657b724e..0cdcce7d 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 - //-------------------------------------------------- - private 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/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 3862afb9..b3c2a92b 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) {} diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 01657e15..358f98cf 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 @@ -98,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() } } @@ -172,9 +162,6 @@ 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 @@ -182,15 +169,9 @@ 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() - - titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) - titleLabel.setContentHuggingPriority(.required, for: .horizontal) - titleLabelWidthConstraint = titleLabel.width(constant: 0) - textField.heightAnchor.constraint(equalToConstant: 20).isActive = true textField.delegate = self - primaryStackView.addArrangedSubview(successLabel) - primaryStackView.setCustomSpacing(8, after: successLabel) + bottomContainerStackView.insertArrangedSubview(successLabel, at: 0) fieldStackView.addArrangedSubview(actionTextLink) @@ -203,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. @@ -262,27 +235,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,26 +270,27 @@ 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 } - - open override var canBecomeFirstResponder: Bool { true } - + + open override func becomeFirstResponder() -> Bool { + return textField.becomeFirstResponder() + } + + open override var canResignFirstResponder: Bool { + return textField.canResignFirstResponder + } + open override func resignFirstResponder() -> Bool { - if textField.isFirstResponder { - textField.resignFirstResponder() - } - return super.resignFirstResponder() + return textField.resignFirstResponder() } } extension InputField: UITextFieldDelegate { public func textFieldDidBeginEditing(_ textField: UITextField) { fieldType.handler().textFieldDidBeginEditing(self, textField: textField) - setNeedsUpdate() + updateContainerView() } public func textFieldDidEndEditing(_ textField: UITextField) { @@ -349,10 +302,9 @@ extension InputField: UITextFieldDelegate { fieldType.handler().textFieldDidChangeSelection(self, textField: textField) if fieldType.handler().validateOnChange { validate() - } else { - setNeedsUpdate() } sendActions(for: .valueChanged) + setNeedsUpdate() } public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 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/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index f0fd6a85..5383e72b 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() } //-------------------------------------------------- diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index f0e1cb4a..ae1977d0 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() {