From 67754676067cbb02e70839804ea37654d2f94b09 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 11 Jul 2019 11:43:33 -0400 Subject: [PATCH] Accessibility update for Label and LabelWithInternalButton. --- MVMCoreUI/Atoms/Views/Label.swift | 118 ++++++------------ .../Atoms/Views/LabelWithInternalButton.swift | 6 +- .../Strings/en.lproj/Localizable.strings | 1 + 3 files changed, 45 insertions(+), 80 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 38fef66f..1b1d622e 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -40,22 +40,15 @@ public typealias ActionBlock = () -> Void //------------------------------------------------------ public var clauses: [ActionableClause] = [] { - didSet { - isUserInteractionEnabled = !clauses.isEmpty - - // Accessibility - let element = UIAccessibilityElement(accessibilityContainer: self) - element.accessibilityFrameInContainerSpace = convert(boundingRect(forCharacterRange: clauses.last!.range!), to: nil) //CGRect(x: 10, y: 10, width: 100, height: 30) - element.accessibilityLabel = "Testing" - element.accessibilityTraits = .button - accessibilityElements?.append(element) - } + didSet { isUserInteractionEnabled = !clauses.isEmpty } } /// Used for tappable links in the text. public struct ActionableClause { var range: NSRange? var actionBlock: ActionBlock? + var text: String? + var hash: Int = 0 func performAction() { actionBlock?() @@ -72,8 +65,7 @@ public typealias ActionBlock = () -> Void numberOfLines = 0 lineBreakMode = .byWordWrapping translatesAutoresizingMaskIntoConstraints = false - isAccessibilityElement = false - accessibilityElements = [] + accessibilityCustomActions = [] } @objc public init() { @@ -267,10 +259,14 @@ public typealias ActionBlock = () -> Void guard let actionLabel = label as? Label else { continue } actionLabel.addActionAttributes(range: range, string: attributedString) - actionLabel.clauses.append(ActionableClause(range: range, - actionBlock: actionLabel.createActionBlockFrom(actionMap: json, - additionalData: additionalData, - delegateObject: delegate))) + let accessibleAction = actionLabel.customAccessibilityAction(range: range) + let actionBlock = actionLabel.createActionBlockFrom(actionMap: json, additionalData: additionalData, delegateObject: delegate) + let actionableClause = ActionableClause(range: range, + actionBlock: actionBlock, + text: accessibleAction?.name ?? "", + hash: accessibleAction?.hash ?? -1) + actionLabel.clauses.append(actionableClause) + default: continue } @@ -416,7 +412,7 @@ extension Label { originalAttributedString = nil hasAttachmentImage = false styleB2(true) - accessibilityElements = [] + accessibilityCustomActions = [] } @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { @@ -457,7 +453,7 @@ extension Label { } self.attributedText = mutableAttributedString -// accessibleClauses = [] + accessibilityElements = [] clauses = [] } @@ -495,7 +491,9 @@ extension Label { @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { setActionAttributes(range: range) - clauses.append(ActionableClause(range: range, actionBlock: actionBlock)) + let actionText = NSString(string: text!).substring(with: range) + let accessibleAction = customAccessibilityAction(range: range) + clauses.append(ActionableClause(range: range, actionBlock: actionBlock, text: actionText, hash: accessibleAction!.hash)) } /** @@ -511,10 +509,10 @@ extension Label { @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { setActionAttributes(range: range) - clauses.append(ActionableClause(range: range, - actionBlock: createActionBlockFrom(actionMap: actionMap, - additionalData: additionalData, - delegateObject: delegateObject))) + let actionText = NSString(string: text!).substring(with: range) + let accessibleAction = customAccessibilityAction(range: range) + let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) + clauses.append(ActionableClause(range: range, actionBlock: actionBlock, text: actionText, hash: accessibleAction!.hash)) } @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { @@ -574,66 +572,28 @@ extension UITapGestureRecognizer { // MARK: - Accessibility extension Label { - override open func accessibilityActivate() -> Bool { -// let point = accessibilityActivationPoint - return true - } - - open override func accessibilityElementDidBecomeFocused() { + func customAccessibilityAction(range: NSRange) -> UIAccessibilityCustomAction? { - } - - open override func accessibilityElementCount() -> Int { - return accessibilityElements?.count ?? 0 - } - - open override func accessibilityElement(at index: Int) -> Any? { - return accessibilityElements?[index] - } - - open override func index(ofAccessibilityElement element: Any) -> Int { - return 0//accessibilityElements?.firstIndex(of: element as! UIAccessibilityElement)! - } - - /* - var accessibleClauses: [UIAccessibilityElement] { - - var elements = [UIAccessibilityElement]() - for clause in clauses { - let element = UIAccessibilityElement(accessibilityContainer: self) -// let rect = CGRect(x: 10, y: 10, width: 100, height: 30) - element.accessibilityFrame = convert(self.frame, to: nil) -// element.accessibilityFrame = convert(conceptualSize(range: clause.range)!, to: nil) -// element.accessibilityFrameInContainerSpace = rect - element.accessibilityLabel = "Testing" - element.accessibilityTraits = .button - element.isAccessibilityElement = true - elements.append(element) + guard let text = text else { return nil } + + if accessibilityHint == nil { + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "plan_selector_int_swipe_action_hint") } - accessibilityElements = elements - return elements + + let actionText = NSString(string: text).substring(with: range) + let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:))) + accessibilityCustomActions?.append(accessibleAction) + + return accessibleAction } - */ - func boundingRect(forCharacterRange range: NSRange) -> CGRect? { + @objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) { - guard let attributedText = attributedText else { return nil } - - let textStorage = NSTextStorage(attributedString: attributedText) - let layoutManager = NSLayoutManager() - - textStorage.addLayoutManager(layoutManager) - - let textContainer = NSTextContainer(size: bounds.size) - textContainer.lineFragmentPadding = 0.0 - - layoutManager.addTextContainer(textContainer) - - var glyphRange = NSRange() - - // Convert the range for glyphs. - layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange) - - return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) + for clause in clauses { + if action.hash == clause.hash { + clause.performAction() + return + } + } } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 5cc6d50b..c8a83862 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -64,7 +64,11 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt if newActionBlock == nil { label?.clearActionableClauses() } 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, text: actionText ?? "", hash: accessibleAction.hash)] + label.accessibilityCustomActions = [accessibleAction] } } } diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index 96088cab..bbf3f6d8 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -8,6 +8,7 @@ //// Accessibility "AccCloseButton" = "Close"; +"plan_selector_int_swipe_action_hint" = "swipe up or down to select action, then double tap to select."; // Tab "AccTab" = ", tab"; "AccTabHint" = "Double tap to select.";