Merge branch 'develop' into mbruce/textArea-refactor-entryfield
# Conflicts: # VDS/Components/TextFields/EntryFieldBase.swift # VDS/Components/TextFields/TextArea/TextArea.swift Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
commit
62338cbfc1
@ -68,6 +68,7 @@
|
|||||||
EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; };
|
EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; };
|
||||||
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; };
|
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; };
|
||||||
EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; };
|
EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; };
|
||||||
|
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6F330D2B911E9000BACAB9 /* TextView.swift */; };
|
||||||
EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; };
|
EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; };
|
||||||
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; };
|
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; };
|
||||||
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
|
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
|
||||||
@ -236,6 +237,7 @@
|
|||||||
EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = "<group>"; };
|
EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = "<group>"; };
|
||||||
EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
|
EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
|
||||||
EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = "<group>"; };
|
EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = "<group>"; };
|
||||||
|
EA6F330D2B911E9000BACAB9 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
|
||||||
EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
|
EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
|
||||||
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; };
|
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; };
|
||||||
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
|
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
|
||||||
@ -717,6 +719,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
EA985C22296E033A00F2FF2E /* TextArea.swift */,
|
EA985C22296E033A00F2FF2E /* TextArea.swift */,
|
||||||
|
EA6F330D2B911E9000BACAB9 /* TextView.swift */,
|
||||||
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */,
|
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */,
|
||||||
);
|
);
|
||||||
path = TextArea;
|
path = TextArea;
|
||||||
@ -1059,6 +1062,7 @@
|
|||||||
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
|
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
|
||||||
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
|
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
|
||||||
EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */,
|
EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */,
|
||||||
|
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */,
|
||||||
EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */,
|
EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */,
|
||||||
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */,
|
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */,
|
||||||
EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */,
|
EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */,
|
||||||
@ -1257,7 +1261,7 @@
|
|||||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 54;
|
CURRENT_PROJECT_VERSION = 55;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
@ -1294,7 +1298,7 @@
|
|||||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 54;
|
CURRENT_PROJECT_VERSION = 55;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
|||||||
@ -89,6 +89,24 @@ open class Loader: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
open override func updateAccessibility() {
|
||||||
|
super.updateAccessibility()
|
||||||
|
|
||||||
|
// check to make sure VoiceOver is running
|
||||||
|
guard UIAccessibility.isVoiceOverRunning, isActive else {
|
||||||
|
loadingTimer?.invalidate()
|
||||||
|
loadingTimer = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus VoiceOver on this view
|
||||||
|
UIAccessibility.post(notification: .layoutChanged, argument: self)
|
||||||
|
|
||||||
|
// setup timer for post
|
||||||
|
loadingTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
|
||||||
|
guard let self, self.isActive, self.isVisibleOnScreen else { return }
|
||||||
|
self.accessibilityLabel = "Still Loading"
|
||||||
|
UIAccessibility.post(notification: .announcement, argument: "Still Loading")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -107,23 +125,6 @@ open class Loader: View {
|
|||||||
rotation.duration = 0.5
|
rotation.duration = 0.5
|
||||||
rotation.repeatCount = .infinity
|
rotation.repeatCount = .infinity
|
||||||
icon.layer.add(rotation, forKey: rotationLayerName)
|
icon.layer.add(rotation, forKey: rotationLayerName)
|
||||||
|
|
||||||
// check to make sure VoiceOver is running
|
|
||||||
guard UIAccessibility.isVoiceOverRunning else {
|
|
||||||
loadingTimer?.invalidate()
|
|
||||||
loadingTimer = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Focus VoiceOver on this view
|
|
||||||
UIAccessibility.post(notification: .layoutChanged, argument: self)
|
|
||||||
|
|
||||||
// setup timer for post
|
|
||||||
loadingTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
|
|
||||||
guard let self, self.isActive, self.isVisibleOnScreen else { return }
|
|
||||||
self.accessibilityLabel = "Still Loading"
|
|
||||||
UIAccessibility.post(notification: .announcement, argument: "Still Loading")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopAnimating() {
|
private func stopAnimating() {
|
||||||
|
|||||||
@ -153,27 +153,30 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
/// Whether not to show the error.
|
/// Whether not to show the error.
|
||||||
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Whether or not to show the internal error
|
||||||
|
internal var showInternalError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Override UIControl state to add the .error state if showError is true.
|
/// Override UIControl state to add the .error state if showError is true.
|
||||||
open override var state: UIControl.State {
|
open override var state: UIControl.State {
|
||||||
get {
|
get {
|
||||||
var state = super.state
|
var state = super.state
|
||||||
if showError {
|
if showError || showInternalError {
|
||||||
state.insert(.error)
|
state.insert(.error)
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _errorText: String?
|
|
||||||
|
|
||||||
open var errorText: String? {
|
open var errorText: String? {
|
||||||
get { return _errorText }
|
didSet {
|
||||||
set {
|
updateContainerView()
|
||||||
if let newValue {
|
updateErrorLabel()
|
||||||
_errorText = newValue
|
setNeedsUpdate()
|
||||||
} else {
|
}
|
||||||
_errorText = nil
|
}
|
||||||
}
|
|
||||||
|
internal var internalErrorText: String? {
|
||||||
|
didSet {
|
||||||
updateContainerView()
|
updateContainerView()
|
||||||
updateErrorLabel()
|
updateErrorLabel()
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
@ -196,7 +199,18 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
|
|
||||||
open var inputId: String? { didSet { setNeedsUpdate() } }
|
open var inputId: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var value: String? { didSet { setNeedsUpdate() } }
|
/// The text of this textField.
|
||||||
|
private var _value: String?
|
||||||
|
open var value: String? {
|
||||||
|
get { _value }
|
||||||
|
set {
|
||||||
|
if let newValue, newValue != _value {
|
||||||
|
_value = newValue
|
||||||
|
text = newValue
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
@ -365,7 +379,16 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateErrorLabel(){
|
open func updateErrorLabel(){
|
||||||
if showError, let errorText {
|
if showError, showInternalError, let errorText, let internalErrorText {
|
||||||
|
errorLabel.text = [internalErrorText, errorText].joined(separator: "\n")
|
||||||
|
errorLabel.surface = surface
|
||||||
|
errorLabel.isEnabled = isEnabled
|
||||||
|
errorLabel.isHidden = false
|
||||||
|
icon.name = .error
|
||||||
|
icon.color = VDSColor.paletteBlack
|
||||||
|
icon.surface = surface
|
||||||
|
icon.isHidden = !isEnabled
|
||||||
|
} else if showError, let errorText {
|
||||||
errorLabel.text = errorText
|
errorLabel.text = errorText
|
||||||
errorLabel.surface = surface
|
errorLabel.surface = surface
|
||||||
errorLabel.isEnabled = isEnabled
|
errorLabel.isEnabled = isEnabled
|
||||||
@ -374,6 +397,15 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
icon.color = VDSColor.paletteBlack
|
icon.color = VDSColor.paletteBlack
|
||||||
icon.surface = surface
|
icon.surface = surface
|
||||||
icon.isHidden = !isEnabled
|
icon.isHidden = !isEnabled
|
||||||
|
} else if showInternalError, let internalErrorText {
|
||||||
|
errorLabel.text = internalErrorText
|
||||||
|
errorLabel.surface = surface
|
||||||
|
errorLabel.isEnabled = isEnabled
|
||||||
|
errorLabel.isHidden = false
|
||||||
|
icon.name = .error
|
||||||
|
icon.color = VDSColor.paletteBlack
|
||||||
|
icon.surface = surface
|
||||||
|
icon.isHidden = !isEnabled
|
||||||
} else {
|
} else {
|
||||||
icon.isHidden = true
|
icon.isHidden = true
|
||||||
errorLabel.isHidden = true
|
errorLabel.isHidden = true
|
||||||
|
|||||||
@ -79,10 +79,14 @@ open class InputField: EntryFieldBase, UITextFieldDelegate {
|
|||||||
open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } }
|
open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// The text of this textField.
|
/// The text of this textField.
|
||||||
open override var value: String? {
|
open override var text: String? {
|
||||||
get { textField.text }
|
get { textField.text }
|
||||||
set {
|
set {
|
||||||
textField.text = newValue
|
if let newValue, newValue != text {
|
||||||
|
textField.text = newValue
|
||||||
|
value = newValue
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,6 @@ open class TextArea: EntryFieldBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
internal var minWidthConstraint: NSLayoutConstraint?
|
internal var minWidthConstraint: NSLayoutConstraint?
|
||||||
internal var textViewHeightConstraint: NSLayoutConstraint?
|
internal var textViewHeightConstraint: NSLayoutConstraint?
|
||||||
internal var allowCharCount: Int = 0
|
|
||||||
|
|
||||||
internal var inputFieldStackView: UIStackView = {
|
internal var inputFieldStackView: UIStackView = {
|
||||||
return UIStackView().with {
|
return UIStackView().with {
|
||||||
@ -107,12 +106,17 @@ open class TextArea: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override var value: String? {
|
/// The text of this textView
|
||||||
|
private var _text: String?
|
||||||
|
open override var text: String? {
|
||||||
get { textView.text }
|
get { textView.text }
|
||||||
set {
|
set {
|
||||||
textView.text = newValue
|
if let newValue, newValue != _text {
|
||||||
|
_text = newValue
|
||||||
|
textView.text = newValue
|
||||||
|
value = newValue
|
||||||
|
}
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,17 +205,15 @@ open class TextArea: EntryFieldBase {
|
|||||||
minWidthConstraint?.isActive = true
|
minWidthConstraint?.isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let characterError = getCharacterCounterText()
|
||||||
if let maxLength, maxLength > 0 {
|
if let maxLength, maxLength > 0 {
|
||||||
// allow - 20% of character limit
|
characterCounterLabel.text = characterError
|
||||||
let overflowLimit = Double(maxLength) * 0.20
|
|
||||||
allowCharCount = Int(overflowLimit) + maxLength
|
|
||||||
characterCounterLabel.text = getCharacterCounterText()
|
|
||||||
} else {
|
} else {
|
||||||
characterCounterLabel.text = ""
|
characterCounterLabel.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
showError = !validator.validate()
|
showInternalError = !validator.validate()
|
||||||
errorText = validator.errorMessage
|
internalErrorText = validator.errorMessage
|
||||||
|
|
||||||
icon.size = .medium
|
icon.size = .medium
|
||||||
icon.color = iconColorConfiguration.getColor(self)
|
icon.color = iconColorConfiguration.getColor(self)
|
||||||
@ -245,17 +247,17 @@ open class TextArea: EntryFieldBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private func getCharacterCounterText() -> String {
|
private func getCharacterCounterText() -> String? {
|
||||||
let count = textView.text.count
|
let count = textView.text.count
|
||||||
let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)"
|
let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)"
|
||||||
if ((maxLength ?? 0) > 0) {
|
if let maxLength, maxLength > 0 {
|
||||||
if (count > (maxLength ?? 0)) {
|
if count > maxLength {
|
||||||
return countStr
|
return countStr
|
||||||
} else {
|
} else {
|
||||||
return ("\(countStr)" + "/" + "\(maxLength ?? 0)")
|
return ("\(countStr)" + "/" + "\(maxLength)")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,16 +291,8 @@ open class TextArea: EntryFieldBase {
|
|||||||
var errorMessage: String = "You have exceeded the character limit."
|
var errorMessage: String = "You have exceeded the character limit."
|
||||||
|
|
||||||
func isValid(value: String?) -> Bool {
|
func isValid(value: String?) -> Bool {
|
||||||
guard let text = value else { return true }
|
guard let text = value, let maxLength, maxLength > 0 else { return true }
|
||||||
|
return text.count <= maxLength
|
||||||
if ((maxLength ?? 0) > 0) {
|
|
||||||
if (text.count > (maxLength ?? 0)) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,12 +319,16 @@ extension TextArea: UITextViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//The exceeding characters will be highlighted to help users correct their entry.
|
//The exceeding characters will be highlighted to help users correct their entry.
|
||||||
if ((maxLength ?? 0) > 0) {
|
if let maxLength, maxLength > 0 {
|
||||||
|
// allow - 20% of character limit
|
||||||
|
let overflowLimit = Double(maxLength) * 0.20
|
||||||
|
let allowCharCount = Int(overflowLimit) + maxLength
|
||||||
|
|
||||||
if textView.text.count <= allowCharCount {
|
if textView.text.count <= allowCharCount {
|
||||||
highlightCharacterOverflow()
|
highlightCharacterOverflow()
|
||||||
|
|
||||||
//setting the value and firing control event
|
//setting the value and firing control event
|
||||||
value = textView.text
|
text = textView.text
|
||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
} else {
|
} else {
|
||||||
textView.text.removeLast()
|
textView.text.removeLast()
|
||||||
@ -338,149 +336,8 @@ extension TextArea: UITextViewDelegate {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//setting the value and firing control event
|
//setting the value and firing control event
|
||||||
value = textView.text
|
text = textView.text
|
||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will move this into a new file, need to talk with Scott/Kyle
|
|
||||||
open class TextView: UITextView, ViewProtocol {
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Initializers
|
|
||||||
//--------------------------------------------------
|
|
||||||
required public init() {
|
|
||||||
super.init(frame: .zero, textContainer: nil)
|
|
||||||
initialSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
public override init(frame: CGRect, textContainer: NSTextContainer?) {
|
|
||||||
super.init(frame: frame, textContainer: textContainer)
|
|
||||||
initialSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
public required init?(coder: NSCoder) {
|
|
||||||
super.init(coder: coder)
|
|
||||||
initialSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Combine Properties
|
|
||||||
//--------------------------------------------------
|
|
||||||
/// Set of Subscribers for any Publishers for this Control.
|
|
||||||
open var subscribers = Set<AnyCancellable>()
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Private Properties
|
|
||||||
//--------------------------------------------------
|
|
||||||
private var initialSetupPerformed = false
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Properties
|
|
||||||
//--------------------------------------------------
|
|
||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
|
||||||
open var shouldUpdateView: Bool = true
|
|
||||||
|
|
||||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
/// Array of LabelAttributeModel objects used in rendering the text.
|
|
||||||
open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
/// TextStyle used on the titleLabel.
|
|
||||||
open var textStyle: TextStyle { .defaultStyle }
|
|
||||||
|
|
||||||
/// Will determine if a scaled font should be used for the titleLabel font.
|
|
||||||
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
open var isEnabled: Bool = true { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with {
|
|
||||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
|
|
||||||
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
|
|
||||||
}.eraseToAnyColorable(){ didSet { setNeedsUpdate() }}
|
|
||||||
|
|
||||||
open override var textColor: UIColor? {
|
|
||||||
get { textColorConfiguration.getColor(self) }
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
override public var text: String! {
|
|
||||||
get { super.text }
|
|
||||||
set {
|
|
||||||
super.text = newValue
|
|
||||||
updateLabel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override public var textAlignment: NSTextAlignment {
|
|
||||||
didSet {
|
|
||||||
if textAlignment != oldValue {
|
|
||||||
// Text alignment can be part of our paragraph style, so we may need to
|
|
||||||
// re-style when changed
|
|
||||||
updateLabel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Lifecycle
|
|
||||||
//--------------------------------------------------
|
|
||||||
open func initialSetup() {
|
|
||||||
if !initialSetupPerformed {
|
|
||||||
backgroundColor = .clear
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
setup()
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open func setup() {
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
}
|
|
||||||
|
|
||||||
open func updateView() {
|
|
||||||
updateLabel()
|
|
||||||
}
|
|
||||||
|
|
||||||
open func updateAccessibility() {}
|
|
||||||
|
|
||||||
open func reset() {
|
|
||||||
shouldUpdateView = false
|
|
||||||
surface = .light
|
|
||||||
text = nil
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Private Methods
|
|
||||||
//--------------------------------------------------
|
|
||||||
private func updateLabel() {
|
|
||||||
|
|
||||||
//clear the arrays holding actions
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
if let text, !text.isEmpty {
|
|
||||||
//create the primary string
|
|
||||||
let mutableText = NSMutableAttributedString.mutableText(for: text,
|
|
||||||
textStyle: textStyle,
|
|
||||||
useScaledFont: useScaledFont,
|
|
||||||
textColor: textColor!,
|
|
||||||
alignment: textAlignment,
|
|
||||||
lineBreakMode: .byWordWrapping)
|
|
||||||
//apply any attributes
|
|
||||||
if let attributes = textAttributes {
|
|
||||||
mutableText.apply(attributes: attributes)
|
|
||||||
}
|
|
||||||
attributedText = mutableText
|
|
||||||
} else {
|
|
||||||
attributedText = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
150
VDS/Components/TextFields/TextArea/TextView.swift
Normal file
150
VDS/Components/TextFields/TextArea/TextView.swift
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
//
|
||||||
|
// TextView.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 2/29/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
import VDSColorTokens
|
||||||
|
|
||||||
|
/// Will move this into a new file, need to talk with Scott/Kyle
|
||||||
|
open class TextView: UITextView, ViewProtocol {
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializers
|
||||||
|
//--------------------------------------------------
|
||||||
|
required public init() {
|
||||||
|
super.init(frame: .zero, textContainer: nil)
|
||||||
|
initialSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override init(frame: CGRect, textContainer: NSTextContainer?) {
|
||||||
|
super.init(frame: frame, textContainer: textContainer)
|
||||||
|
initialSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
initialSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Combine Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Set of Subscribers for any Publishers for this Control.
|
||||||
|
open var subscribers = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
private var initialSetupPerformed = false
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Array of LabelAttributeModel objects used in rendering the text.
|
||||||
|
open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// TextStyle used on the titleLabel.
|
||||||
|
open var textStyle: TextStyle { .defaultStyle }
|
||||||
|
|
||||||
|
/// Will determine if a scaled font should be used for the titleLabel font.
|
||||||
|
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
open var isEnabled: Bool = true { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
|
||||||
|
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
|
||||||
|
}.eraseToAnyColorable(){ didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
open override var textColor: UIColor? {
|
||||||
|
get { textColorConfiguration.getColor(self) }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var text: String! {
|
||||||
|
get { super.text }
|
||||||
|
set {
|
||||||
|
super.text = newValue
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var textAlignment: NSTextAlignment {
|
||||||
|
didSet {
|
||||||
|
if textAlignment != oldValue {
|
||||||
|
// Text alignment can be part of our paragraph style, so we may need to
|
||||||
|
// re-style when changed
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
open func initialSetup() {
|
||||||
|
if !initialSetupPerformed {
|
||||||
|
backgroundColor = .clear
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
setup()
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open func setup() {
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
}
|
||||||
|
|
||||||
|
open func updateView() {
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
open func updateAccessibility() {}
|
||||||
|
|
||||||
|
open func reset() {
|
||||||
|
shouldUpdateView = false
|
||||||
|
surface = .light
|
||||||
|
text = nil
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
shouldUpdateView = true
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
private func updateLabel() {
|
||||||
|
|
||||||
|
//clear the arrays holding actions
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
if let text, !text.isEmpty {
|
||||||
|
//create the primary string
|
||||||
|
let mutableText = NSMutableAttributedString.mutableText(for: text,
|
||||||
|
textStyle: textStyle,
|
||||||
|
useScaledFont: useScaledFont,
|
||||||
|
textColor: textColor!,
|
||||||
|
alignment: textAlignment,
|
||||||
|
lineBreakMode: .byWordWrapping)
|
||||||
|
//apply any attributes
|
||||||
|
if let attributes = textAttributes {
|
||||||
|
mutableText.apply(attributes: attributes)
|
||||||
|
}
|
||||||
|
attributedText = mutableText
|
||||||
|
} else {
|
||||||
|
attributedText = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ extension Tilelet {
|
|||||||
// MARK: - Enums
|
// MARK: - Enums
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Enum used to describe the textStyle of the subTitle label.
|
/// Enum used to describe the textStyle of the subTitle label.
|
||||||
public enum StandardStyle: String, EnumSubset {
|
public enum OtherStandardStyle: String, EnumSubset {
|
||||||
case bodyLarge
|
case bodyLarge
|
||||||
case bodyMedium
|
case bodyMedium
|
||||||
case bodySmall
|
case bodySmall
|
||||||
@ -28,7 +28,7 @@ extension Tilelet {
|
|||||||
public var text: String = ""
|
public var text: String = ""
|
||||||
|
|
||||||
/// Text style that will be used for the subTitle label.
|
/// Text style that will be used for the subTitle label.
|
||||||
public var standardStyle: StandardStyle = .bodySmall
|
public var otherStandardStyle: OtherStandardStyle = .bodySmall
|
||||||
|
|
||||||
/// Text attributes that will be used for the subTitle label.
|
/// Text attributes that will be used for the subTitle label.
|
||||||
public var textAttributes: [any LabelAttributeModel]?
|
public var textAttributes: [any LabelAttributeModel]?
|
||||||
@ -40,13 +40,13 @@ extension Tilelet {
|
|||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public init(text: String,
|
public init(text: String,
|
||||||
|
otherStandardStyle: OtherStandardStyle = .bodySmall,
|
||||||
textColor: Use = .primary,
|
textColor: Use = .primary,
|
||||||
textAttributes: [any LabelAttributeModel]? = nil,
|
textAttributes: [any LabelAttributeModel]? = nil) {
|
||||||
standardStyle: StandardStyle = .bodySmall) {
|
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.otherStandardStyle = otherStandardStyle
|
||||||
self.textAttributes = textAttributes
|
self.textAttributes = textAttributes
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.standardStyle = standardStyle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -55,7 +55,7 @@ extension Tilelet {
|
|||||||
/// Converts this type of model to a TitleLockup.SubTitleModel.
|
/// Converts this type of model to a TitleLockup.SubTitleModel.
|
||||||
public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel {
|
public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel {
|
||||||
TitleLockup.SubTitleModel(text: text,
|
TitleLockup.SubTitleModel(text: text,
|
||||||
standardStyle: standardStyle.value,
|
otherStandardStyle: otherStandardStyle.value,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
textAttributes: textAttributes)
|
textAttributes: textAttributes)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ open class TitleLockup: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private var otherStandardStyle: OtherStandardStyle {
|
private var otherStandardStyle: OtherStandardStyle {
|
||||||
if let subTitleModel, !subTitleModel.text.isEmpty {
|
if let subTitleModel, !subTitleModel.text.isEmpty {
|
||||||
return subTitleModel.standardStyle
|
return subTitleModel.otherStandardStyle
|
||||||
} else if let eyebrowModel, !eyebrowModel.text.isEmpty {
|
} else if let eyebrowModel, !eyebrowModel.text.isEmpty {
|
||||||
return eyebrowModel.standardStyle
|
return eyebrowModel.standardStyle
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ extension TitleLockup {
|
|||||||
public var text: String
|
public var text: String
|
||||||
|
|
||||||
/// Standard style that will be used for the subTitle label.
|
/// Standard style that will be used for the subTitle label.
|
||||||
public var standardStyle: OtherStandardStyle
|
public var otherStandardStyle: OtherStandardStyle
|
||||||
|
|
||||||
/// Text color used in the subtitle label.
|
/// Text color used in the subtitle label.
|
||||||
public var textColor: Use
|
public var textColor: Use
|
||||||
@ -26,19 +26,19 @@ extension TitleLockup {
|
|||||||
public var numberOfLines: Int
|
public var numberOfLines: Int
|
||||||
|
|
||||||
public init(text: String,
|
public init(text: String,
|
||||||
standardStyle: OtherStandardStyle = .bodyLarge,
|
otherStandardStyle: OtherStandardStyle = .bodyLarge,
|
||||||
textColor: Use = .primary,
|
textColor: Use = .primary,
|
||||||
textAttributes: [any LabelAttributeModel]? = nil,
|
textAttributes: [any LabelAttributeModel]? = nil,
|
||||||
numberOfLines: Int = 0) {
|
numberOfLines: Int = 0) {
|
||||||
self.text = text
|
self.text = text
|
||||||
self.standardStyle = standardStyle
|
self.otherStandardStyle = otherStandardStyle
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.textAttributes = textAttributes
|
self.textAttributes = textAttributes
|
||||||
self.numberOfLines = numberOfLines
|
self.numberOfLines = numberOfLines
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TextStyle used to render the text.
|
/// TextStyle used to render the text.
|
||||||
public var textStyle: TextStyle { standardStyle.value.regular }
|
public var textStyle: TextStyle { otherStandardStyle.value.regular }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,8 @@
|
|||||||
|
1.0.55
|
||||||
|
----------------
|
||||||
|
- ONEAPP-6305 - BadgeIndicator - Finished Development
|
||||||
|
- ONEAPP-6679 - TileContainer - Finished Development
|
||||||
|
|
||||||
1.0.54
|
1.0.54
|
||||||
----------------
|
----------------
|
||||||
- CXTDT-518373 Accessibility Voiceover is reading “Still Loading” after waiting for a short time in all the screens.
|
- CXTDT-518373 Accessibility Voiceover is reading “Still Loading” after waiting for a short time in all the screens.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user