Merge branch 'feature/label_accessibility' into 'develop'

Label Accessibility

See merge request BPHV_MIPS/mvm_core_ui!96
This commit is contained in:
Pfeil, Scott Robert 2019-09-05 13:53:56 -04:00
commit 814d470775
5 changed files with 77 additions and 15 deletions

View File

@ -14,7 +14,7 @@ public typealias ActionBlock = () -> ()
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol { @objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - General Properties // MARK: - Properties
//------------------------------------------------------ //------------------------------------------------------
public var makeWholeViewClickable = false public var makeWholeViewClickable = false
@ -56,12 +56,22 @@ public typealias ActionBlock = () -> ()
public struct ActionableClause { public struct ActionableClause {
var range: NSRange? var range: NSRange?
var actionBlock: ActionBlock? var actionBlock: ActionBlock?
var accessibilityID: Int = 0
func performAction() { func performAction() {
actionBlock?() actionBlock?()
} }
} }
//------------------------------------------------------
// MARK: - Convenience Setter For objective-C
//------------------------------------------------------
/// Sets the clauses array to empty.
@objc public func setEmptyClauses() {
clauses = []
}
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Initialization // MARK: - Initialization
//------------------------------------------------------ //------------------------------------------------------
@ -72,6 +82,8 @@ public typealias ActionBlock = () -> ()
numberOfLines = 0 numberOfLines = 0
lineBreakMode = .byWordWrapping lineBreakMode = .byWordWrapping
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
clauses = []
accessibilityCustomActions = []
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:))) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:)))
tapGesture.numberOfTapsRequired = 1 tapGesture.numberOfTapsRequired = 1
@ -164,7 +176,7 @@ public typealias ActionBlock = () -> ()
} }
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Functions // MARK: - Style
//------------------------------------------------------ //------------------------------------------------------
@objc public static func setLabel(_ label: UILabel?, withHTML html: String?) { @objc public static func setLabel(_ label: UILabel?, withHTML html: String?) {
@ -293,10 +305,9 @@ public typealias ActionBlock = () -> ()
guard let actionLabel = label as? Label else { continue } guard let actionLabel = label as? Label else { continue }
actionLabel.addActionAttributes(range: range, string: attributedString) actionLabel.addActionAttributes(range: range, string: attributedString)
actionLabel.clauses.append(ActionableClause(range: range, let actionBlock = actionLabel.createActionBlockFrom(actionMap: json, additionalData: additionalData, delegateObject: delegate)
actionBlock: actionLabel.createActionBlockFrom(actionMap: json, actionLabel.appendActionableClause(range: range, actionBlock: actionBlock)
additionalData: additionalData,
delegateObject: delegate)))
default: default:
continue continue
} }
@ -400,7 +411,7 @@ public typealias ActionBlock = () -> ()
/** /**
Appends an external link image to the end of the attributed string. Appends an external link image to the end of the attributed string.
Will provide one whitespace to the left of the icon Will provide one whitespace to the left of the icon; adds 2 chars to the end of the string.
*/ */
@objc public func appendExternalLinkIcon() { @objc public func appendExternalLinkIcon() {
@ -417,7 +428,7 @@ public typealias ActionBlock = () -> ()
Insert external link icon anywhere within text of Label. Insert external link icon anywhere within text of Label.
- Note: Each icon insertion adds 1 additional characters to the overall text length. - Note: Each icon insertion adds 1 additional characters to the overall text length.
Therefore, you MUST insert icons and links in the order they would appear. Therefore, you **MUST** insert icons and links in the order they would appear.
- parameter index: Location within the associated text to insert an external Link Icon - parameter index: Location within the associated text to insert an external Link Icon
*/ */
public func insertExternalLinkIcon(at index: Int) { public func insertExternalLinkIcon(at index: Int) {
@ -485,6 +496,12 @@ public typealias ActionBlock = () -> ()
return containsAttachment return containsAttachment
} }
func appendActionableClause(range: NSRange, actionBlock: @escaping ActionBlock) {
let accessibleAction = customAccessibilityAction(range: range)
clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1))
}
} }
// MARK: - Atomization // MARK: - Atomization
@ -496,6 +513,8 @@ extension Label {
textAlignment = .left textAlignment = .left
originalAttributedString = nil originalAttributedString = nil
styleB2(true) styleB2(true)
accessibilityCustomActions = []
clauses = []
} }
@objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@ -524,7 +543,7 @@ extension Label {
// MARK: - Multi-Action Functionality // MARK: - Multi-Action Functionality
extension Label { extension Label {
/// Reseting to default Label values. /// Applied to existing text. Removes underlines of tappable links and assoated actionable clauses.
@objc public func clearActionableClauses() { @objc public func clearActionableClauses() {
guard let attributedText = attributedText else { return } guard let attributedText = attributedText else { return }
@ -536,6 +555,7 @@ extension Label {
} }
self.attributedText = mutableAttributedString self.attributedText = mutableAttributedString
accessibilityElements = []
clauses = [] clauses = []
} }
@ -572,7 +592,7 @@ extension Label {
@objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) {
setActionAttributes(range: range) setActionAttributes(range: range)
clauses.append(ActionableClause(range: range, actionBlock: actionBlock)) appendActionableClause(range: range, actionBlock: actionBlock)
} }
/** /**
@ -587,10 +607,8 @@ extension Label {
@objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
setActionAttributes(range: range) setActionAttributes(range: range)
clauses.append(ActionableClause(range: range, let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
actionBlock: createActionBlockFrom(actionMap: actionMap, appendActionableClause(range: range, actionBlock: actionBlock)
additionalData: additionalData,
delegateObject: delegateObject)))
} }
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
@ -642,3 +660,32 @@ extension UITapGestureRecognizer {
return NSLocationInRange(indexOfGlyph, targetRange) return NSLocationInRange(indexOfGlyph, targetRange)
} }
} }
// MARK: - Accessibility
extension Label {
func customAccessibilityAction(range: NSRange) -> UIAccessibilityCustomAction? {
guard let text = text else { return nil }
if accessibilityHint == nil {
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
}
let actionText = NSString(string: text).substring(with: range)
let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:)))
accessibilityCustomActions?.append(accessibleAction)
return accessibleAction
}
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
for clause in clauses {
if action.hash == clause.accessibilityID {
clause.performAction()
return
}
}
}
}

View File

@ -64,7 +64,15 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
if newActionBlock == nil { if newActionBlock == nil {
label?.clearActionableClauses() label?.clearActionableClauses()
} else { } else {
label?.clauses = [Label.ActionableClause(range: actionRange, actionBlock: newActionBlock)] guard let label = label else { return }
let accessibleAction = UIAccessibilityCustomAction(name: actionText ?? "", target: label, selector: #selector(label.accessibilityCustomAction(_:)))
label.clauses = [Label.ActionableClause(range: actionRange, actionBlock: newActionBlock, accessibilityID: accessibleAction.hash)]
label.accessibilityCustomActions = [accessibleAction]
if label.accessibilityHint == nil {
label.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
}
} }
} }
} }

View File

@ -8,6 +8,7 @@
//// Accessibility //// Accessibility
"AccCloseButton" = "Close"; "AccCloseButton" = "Close";
"swipe_to_select_with_action_hint" = "swipe up or down to select action, then double tap to select.";
// Tab // Tab
"AccTab" = ", tab"; "AccTab" = ", tab";
"AccTabHint" = "Double tap to select."; "AccTabHint" = "Double tap to select.";

View File

@ -6,6 +6,9 @@
Copyright © 2017 myverizon. All rights reserved. Copyright © 2017 myverizon. All rights reserved.
*/ */
// Accessibility
"swipe_to_select_with_action_hint" = "deslízate hacia arriba o hacia abajo para seleccionar la acción, luego toca dos veces para seleccionar.";
"AccCloseButton" = "Cerrar"; "AccCloseButton" = "Cerrar";
// Tab // Tab
"AccTab" = ", pestaña"; "AccTab" = ", pestaña";

View File

@ -6,6 +6,9 @@
Copyright © 2017 myverizon. All rights reserved. Copyright © 2017 myverizon. All rights reserved.
*/ */
// Accessibility
"swipe_to_select_with_action_hint" = "deslízate hacia arriba o hacia abajo para seleccionar la acción, luego toca dos veces para seleccionar.";
"AccCloseButton" = "Cerrar"; "AccCloseButton" = "Cerrar";
// Tab // Tab
"AccTab" = ", pestaña"; "AccTab" = ", pestaña";