Merge branch 'feature/label_accessibility' into 'develop'
Label Accessibility See merge request BPHV_MIPS/mvm_core_ui!96
This commit is contained in:
commit
814d470775
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.";
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user