Reworked bezier logic for CaretView. It was not optimal.
This commit is contained in:
parent
5e96653d4b
commit
475d08296b
@ -25,8 +25,6 @@ import UIKit
|
||||
return layer
|
||||
}()
|
||||
|
||||
weak var textBoxDelegate: DigitBoxDelegate?
|
||||
|
||||
private var previousSize: CGFloat = 0.0
|
||||
|
||||
/// Determines if a border should be drawn.
|
||||
@ -36,6 +34,12 @@ import UIKit
|
||||
private var borderStrokeColor: UIColor = .mfSilver()
|
||||
private var borderPath: UIBezierPath = UIBezierPath()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
weak var textBoxDelegate: DigitBoxDelegate?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
@ -47,11 +51,6 @@ import UIKit
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
fatalError("DigitBox has not been implemented")
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup()
|
||||
@ -61,11 +60,16 @@ import UIKit
|
||||
self.init(frame: .zero)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
fatalError("DigitBox has not been implemented")
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
func setup() {
|
||||
private func setup() {
|
||||
|
||||
guard constraints.isEmpty else { return }
|
||||
|
||||
@ -74,7 +78,7 @@ import UIKit
|
||||
textAlignment = .center
|
||||
keyboardType = .numberPad
|
||||
layer.borderWidth = 1
|
||||
showError(false)
|
||||
showErrorState(false)
|
||||
|
||||
widthConstraint = widthAnchor.constraint(equalToConstant: 39)
|
||||
widthConstraint?.isActive = true
|
||||
@ -83,7 +87,6 @@ import UIKit
|
||||
heightConstraint?.isActive = true
|
||||
|
||||
layer.addSublayer(bottomBar)
|
||||
|
||||
updateView(MVMCoreUISplitViewController.getDetailViewWidth())
|
||||
}
|
||||
|
||||
@ -161,7 +164,7 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
func showError(_ show: Bool) {
|
||||
func showErrorState(_ show: Bool) {
|
||||
|
||||
showError = show
|
||||
borderStrokeColor = show ? .mfPumpkin() : .mfSilver()
|
||||
|
||||
@ -8,7 +8,10 @@
|
||||
|
||||
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 {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Outlets
|
||||
@ -20,69 +23,65 @@ import UIKit
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
private var numberOfDigits = 0
|
||||
private var numberOfDigits = 4
|
||||
private var switchedAutomatically = false
|
||||
public var digitFields: [DigitBox]?
|
||||
public var digitFields: [DigitBox] = []
|
||||
|
||||
public override var isEnabled: Bool {
|
||||
didSet {
|
||||
if isEnabled {
|
||||
titleLabel.styleB2(true)
|
||||
} else {
|
||||
titleLabel.textColor = UIColor.mfBattleshipGrey()
|
||||
titleLabel.textColor = .mfBattleshipGrey()
|
||||
}
|
||||
|
||||
for textField in digitFields ?? [] {
|
||||
for textField in digitFields {
|
||||
textField.isUserInteractionEnabled = isEnabled
|
||||
textField.isEnabled = isEnabled
|
||||
textField.textColor = isEnabled ? .black : UIColor.mfBattleshipGrey()
|
||||
textField.textColor = isEnabled ? .black : .mfBattleshipGrey()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets placeholder text in the textField.
|
||||
public override var feedback: String? {
|
||||
public override var placeholder: String? {
|
||||
get {
|
||||
var string = ""
|
||||
|
||||
for digitField in digitFields ?? [] {
|
||||
if let digitext = digitField.attributedPlaceholder?.string {
|
||||
string += digitext
|
||||
}
|
||||
}
|
||||
|
||||
digitFields.forEach { string += $0.attributedPlaceholder?.string ?? "" }
|
||||
|
||||
return !string.isEmpty ? string : nil
|
||||
}
|
||||
set {
|
||||
guard let fieldValue = newValue, let fields = digitFields else { return }
|
||||
guard let newValue = newValue else { return }
|
||||
|
||||
for (index, field) in fields.enumerated() {
|
||||
if index < fieldValue.count {
|
||||
let stringForIndex = (newValue as NSString?)?.substring(with: NSRange(location: index, length: 1))
|
||||
field.attributedPlaceholder = NSAttributedString(string: stringForIndex ?? "", attributes: [
|
||||
for (index, field) in digitFields.enumerated() {
|
||||
if index < newValue.count {
|
||||
let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
|
||||
field.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [
|
||||
NSAttributedString.Key.foregroundColor: UIColor.mfBattleshipGrey()])
|
||||
}
|
||||
}
|
||||
//
|
||||
// if feedback.text.length > 0 {
|
||||
// labelToTextFieldPin?.constant = 10
|
||||
// } else {
|
||||
// labelToTextFieldPin?.constant = 0
|
||||
// }
|
||||
|
||||
// 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"))
|
||||
//
|
||||
// super.showErrorMessage(errorMessage)
|
||||
//
|
||||
// if self.showErrorMessage {
|
||||
// self.labelToTextFieldPin?.constant = 10
|
||||
// }
|
||||
// for field in self.digitFields ?? [] {
|
||||
// field.setAsError()
|
||||
// }
|
||||
// If there is already text in the textfield, set the place holder label below.
|
||||
if let text = text, !text.isEmpty && !showError {
|
||||
feedback = placeholder
|
||||
} else if !showError {
|
||||
feedback = ""
|
||||
}
|
||||
|
||||
if let feedback = feedback, !feedback.isEmpty {
|
||||
feedbackContainerDistance?.constant = 10
|
||||
} else {
|
||||
feedbackContainerDistance?.constant = 0
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
var string = ""
|
||||
|
||||
for digitField in digitFields ?? [] {
|
||||
if let digitText = digitField.text {
|
||||
string += digitText
|
||||
}
|
||||
}
|
||||
|
||||
digitFields.forEach { string += $0.text ?? "" }
|
||||
|
||||
return string
|
||||
}
|
||||
set {
|
||||
guard let fields = self.digitFields else { return }
|
||||
guard let newValue = newValue else { return }
|
||||
|
||||
for (index, field) in fields.enumerated() {
|
||||
if index < (text?.count ?? 0) {
|
||||
let stringForIndex = (text as NSString?)?.substring(with: NSRange(location: index, length: 1))
|
||||
field.text = stringForIndex
|
||||
for (index, field) in digitFields.enumerated() {
|
||||
if index < newValue.count {
|
||||
let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
|
||||
field.text = String(newValue[indexChar])
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,9 +111,9 @@ import UIKit
|
||||
get { return titleLabel.text }
|
||||
set {
|
||||
if let formText = newValue, !formText.isEmpty {
|
||||
messageToTextFieldPin?.constant = 10
|
||||
titleContainerDistance?.constant = 10
|
||||
} else {
|
||||
messageToTextFieldPin?.constant = 0
|
||||
titleContainerDistance?.constant = 0
|
||||
}
|
||||
|
||||
super.title = newValue
|
||||
@ -131,21 +126,17 @@ import UIKit
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
for field in self.digitFields ?? [] {
|
||||
field.showError(false)
|
||||
if self.showError {
|
||||
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
|
||||
//--------------------------------------------------
|
||||
@ -185,6 +176,7 @@ import UIKit
|
||||
|
||||
open func setup() {
|
||||
|
||||
hideBorder = true
|
||||
titleLabel.styleB2(true)
|
||||
alignCenterHorizontal()
|
||||
}
|
||||
@ -206,16 +198,11 @@ import UIKit
|
||||
|
||||
self.titleLabel.updateView(size)
|
||||
|
||||
|
||||
if let digitFields = self.digitFields, !digitFields.isEmpty {
|
||||
|
||||
// Remove all current UI.
|
||||
StackableViewController.remove(digitFields)
|
||||
|
||||
// Update text boxes.
|
||||
for digitField in digitFields {
|
||||
digitField.updateView(size)
|
||||
}
|
||||
if !self.digitFields.isEmpty {
|
||||
|
||||
StackableViewController.remove(self.digitFields)
|
||||
|
||||
self.digitFields.forEach { $0.updateView(size) }
|
||||
}
|
||||
|
||||
// Layout text boxes.
|
||||
@ -227,6 +214,14 @@ import UIKit
|
||||
// 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 {
|
||||
|
||||
let textField = DigitBox()
|
||||
@ -239,22 +234,20 @@ import UIKit
|
||||
func buildTextFieldsView(size: CGFloat) {
|
||||
|
||||
// Remove all current UI.
|
||||
if let digitFields = digitFields, !digitFields.isEmpty {
|
||||
if !digitFields.isEmpty {
|
||||
StackableViewController.remove(digitFields)
|
||||
}
|
||||
|
||||
if numberOfDigits > 0 {
|
||||
let digitFields = [DigitBox](repeating: createDigitField(), count: numberOfDigits)
|
||||
|
||||
for digitField in digitFields {
|
||||
digitField.updateView(size)
|
||||
}
|
||||
digitFields.forEach { $0.updateView(size) }
|
||||
|
||||
self.digitFields = digitFields
|
||||
setupTextFieldsView(forSize: size)
|
||||
|
||||
} else {
|
||||
digitFields = nil
|
||||
digitFields = []
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,10 +258,10 @@ import UIKit
|
||||
guard let self = self else { return }
|
||||
|
||||
if let feedback = self.feedback, !feedback.isEmpty {
|
||||
self.labelToTextFieldPin?.constant = 10
|
||||
self.feedbackContainerDistance?.constant = 10
|
||||
|
||||
} else {
|
||||
self.labelToTextFieldPin?.constant = 0
|
||||
self.feedbackContainerDistance?.constant = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -276,15 +269,13 @@ import UIKit
|
||||
func setAsSecureTextEntry(_ secureEntry: Bool) {
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self,
|
||||
let fields = self.digitFields
|
||||
else { return }
|
||||
guard let self = self else { return }
|
||||
|
||||
for (index, field) in fields.enumerated() {
|
||||
for (index, field) in self.digitFields.enumerated() {
|
||||
field.isSecureTextEntry = secureEntry
|
||||
|
||||
// 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) {
|
||||
|
||||
guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize(),
|
||||
let digitFieldsView = digitFieldsView,
|
||||
let digitFields = digitFields
|
||||
let digitFieldsView = digitFieldsView
|
||||
else { return }
|
||||
|
||||
StackableViewController.populateViewHorizontally(digitFieldsView, withUIArray: digitFields, withSpacingBlock: { object in
|
||||
|
||||
var inset = UIEdgeInsets(top: 0, left: space, bottom: 0, right: space)
|
||||
|
||||
guard let digitFields = self.digitFields else { return inset }
|
||||
|
||||
if digitFields.count == 1 {
|
||||
if self.digitFields.count == 1 {
|
||||
inset.left = 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
|
||||
|
||||
} else if let field = object as? UITextField, field == digitFields.last {
|
||||
} else if let field = object as? UITextField, field == self.digitFields.last {
|
||||
inset.right = 0
|
||||
}
|
||||
|
||||
@ -319,57 +307,18 @@ import UIKit
|
||||
|
||||
public override func defaultValidationBlock() {
|
||||
|
||||
weak var weakSelf = self
|
||||
|
||||
validationBlock = { enteredValue in
|
||||
|
||||
if (enteredValue?.count ?? 0) > 0 && (enteredValue?.count ?? 0) == weakSelf?.digitFields?.count {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
guard let enteredValue = enteredValue else { return false }
|
||||
|
||||
return enteredValue.count > 0 && enteredValue.count == self.digitFields.count
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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) {
|
||||
|
||||
var selectNextField = false
|
||||
|
||||
guard let fields = digitFields else { return }
|
||||
|
||||
for field in fields {
|
||||
for field in digitFields {
|
||||
|
||||
if field == currentTextField {
|
||||
selectNextField = true
|
||||
@ -390,18 +339,16 @@ import UIKit
|
||||
|
||||
var selectNextField = false
|
||||
|
||||
guard let fields = digitFields else { return }
|
||||
|
||||
for field in fields{
|
||||
for field in digitFields {
|
||||
if field == currentTextField {
|
||||
selectNextField = true
|
||||
|
||||
} else if selectNextField {
|
||||
if !clear {
|
||||
self.switchedAutomatically = true
|
||||
switchedAutomatically = true
|
||||
}
|
||||
field.becomeFirstResponder()
|
||||
self.switchedAutomatically = false
|
||||
switchedAutomatically = false
|
||||
|
||||
UIAccessibility.post(notification: .layoutChanged, argument: field)
|
||||
}
|
||||
@ -409,20 +356,46 @@ import UIKit
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Accessinility
|
||||
// MARK: - Molecule
|
||||
//--------------------------------------------------
|
||||
|
||||
open override class func accessibilityElements() -> [Any]? {
|
||||
// self.digit
|
||||
// if let digitFields = self.digitFields {
|
||||
// return [digitFields] + [textField]
|
||||
// }
|
||||
//
|
||||
return [textField]
|
||||
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: - 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 {
|
||||
@ -495,7 +468,10 @@ import UIKit
|
||||
return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Passed Along TextField delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
|
||||
@ -14,23 +14,19 @@ import UIKit
|
||||
// MARK: - Outlets
|
||||
//--------------------------------------------------
|
||||
|
||||
let dropDownCaretLabel: Label = {
|
||||
let label = Label()
|
||||
label.setContentHuggingPriority(UILayoutPriority(900), for: .horizontal)
|
||||
label.setContentHuggingPriority(UILayoutPriority(251), for: .vertical)
|
||||
label.setContentCompressionResistancePriority(UILayoutPriority(900), for: .horizontal)
|
||||
label.isHidden = true
|
||||
label.isUserInteractionEnabled = true
|
||||
return label
|
||||
let dropDownCaretLabel: CaretView = {
|
||||
let caret = CaretView()
|
||||
caret.isHidden = true
|
||||
caret.isUserInteractionEnabled = true
|
||||
return caret
|
||||
}()
|
||||
|
||||
private var calendar: Calendar?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Accessories
|
||||
//--------------------------------------------------
|
||||
|
||||
public weak var datePicker: UIDatePicker?
|
||||
private var calendar: Calendar?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
@ -39,9 +35,7 @@ import UIKit
|
||||
public var dropDownIsDisplayed = false
|
||||
|
||||
public override var isEnabled: Bool {
|
||||
didSet {
|
||||
showDropDown(isEnabled)
|
||||
}
|
||||
didSet { showDropDown(isEnabled) }
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -56,10 +50,9 @@ import UIKit
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupView()
|
||||
setup()
|
||||
}
|
||||
|
||||
/// Basic initializer.
|
||||
public convenience init() {
|
||||
self.init(frame: .zero)
|
||||
}
|
||||
@ -68,7 +61,7 @@ import UIKit
|
||||
public override init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) {
|
||||
super.init(frame: .zero)
|
||||
|
||||
setupView()
|
||||
setup()
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: bothDelegates)
|
||||
setBothTextDelegates(to: bothDelegates)
|
||||
}
|
||||
@ -78,6 +71,13 @@ import UIKit
|
||||
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
|
||||
//--------------------------------------------------
|
||||
@ -116,7 +116,7 @@ import UIKit
|
||||
override func startEditing() {
|
||||
super.startEditing()
|
||||
|
||||
showDropDown(!showErrorMessage)
|
||||
showDropDown(!showError)
|
||||
}
|
||||
|
||||
class func getEnabledTextfields(_ textFieldToDetermine: [TextEntryField]?) -> [AnyHashable]? {
|
||||
|
||||
@ -43,6 +43,22 @@ import UIKit
|
||||
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 borderPath: UIBezierPath = UIBezierPath()
|
||||
|
||||
@ -55,21 +71,9 @@ import UIKit
|
||||
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 var showErrorMessage = false
|
||||
public var showError = false
|
||||
|
||||
public var errorMessage: String?
|
||||
|
||||
@ -133,6 +137,9 @@ import UIKit
|
||||
public var titleLabelLeading: NSLayoutConstraint?
|
||||
public var titleLabelTrailing: NSLayoutConstraint?
|
||||
|
||||
public var titleContainerDistance: NSLayoutConstraint?
|
||||
public var feedbackContainerDistance: NSLayoutConstraint?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
@ -150,7 +157,7 @@ import UIKit
|
||||
public init(title: String) {
|
||||
super.init(frame: .zero)
|
||||
setupView()
|
||||
self.titleLabel.text = title
|
||||
titleLabel.text = title
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
@ -182,7 +189,8 @@ import UIKit
|
||||
addSubview(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?.isActive = true
|
||||
fieldContainerTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: fieldContainer.trailingAnchor)
|
||||
@ -190,7 +198,8 @@ import UIKit
|
||||
|
||||
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?.isActive = true
|
||||
feedbackLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: feedbackLabel.trailingAnchor)
|
||||
@ -203,10 +212,8 @@ import UIKit
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
/**
|
||||
Method to override.
|
||||
Intended to add the interactive content (i.e. textField) to the fieldContainer.
|
||||
*/
|
||||
/// Method to override.
|
||||
/// Intended to add the interactive content (i.e. textField) to the fieldContainer.
|
||||
open func setupFieldContainerContent(_ container: UIView) {
|
||||
// To be overridden by subclass.
|
||||
}
|
||||
@ -237,7 +244,7 @@ import UIKit
|
||||
|
||||
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)
|
||||
|
||||
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self)
|
||||
@ -274,11 +281,6 @@ import UIKit
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
func resizeBottomBar(size: CGFloat) {
|
||||
|
||||
bottomBar.frame = CGRect(x: 0, y: fieldContainer.bounds.height - size, width: fieldContainer.bounds.width, height: size)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraint Methods
|
||||
//--------------------------------------------------
|
||||
@ -313,37 +315,30 @@ import UIKit
|
||||
|
||||
self.appearance = appearance
|
||||
isUserInteractionEnabled = true
|
||||
titleLabel.textColor = .mfBattleshipGrey()
|
||||
hideBorder = false
|
||||
feedback = showError ? errorMessage : nil
|
||||
|
||||
switch appearance {
|
||||
case .original:
|
||||
borderStrokeColor = .mfSilver()
|
||||
feedback = nil
|
||||
bottomBar.backgroundColor = UIColor.black.cgColor
|
||||
titleLabel.textColor = .mfBattleshipGrey()
|
||||
|
||||
case .error:
|
||||
borderStrokeColor = .mfPumpkin()
|
||||
titleLabel.textColor = UIColor.mfBattleshipGrey()
|
||||
bottomBar.backgroundColor = UIColor.mfPumpkin().cgColor
|
||||
feedback = showErrorMessage ? errorMessage : nil
|
||||
|
||||
case .lock:
|
||||
isUserInteractionEnabled = false
|
||||
hideBorder = true
|
||||
feedback = nil
|
||||
titleLabel.textColor = UIColor.mfBattleshipGrey()
|
||||
bottomBar.backgroundColor = UIColor.clear.cgColor
|
||||
|
||||
case .select:
|
||||
borderStrokeColor = .black
|
||||
feedback = nil
|
||||
titleLabel.textColor = UIColor.mfBattleshipGrey()
|
||||
bottomBar.backgroundColor = UIColor.black.cgColor
|
||||
|
||||
case .disable:
|
||||
isUserInteractionEnabled = false
|
||||
feedback = nil
|
||||
borderStrokeColor = .mfSilver()
|
||||
titleLabel.textColor = self.isEnabled ? UIColor.mfBattleshipGrey() : UIColor.mfSilver()
|
||||
bottomBar.backgroundColor = self.isEnabled ? UIColor.black.cgColor : UIColor.mfSilver().cgColor
|
||||
|
||||
@ -20,8 +20,6 @@ import MVMCore
|
||||
public var isNationalMdn = true
|
||||
public var shouldValidateMDN = false
|
||||
|
||||
// public var pickerView: UIPickerView?
|
||||
|
||||
public var mdn: String? {
|
||||
get { return MVMCoreUIUtility.removeMdnFormat(text) }
|
||||
set { text = MVMCoreUIUtility.formatMdn(newValue) }
|
||||
@ -40,6 +38,13 @@ import MVMCore
|
||||
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) {
|
||||
super.init(coder: coder)
|
||||
fatalError("MdnEntryField xib not supported.")
|
||||
@ -65,7 +70,7 @@ import MVMCore
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
func hasValidMDN() -> Bool {
|
||||
public func hasValidMDN() -> Bool {
|
||||
|
||||
guard let MDN = mdn,
|
||||
!MDN.isEmpty
|
||||
@ -78,7 +83,7 @@ import MVMCore
|
||||
return MVMCoreUIUtility.validateInternationalMDNString(MDN)
|
||||
}
|
||||
|
||||
func validateAndColor() -> Bool {
|
||||
public func validateAndColor() -> Bool {
|
||||
|
||||
if !shouldValidateMDN {
|
||||
let isValid = hasValidMDN()
|
||||
@ -107,7 +112,7 @@ import MVMCore
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - ContactPicker Delegate
|
||||
// MARK: - Contact Picker Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
public func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
|
||||
@ -127,7 +132,7 @@ import MVMCore
|
||||
}
|
||||
|
||||
text = unformattedMDN
|
||||
textFieldShouldReturn(textField)
|
||||
_ = textFieldShouldReturn(textField)
|
||||
textFieldDidEndEditing(textField)
|
||||
}
|
||||
}
|
||||
@ -136,15 +141,14 @@ import MVMCore
|
||||
// MARK: - Implemented TextField Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
@discardableResult
|
||||
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
|
||||
textField.resignFirstResponder()
|
||||
|
||||
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) {
|
||||
return false
|
||||
@ -172,17 +176,17 @@ import MVMCore
|
||||
// MARK: - Passed Along TextField delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldEndEditing?(textField) ?? true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
|
||||
public func textFieldShouldClear(_ textField: UITextField) -> Bool {
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true
|
||||
}
|
||||
|
||||
@ -65,15 +65,11 @@ import UIKit
|
||||
|
||||
public var placeholder: String? {
|
||||
get { return textField.placeholder }
|
||||
set {
|
||||
textField.placeholder = newValue
|
||||
}
|
||||
set { textField.placeholder = newValue }
|
||||
}
|
||||
|
||||
public var validationBlock: ((_ value: String?) -> Bool)? {
|
||||
didSet {
|
||||
valueChanged()
|
||||
}
|
||||
didSet { valueChanged() }
|
||||
}
|
||||
|
||||
public override var errorMessage: String? {
|
||||
@ -174,6 +170,21 @@ import UIKit
|
||||
|
||||
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
|
||||
updateUI(appearance: .original)
|
||||
}
|
||||
@ -202,7 +213,7 @@ import UIKit
|
||||
|
||||
@objc func valueChanged() {
|
||||
|
||||
if !showErrorMessage {
|
||||
if !showError {
|
||||
feedback = ""
|
||||
}
|
||||
|
||||
@ -217,10 +228,7 @@ import UIKit
|
||||
|
||||
} else if !previousValidity && isValid {
|
||||
clearErrorState()
|
||||
|
||||
if let mfTextFieldDelegate = mfTextFieldDelegate {
|
||||
mfTextFieldDelegate.isValid?(textfield: self)
|
||||
}
|
||||
mfTextFieldDelegate?.isValid?(textfield: self)
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,6 +236,7 @@ import UIKit
|
||||
|
||||
if isValid {
|
||||
clearErrorState()
|
||||
bottomBar.backgroundColor = UIColor.black.cgColor
|
||||
|
||||
} else if let errMessage = errorMessage {
|
||||
feedback = errMessage
|
||||
@ -236,6 +245,10 @@ import UIKit
|
||||
|
||||
@objc func startEditing() {
|
||||
|
||||
if appearance != .original {
|
||||
updateUI(appearance: .original)
|
||||
}
|
||||
|
||||
textField.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,52 +2,53 @@
|
||||
// CaretView.swift
|
||||
// MobileFirstFramework
|
||||
//
|
||||
// Created by Kolli, Praneeth on 1/5/18.
|
||||
// Converted by Christiano, Kevin on 1/5/18.
|
||||
// Created by Christiano, Kevin on 1/5/18.
|
||||
// Copyright © 2018 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
open class CaretView: MFView {
|
||||
@objcMembers open class CaretView: MFView {
|
||||
//------------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//------------------------------------------------------
|
||||
|
||||
// Objc can't use float enum.
|
||||
@objc public static let thin: CGFloat = 6.0
|
||||
@objc public static let standard: CGFloat = 2.6
|
||||
@objc public static let thick: CGFloat = 1.5
|
||||
public static let thin: CGFloat = 6.0
|
||||
public static let standard: CGFloat = 2.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
|
||||
//------------------------------------------------------
|
||||
|
||||
@objc public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
@objc public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
@objc required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
@objc public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
/// Can init with a specific line width.
|
||||
@objc public init(lineWidth: CGFloat) {
|
||||
super.init(frame: CGRect())
|
||||
super.init(frame: .zero)
|
||||
self.lineWidth = lineWidth
|
||||
}
|
||||
|
||||
/// Can init with a specific line thickness, scales based on width and height.
|
||||
@objc public init(lineThickness: CGFloat) {
|
||||
super.init(frame: CGRect())
|
||||
self.lineThickness = lineThickness
|
||||
super.init(frame: .zero)
|
||||
// self.lineThickness = lineThickness
|
||||
}
|
||||
|
||||
@objc required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
fatalError("CaretView xib not supported.")
|
||||
}
|
||||
|
||||
@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() {
|
||||
isOpaque = false
|
||||
isHidden = false
|
||||
@ -65,34 +117,6 @@ open class CaretView: MFView {
|
||||
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
|
||||
@ -102,10 +126,10 @@ open class CaretView: MFView {
|
||||
@objc open override func setAsMolecule() {
|
||||
defaultState()
|
||||
}
|
||||
|
||||
|
||||
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
// Configure class properties with JSON values
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let strokeColorHex = dictionary["strokeColor"] as? String {
|
||||
@ -115,7 +139,7 @@ open class CaretView: MFView {
|
||||
if let isHiddenValue = dictionary[KeyIsHidden] as? Bool {
|
||||
isHidden = isHiddenValue
|
||||
}
|
||||
|
||||
|
||||
if let isOpaqueValue = dictionary[KeyIsOpaque] as? Bool {
|
||||
isOpaque = isOpaqueValue
|
||||
}
|
||||
@ -130,6 +154,6 @@ open class CaretView: MFView {
|
||||
}
|
||||
|
||||
open override func alignment() -> UIStackView.Alignment {
|
||||
return UIStackView.Alignment.leading;
|
||||
return .leading
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ open class DashLine: MFView {
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
fatalError("DashLine xib not supported")
|
||||
// fatalError("DashLine xib not supported")
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
|
||||
Loading…
Reference in New Issue
Block a user