Reworked bezier logic for CaretView. It was not optimal.

This commit is contained in:
Kevin G Christiano 2019-11-01 16:15:19 -04:00
parent 5e96653d4b
commit 475d08296b
8 changed files with 300 additions and 285 deletions

View File

@ -25,8 +25,6 @@ import UIKit
return layer return layer
}() }()
weak var textBoxDelegate: DigitBoxDelegate?
private var previousSize: CGFloat = 0.0 private var previousSize: CGFloat = 0.0
/// Determines if a border should be drawn. /// Determines if a border should be drawn.
@ -36,6 +34,12 @@ import UIKit
private var borderStrokeColor: UIColor = .mfSilver() private var borderStrokeColor: UIColor = .mfSilver()
private var borderPath: UIBezierPath = UIBezierPath() private var borderPath: UIBezierPath = UIBezierPath()
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
weak var textBoxDelegate: DigitBoxDelegate?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
//-------------------------------------------------- //--------------------------------------------------
@ -47,11 +51,6 @@ import UIKit
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("DigitBox has not been implemented")
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
setup() setup()
@ -61,11 +60,16 @@ import UIKit
self.init(frame: .zero) self.init(frame: .zero)
} }
required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("DigitBox has not been implemented")
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
func setup() { private func setup() {
guard constraints.isEmpty else { return } guard constraints.isEmpty else { return }
@ -74,7 +78,7 @@ import UIKit
textAlignment = .center textAlignment = .center
keyboardType = .numberPad keyboardType = .numberPad
layer.borderWidth = 1 layer.borderWidth = 1
showError(false) showErrorState(false)
widthConstraint = widthAnchor.constraint(equalToConstant: 39) widthConstraint = widthAnchor.constraint(equalToConstant: 39)
widthConstraint?.isActive = true widthConstraint?.isActive = true
@ -83,7 +87,6 @@ import UIKit
heightConstraint?.isActive = true heightConstraint?.isActive = true
layer.addSublayer(bottomBar) layer.addSublayer(bottomBar)
updateView(MVMCoreUISplitViewController.getDetailViewWidth()) updateView(MVMCoreUISplitViewController.getDetailViewWidth())
} }
@ -161,7 +164,7 @@ import UIKit
} }
} }
func showError(_ show: Bool) { func showErrorState(_ show: Bool) {
showError = show showError = show
borderStrokeColor = show ? .mfPumpkin() : .mfSilver() borderStrokeColor = show ? .mfPumpkin() : .mfSilver()

View File

@ -8,7 +8,10 @@
import UIKit import UIKit
/**
* Subclass of TextEntryField due to the conveniences provided.
* TODO: Compare overrides to determine if TextEntryField is necessary.
*/
@objcMembers open class DigitEntryField: TextEntryField, DigitBoxDelegate { @objcMembers open class DigitEntryField: TextEntryField, DigitBoxDelegate {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Outlets // MARK: - Outlets
@ -20,69 +23,65 @@ import UIKit
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
private var numberOfDigits = 0 private var numberOfDigits = 4
private var switchedAutomatically = false private var switchedAutomatically = false
public var digitFields: [DigitBox]? public var digitFields: [DigitBox] = []
public override var isEnabled: Bool { public override var isEnabled: Bool {
didSet { didSet {
if isEnabled { if isEnabled {
titleLabel.styleB2(true) titleLabel.styleB2(true)
} else { } else {
titleLabel.textColor = UIColor.mfBattleshipGrey() titleLabel.textColor = .mfBattleshipGrey()
} }
for textField in digitFields ?? [] { for textField in digitFields {
textField.isUserInteractionEnabled = isEnabled textField.isUserInteractionEnabled = isEnabled
textField.isEnabled = isEnabled textField.isEnabled = isEnabled
textField.textColor = isEnabled ? .black : UIColor.mfBattleshipGrey() textField.textColor = isEnabled ? .black : .mfBattleshipGrey()
} }
} }
} }
/// Sets placeholder text in the textField. /// Sets placeholder text in the textField.
public override var feedback: String? { public override var placeholder: String? {
get { get {
var string = "" var string = ""
for digitField in digitFields ?? [] { digitFields.forEach { string += $0.attributedPlaceholder?.string ?? "" }
if let digitext = digitField.attributedPlaceholder?.string {
string += digitext
}
}
return !string.isEmpty ? string : nil return !string.isEmpty ? string : nil
} }
set { set {
guard let fieldValue = newValue, let fields = digitFields else { return } guard let newValue = newValue else { return }
for (index, field) in fields.enumerated() { for (index, field) in digitFields.enumerated() {
if index < fieldValue.count { if index < newValue.count {
let stringForIndex = (newValue as NSString?)?.substring(with: NSRange(location: index, length: 1)) let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
field.attributedPlaceholder = NSAttributedString(string: stringForIndex ?? "", attributes: [ field.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [
NSAttributedString.Key.foregroundColor: UIColor.mfBattleshipGrey()]) NSAttributedString.Key.foregroundColor: UIColor.mfBattleshipGrey()])
} }
} }
//
// if feedback.text.length > 0 {
// labelToTextFieldPin?.constant = 10
// } else {
// labelToTextFieldPin?.constant = 0
// }
// adding missing accessibilityLabel value // If there is already text in the textfield, set the place holder label below.
// if we have some value in accessibilityLabel, if let text = text, !text.isEmpty && !showError {
// then only can append regular and picker item feedback = placeholder
// textField.accessibilityLabel() = newValue ?? "" + (MVMCoreUIUtility.hardcodedString(withKey: "mfdigittextfield_regular")) } else if !showError {
// feedback = ""
// super.showErrorMessage(errorMessage) }
//
// if self.showErrorMessage { if let feedback = feedback, !feedback.isEmpty {
// self.labelToTextFieldPin?.constant = 10 feedbackContainerDistance?.constant = 10
// } } else {
// for field in self.digitFields ?? [] { feedbackContainerDistance?.constant = 0
// field.setAsError() }
// }
/*
* adding missing accessibilityLabel value
* if we have some value in accessibilityLabel,
* then only can append regular and picker item
*/
textField.accessibilityLabel = newValue + (MVMCoreUIUtility.hardcodedString(withKey: "mfdigittextfield_regular") ?? "")
} }
} }
@ -90,21 +89,17 @@ import UIKit
get { get {
var string = "" var string = ""
for digitField in digitFields ?? [] { digitFields.forEach { string += $0.text ?? "" }
if let digitText = digitField.text {
string += digitText
}
}
return string return string
} }
set { set {
guard let fields = self.digitFields else { return } guard let newValue = newValue else { return }
for (index, field) in fields.enumerated() { for (index, field) in digitFields.enumerated() {
if index < (text?.count ?? 0) { if index < newValue.count {
let stringForIndex = (text as NSString?)?.substring(with: NSRange(location: index, length: 1)) let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
field.text = stringForIndex field.text = String(newValue[indexChar])
} }
} }
@ -116,9 +111,9 @@ import UIKit
get { return titleLabel.text } get { return titleLabel.text }
set { set {
if let formText = newValue, !formText.isEmpty { if let formText = newValue, !formText.isEmpty {
messageToTextFieldPin?.constant = 10 titleContainerDistance?.constant = 10
} else { } else {
messageToTextFieldPin?.constant = 0 titleContainerDistance?.constant = 0
} }
super.title = newValue super.title = newValue
@ -131,21 +126,17 @@ import UIKit
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
for field in self.digitFields ?? [] { if self.showError {
field.showError(false) self.feedbackContainerDistance?.constant = 10
self.setNeedsLayout()
} }
self.digitFields.forEach { $0.showErrorState(true) }
} }
} }
} }
} }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private weak var messageToTextFieldPin: NSLayoutConstraint?
private weak var labelToTextFieldPin: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -185,6 +176,7 @@ import UIKit
open func setup() { open func setup() {
hideBorder = true
titleLabel.styleB2(true) titleLabel.styleB2(true)
alignCenterHorizontal() alignCenterHorizontal()
} }
@ -206,16 +198,11 @@ import UIKit
self.titleLabel.updateView(size) self.titleLabel.updateView(size)
if !self.digitFields.isEmpty {
if let digitFields = self.digitFields, !digitFields.isEmpty {
StackableViewController.remove(self.digitFields)
// Remove all current UI.
StackableViewController.remove(digitFields) self.digitFields.forEach { $0.updateView(size) }
// Update text boxes.
for digitField in digitFields {
digitField.updateView(size)
}
} }
// Layout text boxes. // Layout text boxes.
@ -227,6 +214,14 @@ import UIKit
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
func removeError() {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.digitFields.forEach { $0.showErrorState(false) }
}
}
func createDigitField() -> DigitBox { func createDigitField() -> DigitBox {
let textField = DigitBox() let textField = DigitBox()
@ -239,22 +234,20 @@ import UIKit
func buildTextFieldsView(size: CGFloat) { func buildTextFieldsView(size: CGFloat) {
// Remove all current UI. // Remove all current UI.
if let digitFields = digitFields, !digitFields.isEmpty { if !digitFields.isEmpty {
StackableViewController.remove(digitFields) StackableViewController.remove(digitFields)
} }
if numberOfDigits > 0 { if numberOfDigits > 0 {
let digitFields = [DigitBox](repeating: createDigitField(), count: numberOfDigits) let digitFields = [DigitBox](repeating: createDigitField(), count: numberOfDigits)
for digitField in digitFields { digitFields.forEach { $0.updateView(size) }
digitField.updateView(size)
}
self.digitFields = digitFields self.digitFields = digitFields
setupTextFieldsView(forSize: size) setupTextFieldsView(forSize: size)
} else { } else {
digitFields = nil digitFields = []
} }
} }
@ -265,10 +258,10 @@ import UIKit
guard let self = self else { return } guard let self = self else { return }
if let feedback = self.feedback, !feedback.isEmpty { if let feedback = self.feedback, !feedback.isEmpty {
self.labelToTextFieldPin?.constant = 10 self.feedbackContainerDistance?.constant = 10
} else { } else {
self.labelToTextFieldPin?.constant = 0 self.feedbackContainerDistance?.constant = 0
} }
} }
} }
@ -276,15 +269,13 @@ import UIKit
func setAsSecureTextEntry(_ secureEntry: Bool) { func setAsSecureTextEntry(_ secureEntry: Bool) {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self, guard let self = self else { return }
let fields = self.digitFields
else { return }
for (index, field) in fields.enumerated() { for (index, field) in self.digitFields.enumerated() {
field.isSecureTextEntry = secureEntry field.isSecureTextEntry = secureEntry
// Accessibility - 33704 fix voice over will read what pin user is filling // Accessibility - 33704 fix voice over will read what pin user is filling
field.accessibilityLabel = String(format: "PIN %lu of %lu", UInt(index) + 1, UInt(fields.count)) field.accessibilityLabel = String(format: "PIN %lu of %lu", UInt(index) + 1, UInt(self.digitFields.count))
} }
} }
} }
@ -292,24 +283,21 @@ import UIKit
func setupTextFieldsView(forSize size: CGFloat) { func setupTextFieldsView(forSize size: CGFloat) {
guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize(), guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize(),
let digitFieldsView = digitFieldsView, let digitFieldsView = digitFieldsView
let digitFields = digitFields
else { return } else { return }
StackableViewController.populateViewHorizontally(digitFieldsView, withUIArray: digitFields, withSpacingBlock: { object in StackableViewController.populateViewHorizontally(digitFieldsView, withUIArray: digitFields, withSpacingBlock: { object in
var inset = UIEdgeInsets(top: 0, left: space, bottom: 0, right: space) var inset = UIEdgeInsets(top: 0, left: space, bottom: 0, right: space)
guard let digitFields = self.digitFields else { return inset } if self.digitFields.count == 1 {
if digitFields.count == 1 {
inset.left = 0 inset.left = 0
inset.right = 0 inset.right = 0
} else if let field = object as? UITextField, field == digitFields.first { } else if let field = object as? UITextField, field == self.digitFields.first {
inset.left = 0 inset.left = 0
} else if let field = object as? UITextField, field == digitFields.last { } else if let field = object as? UITextField, field == self.digitFields.last {
inset.right = 0 inset.right = 0
} }
@ -319,57 +307,18 @@ import UIKit
public override func defaultValidationBlock() { public override func defaultValidationBlock() {
weak var weakSelf = self
validationBlock = { enteredValue in validationBlock = { enteredValue in
guard let enteredValue = enteredValue else { return false }
if (enteredValue?.count ?? 0) > 0 && (enteredValue?.count ?? 0) == weakSelf?.digitFields?.count {
return true return enteredValue.count > 0 && enteredValue.count == self.digitFields.count
}
return false
} }
} }
//--------------------------------------------------
// MARK: - Molecule
//--------------------------------------------------
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
guard let dictionary = json else { return }
let digits = dictionary["digits"] as? Int ?? 4
if digits != numberOfDigits {
numberOfDigits = digits
}
if !dictionary.isEmpty{
for textField in digitFields ?? [] {
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegateObject as? UITextFieldDelegate)
}
}
buildTextFieldsView(size: MVMCoreUIUtility.getWidth())
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
open override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 44
}
//--------------------------------------------------
// MARK: - Helpers
//--------------------------------------------------
func selectPreviousTextField(_ currentTextField: UITextField?, clear: Bool) { func selectPreviousTextField(_ currentTextField: UITextField?, clear: Bool) {
var selectNextField = false var selectNextField = false
guard let fields = digitFields else { return } for field in digitFields {
for field in fields {
if field == currentTextField { if field == currentTextField {
selectNextField = true selectNextField = true
@ -390,18 +339,16 @@ import UIKit
var selectNextField = false var selectNextField = false
guard let fields = digitFields else { return } for field in digitFields {
for field in fields{
if field == currentTextField { if field == currentTextField {
selectNextField = true selectNextField = true
} else if selectNextField { } else if selectNextField {
if !clear { if !clear {
self.switchedAutomatically = true switchedAutomatically = true
} }
field.becomeFirstResponder() field.becomeFirstResponder()
self.switchedAutomatically = false switchedAutomatically = false
UIAccessibility.post(notification: .layoutChanged, argument: field) UIAccessibility.post(notification: .layoutChanged, argument: field)
} }
@ -409,20 +356,46 @@ import UIKit
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Accessinility // MARK: - Molecule
//-------------------------------------------------- //--------------------------------------------------
open override class func accessibilityElements() -> [Any]? { open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
// self.digit
// if let digitFields = self.digitFields { guard let dictionary = json else { return }
// return [digitFields] + [textField]
// } let digits = dictionary["digits"] as? Int ?? 4
// if digits != numberOfDigits {
return [textField] numberOfDigits = digits
}
if !dictionary.isEmpty{
for textField in digitFields {
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegateObject as? UITextFieldDelegate)
}
}
buildTextFieldsView(size: MVMCoreUIUtility.getWidth())
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
open override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 44
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - TextFieldDelegate // MARK: - Accessibility
//--------------------------------------------------
open override class func accessibilityElements() -> [Any]? {
// let fields = []
// return self.digitFields
return nil
}
//--------------------------------------------------
// MARK: - Text Field Delegate
//-------------------------------------------------- //--------------------------------------------------
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool { @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@ -495,7 +468,10 @@ import UIKit
return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true
} }
//--------------------------------------------------
// MARK: - Passed Along TextField delegate // MARK: - Passed Along TextField delegate
//--------------------------------------------------
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true

View File

@ -14,23 +14,19 @@ import UIKit
// MARK: - Outlets // MARK: - Outlets
//-------------------------------------------------- //--------------------------------------------------
let dropDownCaretLabel: Label = { let dropDownCaretLabel: CaretView = {
let label = Label() let caret = CaretView()
label.setContentHuggingPriority(UILayoutPriority(900), for: .horizontal) caret.isHidden = true
label.setContentHuggingPriority(UILayoutPriority(251), for: .vertical) caret.isUserInteractionEnabled = true
label.setContentCompressionResistancePriority(UILayoutPriority(900), for: .horizontal) return caret
label.isHidden = true
label.isUserInteractionEnabled = true
return label
}() }()
private var calendar: Calendar?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Accessories // MARK: - Accessories
//-------------------------------------------------- //--------------------------------------------------
public weak var datePicker: UIDatePicker? public weak var datePicker: UIDatePicker?
private var calendar: Calendar?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
@ -39,9 +35,7 @@ import UIKit
public var dropDownIsDisplayed = false public var dropDownIsDisplayed = false
public override var isEnabled: Bool { public override var isEnabled: Bool {
didSet { didSet { showDropDown(isEnabled) }
showDropDown(isEnabled)
}
} }
//-------------------------------------------------- //--------------------------------------------------
@ -56,10 +50,9 @@ import UIKit
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
setupView() setup()
} }
/// Basic initializer.
public convenience init() { public convenience init() {
self.init(frame: .zero) self.init(frame: .zero)
} }
@ -68,7 +61,7 @@ import UIKit
public override init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { public override init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) {
super.init(frame: .zero) super.init(frame: .zero)
setupView() setup()
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: bothDelegates) MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: bothDelegates)
setBothTextDelegates(to: bothDelegates) setBothTextDelegates(to: bothDelegates)
} }
@ -78,6 +71,13 @@ import UIKit
fatalError("DropdownEntryField does not support xib.") fatalError("DropdownEntryField does not support xib.")
} }
private func setup() {
dropDownCaretLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
dropDownCaretWidth = widthAnchor.constraint(equalToConstant: 40)
dropDownCaretWidth?.isActive = true
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
@ -116,7 +116,7 @@ import UIKit
override func startEditing() { override func startEditing() {
super.startEditing() super.startEditing()
showDropDown(!showErrorMessage) showDropDown(!showError)
} }
class func getEnabledTextfields(_ textFieldToDetermine: [TextEntryField]?) -> [AnyHashable]? { class func getEnabledTextfields(_ textFieldToDetermine: [TextEntryField]?) -> [AnyHashable]? {

View File

@ -43,6 +43,22 @@ import UIKit
return view return view
}() }()
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
weak var delegateObject: MVMCoreUIDelegateObject?
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var isValid = false
public var fieldKey: String?
/// Determines if a border should be drawn.
var hideBorder = false
private var borderStrokeColor: UIColor = .mfSilver() private var borderStrokeColor: UIColor = .mfSilver()
private var borderPath: UIBezierPath = UIBezierPath() private var borderPath: UIBezierPath = UIBezierPath()
@ -55,21 +71,9 @@ import UIKit
return layer return layer
}() }()
weak var delegateObject: MVMCoreUIDelegateObject?
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var isValid = false
public var fieldKey: String?
/// Determines if a border should be drawn.
private var hideBorder = false
public private(set) var appearance: Appearance = .original public private(set) var appearance: Appearance = .original
public var showErrorMessage = false public var showError = false
public var errorMessage: String? public var errorMessage: String?
@ -133,6 +137,9 @@ import UIKit
public var titleLabelLeading: NSLayoutConstraint? public var titleLabelLeading: NSLayoutConstraint?
public var titleLabelTrailing: NSLayoutConstraint? public var titleLabelTrailing: NSLayoutConstraint?
public var titleContainerDistance: NSLayoutConstraint?
public var feedbackContainerDistance: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -150,7 +157,7 @@ import UIKit
public init(title: String) { public init(title: String) {
super.init(frame: .zero) super.init(frame: .zero)
setupView() setupView()
self.titleLabel.text = title titleLabel.text = title
} }
required public init?(coder: NSCoder) { required public init?(coder: NSCoder) {
@ -182,7 +189,8 @@ import UIKit
addSubview(fieldContainer) addSubview(fieldContainer)
setupFieldContainerContent(fieldContainer) setupFieldContainerContent(fieldContainer)
fieldContainer.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4).isActive = true titleContainerDistance = fieldContainer.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4)
titleContainerDistance?.isActive = true
fieldContainerLeading = fieldContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) fieldContainerLeading = fieldContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
fieldContainerLeading?.isActive = true fieldContainerLeading?.isActive = true
fieldContainerTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: fieldContainer.trailingAnchor) fieldContainerTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: fieldContainer.trailingAnchor)
@ -190,7 +198,8 @@ import UIKit
addSubview(feedbackLabel) addSubview(feedbackLabel)
feedbackLabel.topAnchor.constraint(equalTo: fieldContainer.bottomAnchor, constant: PaddingOne).isActive = true feedbackContainerDistance = feedbackLabel.topAnchor.constraint(equalTo: fieldContainer.bottomAnchor, constant: PaddingOne)
feedbackContainerDistance?.isActive = true
feedbackLabelLeading = feedbackLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) feedbackLabelLeading = feedbackLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
feedbackLabelLeading?.isActive = true feedbackLabelLeading?.isActive = true
feedbackLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: feedbackLabel.trailingAnchor) feedbackLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: feedbackLabel.trailingAnchor)
@ -203,10 +212,8 @@ import UIKit
layoutIfNeeded() layoutIfNeeded()
} }
/** /// Method to override.
Method to override. /// Intended to add the interactive content (i.e. textField) to the fieldContainer.
Intended to add the interactive content (i.e. textField) to the fieldContainer.
*/
open func setupFieldContainerContent(_ container: UIView) { open func setupFieldContainerContent(_ container: UIView) {
// To be overridden by subclass. // To be overridden by subclass.
} }
@ -237,7 +244,7 @@ import UIKit
open func refreshBorderUI(bottomBarSize: CGFloat? = nil) { open func refreshBorderUI(bottomBarSize: CGFloat? = nil) {
let size = CGFloat(appearance == .error ? 4 : 1) let size: CGFloat = appearance == .error ? 4 : 1
bottomBar.frame = CGRect(x: 0, y: fieldContainer.bounds.height - size, width: fieldContainer.bounds.width, height: size) bottomBar.frame = CGRect(x: 0, y: fieldContainer.bounds.height - size, width: fieldContainer.bounds.width, height: size)
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self) self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self)
@ -274,11 +281,6 @@ import UIKit
layoutIfNeeded() layoutIfNeeded()
} }
func resizeBottomBar(size: CGFloat) {
bottomBar.frame = CGRect(x: 0, y: fieldContainer.bounds.height - size, width: fieldContainer.bounds.width, height: size)
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraint Methods // MARK: - Constraint Methods
//-------------------------------------------------- //--------------------------------------------------
@ -313,37 +315,30 @@ import UIKit
self.appearance = appearance self.appearance = appearance
isUserInteractionEnabled = true isUserInteractionEnabled = true
titleLabel.textColor = .mfBattleshipGrey()
hideBorder = false hideBorder = false
feedback = showError ? errorMessage : nil
switch appearance { switch appearance {
case .original: case .original:
borderStrokeColor = .mfSilver() borderStrokeColor = .mfSilver()
feedback = nil
bottomBar.backgroundColor = UIColor.black.cgColor bottomBar.backgroundColor = UIColor.black.cgColor
titleLabel.textColor = .mfBattleshipGrey()
case .error: case .error:
borderStrokeColor = .mfPumpkin() borderStrokeColor = .mfPumpkin()
titleLabel.textColor = UIColor.mfBattleshipGrey()
bottomBar.backgroundColor = UIColor.mfPumpkin().cgColor bottomBar.backgroundColor = UIColor.mfPumpkin().cgColor
feedback = showErrorMessage ? errorMessage : nil
case .lock: case .lock:
isUserInteractionEnabled = false isUserInteractionEnabled = false
hideBorder = true hideBorder = true
feedback = nil
titleLabel.textColor = UIColor.mfBattleshipGrey()
bottomBar.backgroundColor = UIColor.clear.cgColor bottomBar.backgroundColor = UIColor.clear.cgColor
case .select: case .select:
borderStrokeColor = .black borderStrokeColor = .black
feedback = nil
titleLabel.textColor = UIColor.mfBattleshipGrey()
bottomBar.backgroundColor = UIColor.black.cgColor bottomBar.backgroundColor = UIColor.black.cgColor
case .disable: case .disable:
isUserInteractionEnabled = false isUserInteractionEnabled = false
feedback = nil
borderStrokeColor = .mfSilver() borderStrokeColor = .mfSilver()
titleLabel.textColor = self.isEnabled ? UIColor.mfBattleshipGrey() : UIColor.mfSilver() titleLabel.textColor = self.isEnabled ? UIColor.mfBattleshipGrey() : UIColor.mfSilver()
bottomBar.backgroundColor = self.isEnabled ? UIColor.black.cgColor : UIColor.mfSilver().cgColor bottomBar.backgroundColor = self.isEnabled ? UIColor.black.cgColor : UIColor.mfSilver().cgColor

View File

@ -20,8 +20,6 @@ import MVMCore
public var isNationalMdn = true public var isNationalMdn = true
public var shouldValidateMDN = false public var shouldValidateMDN = false
// public var pickerView: UIPickerView?
public var mdn: String? { public var mdn: String? {
get { return MVMCoreUIUtility.removeMdnFormat(text) } get { return MVMCoreUIUtility.removeMdnFormat(text) }
set { text = MVMCoreUIUtility.formatMdn(newValue) } set { text = MVMCoreUIUtility.formatMdn(newValue) }
@ -40,6 +38,13 @@ import MVMCore
self.init(frame: .zero) self.init(frame: .zero)
} }
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
public override init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) {
super.init(frame: .zero)
setup()
setBothTextDelegates(to: bothDelegates)
}
required public init?(coder: NSCoder) { required public init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
fatalError("MdnEntryField xib not supported.") fatalError("MdnEntryField xib not supported.")
@ -65,7 +70,7 @@ import MVMCore
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
func hasValidMDN() -> Bool { public func hasValidMDN() -> Bool {
guard let MDN = mdn, guard let MDN = mdn,
!MDN.isEmpty !MDN.isEmpty
@ -78,7 +83,7 @@ import MVMCore
return MVMCoreUIUtility.validateInternationalMDNString(MDN) return MVMCoreUIUtility.validateInternationalMDNString(MDN)
} }
func validateAndColor() -> Bool { public func validateAndColor() -> Bool {
if !shouldValidateMDN { if !shouldValidateMDN {
let isValid = hasValidMDN() let isValid = hasValidMDN()
@ -107,7 +112,7 @@ import MVMCore
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - ContactPicker Delegate // MARK: - Contact Picker Delegate
//-------------------------------------------------- //--------------------------------------------------
public func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) { public func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
@ -127,7 +132,7 @@ import MVMCore
} }
text = unformattedMDN text = unformattedMDN
textFieldShouldReturn(textField) _ = textFieldShouldReturn(textField)
textFieldDidEndEditing(textField) textFieldDidEndEditing(textField)
} }
} }
@ -136,15 +141,14 @@ import MVMCore
// MARK: - Implemented TextField Delegate // MARK: - Implemented TextField Delegate
//-------------------------------------------------- //--------------------------------------------------
@discardableResult public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder() textField.resignFirstResponder()
return uiTextFieldDelegate?.textFieldShouldReturn?(textField) ?? true return uiTextFieldDelegate?.textFieldShouldReturn?(textField) ?? true
} }
@objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) { if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) {
return false return false
@ -172,17 +176,17 @@ import MVMCore
// MARK: - Passed Along TextField delegate // MARK: - Passed Along TextField delegate
//-------------------------------------------------- //--------------------------------------------------
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true
} }
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
return uiTextFieldDelegate?.textFieldShouldEndEditing?(textField) ?? true return uiTextFieldDelegate?.textFieldShouldEndEditing?(textField) ?? true
} }
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool { public func textFieldShouldClear(_ textField: UITextField) -> Bool {
return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true
} }

View File

@ -65,15 +65,11 @@ import UIKit
public var placeholder: String? { public var placeholder: String? {
get { return textField.placeholder } get { return textField.placeholder }
set { set { textField.placeholder = newValue }
textField.placeholder = newValue
}
} }
public var validationBlock: ((_ value: String?) -> Bool)? { public var validationBlock: ((_ value: String?) -> Bool)? {
didSet { didSet { valueChanged() }
valueChanged()
}
} }
public override var errorMessage: String? { public override var errorMessage: String? {
@ -174,6 +170,21 @@ import UIKit
open func clearErrorState() { open func clearErrorState() {
/*
[MVMCoreDispatchUtility performBlockOnMainThread:^{
self.separatorHeightConstraint.constant = 1;
self.separatorView.backgroundColor = [UIColor blackColor];
[self layoutIfNeeded];
self.errorShowing = NO;
self.label.textColor = [UIColor blackColor];
self.label.text = @"";
self.textField.accessibilityValue = nil;
[self setNeedsDisplay];
[self layoutIfNeeded];
}];
*/
textField.accessibilityValue = nil textField.accessibilityValue = nil
updateUI(appearance: .original) updateUI(appearance: .original)
} }
@ -202,7 +213,7 @@ import UIKit
@objc func valueChanged() { @objc func valueChanged() {
if !showErrorMessage { if !showError {
feedback = "" feedback = ""
} }
@ -217,10 +228,7 @@ import UIKit
} else if !previousValidity && isValid { } else if !previousValidity && isValid {
clearErrorState() clearErrorState()
mfTextFieldDelegate?.isValid?(textfield: self)
if let mfTextFieldDelegate = mfTextFieldDelegate {
mfTextFieldDelegate.isValid?(textfield: self)
}
} }
} }
@ -228,6 +236,7 @@ import UIKit
if isValid { if isValid {
clearErrorState() clearErrorState()
bottomBar.backgroundColor = UIColor.black.cgColor
} else if let errMessage = errorMessage { } else if let errMessage = errorMessage {
feedback = errMessage feedback = errMessage
@ -236,6 +245,10 @@ import UIKit
@objc func startEditing() { @objc func startEditing() {
if appearance != .original {
updateUI(appearance: .original)
}
textField.becomeFirstResponder() textField.becomeFirstResponder()
} }
} }

View File

@ -2,52 +2,53 @@
// CaretView.swift // CaretView.swift
// MobileFirstFramework // MobileFirstFramework
// //
// Created by Kolli, Praneeth on 1/5/18. // Created by Christiano, Kevin on 1/5/18.
// Converted by Christiano, Kevin on 1/5/18.
// Copyright © 2018 Verizon Wireless. All rights reserved. // Copyright © 2018 Verizon Wireless. All rights reserved.
// //
open class CaretView: MFView { @objcMembers open class CaretView: MFView {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Properties // MARK: - Properties
//------------------------------------------------------ //------------------------------------------------------
// Objc can't use float enum. // Objc can't use float enum.
@objc public static let thin: CGFloat = 6.0 public static let thin: CGFloat = 6.0
@objc public static let standard: CGFloat = 2.6 public static let standard: CGFloat = 2.5
@objc public static let thick: CGFloat = 1.5 public static let thick: CGFloat = 1.5
public var strokeColor: UIColor = .black
public var lineWidth: CGFloat = 1
public var direction: Direction = .right
private var caretPath: UIBezierPath = UIBezierPath()
private(set) var strokeColor: UIColor?
private var lineWidth: CGFloat?
private var lineThickness: CGFloat?
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Initialization // MARK: - Initialization
//------------------------------------------------------ //------------------------------------------------------
@objc public init() {
super.init(frame: .zero)
}
@objc public override init(frame: CGRect) { @objc public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
} }
@objc required public init?(coder aDecoder: NSCoder) { @objc public init() {
super.init(coder: aDecoder) super.init(frame: .zero)
} }
/// Can init with a specific line width.
@objc public init(lineWidth: CGFloat) { @objc public init(lineWidth: CGFloat) {
super.init(frame: CGRect()) super.init(frame: .zero)
self.lineWidth = lineWidth self.lineWidth = lineWidth
} }
/// Can init with a specific line thickness, scales based on width and height. /// Can init with a specific line thickness, scales based on width and height.
@objc public init(lineThickness: CGFloat) { @objc public init(lineThickness: CGFloat) {
super.init(frame: CGRect()) super.init(frame: .zero)
self.lineThickness = lineThickness // self.lineThickness = lineThickness
}
@objc required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("CaretView xib not supported.")
} }
@objc override open func setupView() { @objc override open func setupView() {
@ -55,9 +56,60 @@ open class CaretView: MFView {
} }
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Private Function // MARK: - Drawing
//------------------------------------------------------ //------------------------------------------------------
/// The direction the caret will be pointing to.
public enum Direction: String {
case left
case right
case down
case up
}
@objc override open func draw(_ rect: CGRect) {
super.draw(rect)
caretPath.removeAllPoints()
caretPath.lineJoinStyle = .miter
caretPath.lineWidth = lineWidth
let inset = lineWidth / 2
switch direction {
case .up:
caretPath.move(to: CGPoint(x: inset, y: frame.size.height - inset))
caretPath.addLine(to: CGPoint(x: frame.size.width / 2, y: inset))
caretPath.addLine(to: CGPoint(x: frame.size.width, y: frame.size.height))
case .right:
caretPath.move(to: CGPoint(x: inset, y: inset))
caretPath.addLine(to: CGPoint(x: frame.size.width - inset, y: frame.size.height / 2))
caretPath.addLine(to: CGPoint(x: inset, y: frame.size.height - inset))
case .down:
caretPath.move(to: CGPoint(x: inset, y: inset))
caretPath.addLine(to: CGPoint(x: frame.size.width / 2, y: frame.size.height - inset))
caretPath.addLine(to: CGPoint(x: frame.size.width - inset, y: inset))
case .left:
caretPath.move(to: CGPoint(x: frame.size.width - inset, y: inset))
caretPath.addLine(to: CGPoint(x: inset, y: frame.size.height / 2))
caretPath.addLine(to: CGPoint(x: frame.size.width - inset, y: frame.size.height - inset))
}
strokeColor.setStroke()
caretPath.stroke()
}
//------------------------------------------------------
// MARK: - Methods
//------------------------------------------------------
@objc public func setLineColor(_ color: UIColor) {
strokeColor = color
setNeedsDisplay()
}
private func defaultState() { private func defaultState() {
isOpaque = false isOpaque = false
isHidden = false isHidden = false
@ -65,34 +117,6 @@ open class CaretView: MFView {
strokeColor = .black strokeColor = .black
} }
//------------------------------------------------------
// MARK: - Drawing
//------------------------------------------------------
@objc override open func draw(_ rect: CGRect) {
// Drawing Caret
let context = UIGraphicsGetCurrentContext()
context?.clear(rect)
let lineWidthToDraw: CGFloat = lineWidth ?? frame.size.width / (lineThickness ?? 2.6)
let path = UIBezierPath()
path.move(to: CGPoint(x: lineWidthToDraw / 2.0, y: 0.0))
path.addLine(to: CGPoint(x: frame.size.width, y: frame.size.height / 2.0))
path.addLine(to: CGPoint(x: lineWidthToDraw / 2.0, y: frame.size.height))
path.addLine(to: CGPoint(x: 0.0, y: frame.size.height - lineWidthToDraw / 2.0))
path.addLine(to: CGPoint(x: frame.size.width - lineWidthToDraw, y: frame.size.height / 2.0))
path.addLine(to: CGPoint(x: 0.0, y: lineWidthToDraw / 2.0))
path.addLine(to: CGPoint(x: lineWidthToDraw / 2.0, y: 0.0))
strokeColor?.setFill()
path.fill()
path.close()
}
@objc public func setLineColor(_ color: UIColor?) {
strokeColor = color
setNeedsDisplay()
}
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Atomization // MARK: - Atomization
@ -102,10 +126,10 @@ open class CaretView: MFView {
@objc open override func setAsMolecule() { @objc open override func setAsMolecule() {
defaultState() defaultState()
} }
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
// Configure class properties with JSON values
guard let dictionary = json else { return } guard let dictionary = json else { return }
if let strokeColorHex = dictionary["strokeColor"] as? String { if let strokeColorHex = dictionary["strokeColor"] as? String {
@ -115,7 +139,7 @@ open class CaretView: MFView {
if let isHiddenValue = dictionary[KeyIsHidden] as? Bool { if let isHiddenValue = dictionary[KeyIsHidden] as? Bool {
isHidden = isHiddenValue isHidden = isHiddenValue
} }
if let isOpaqueValue = dictionary[KeyIsOpaque] as? Bool { if let isOpaqueValue = dictionary[KeyIsOpaque] as? Bool {
isOpaque = isOpaqueValue isOpaque = isOpaqueValue
} }
@ -130,6 +154,6 @@ open class CaretView: MFView {
} }
open override func alignment() -> UIStackView.Alignment { open override func alignment() -> UIStackView.Alignment {
return UIStackView.Alignment.leading; return .leading
} }
} }

View File

@ -31,7 +31,7 @@ open class DashLine: MFView {
required public init?(coder: NSCoder) { required public init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
fatalError("DashLine xib not supported") // fatalError("DashLine xib not supported")
} }
//------------------------------------------------------ //------------------------------------------------------