Accessibility update for Label and LabelWithInternalButton.

This commit is contained in:
Kevin G Christiano 2019-07-11 11:43:33 -04:00
parent 244e3c0df3
commit 6775467606
3 changed files with 45 additions and 80 deletions

View File

@ -40,22 +40,15 @@ public typealias ActionBlock = () -> Void
//------------------------------------------------------ //------------------------------------------------------
public var clauses: [ActionableClause] = [] { public var clauses: [ActionableClause] = [] {
didSet { didSet { isUserInteractionEnabled = !clauses.isEmpty }
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)
}
} }
/// Used for tappable links in the text. /// Used for tappable links in the text.
public struct ActionableClause { public struct ActionableClause {
var range: NSRange? var range: NSRange?
var actionBlock: ActionBlock? var actionBlock: ActionBlock?
var text: String?
var hash: Int = 0
func performAction() { func performAction() {
actionBlock?() actionBlock?()
@ -72,8 +65,7 @@ public typealias ActionBlock = () -> Void
numberOfLines = 0 numberOfLines = 0
lineBreakMode = .byWordWrapping lineBreakMode = .byWordWrapping
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
isAccessibilityElement = false accessibilityCustomActions = []
accessibilityElements = []
} }
@objc public init() { @objc public init() {
@ -267,10 +259,14 @@ public typealias ActionBlock = () -> Void
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 accessibleAction = actionLabel.customAccessibilityAction(range: range)
actionBlock: actionLabel.createActionBlockFrom(actionMap: json, let actionBlock = actionLabel.createActionBlockFrom(actionMap: json, additionalData: additionalData, delegateObject: delegate)
additionalData: additionalData, let actionableClause = ActionableClause(range: range,
delegateObject: delegate))) actionBlock: actionBlock,
text: accessibleAction?.name ?? "",
hash: accessibleAction?.hash ?? -1)
actionLabel.clauses.append(actionableClause)
default: default:
continue continue
} }
@ -416,7 +412,7 @@ extension Label {
originalAttributedString = nil originalAttributedString = nil
hasAttachmentImage = false hasAttachmentImage = false
styleB2(true) styleB2(true)
accessibilityElements = [] accessibilityCustomActions = []
} }
@objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@ -457,7 +453,7 @@ extension Label {
} }
self.attributedText = mutableAttributedString self.attributedText = mutableAttributedString
// accessibleClauses = [] accessibilityElements = []
clauses = [] clauses = []
} }
@ -495,7 +491,9 @@ 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)) 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?) { @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 actionText = NSString(string: text!).substring(with: range)
actionBlock: createActionBlockFrom(actionMap: actionMap, let accessibleAction = customAccessibilityAction(range: range)
additionalData: additionalData, let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
delegateObject: delegateObject))) clauses.append(ActionableClause(range: range, actionBlock: actionBlock, text: actionText, hash: accessibleAction!.hash))
} }
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
@ -574,66 +572,28 @@ extension UITapGestureRecognizer {
// MARK: - Accessibility // MARK: - Accessibility
extension Label { extension Label {
override open func accessibilityActivate() -> Bool { func customAccessibilityAction(range: NSRange) -> UIAccessibilityCustomAction? {
// let point = accessibilityActivationPoint
return true
}
open override func accessibilityElementDidBecomeFocused() { guard let text = text else { return nil }
} if accessibilityHint == nil {
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "plan_selector_int_swipe_action_hint")
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)
} }
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 } for clause in clauses {
if action.hash == clause.hash {
let textStorage = NSTextStorage(attributedString: attributedText) clause.performAction()
let layoutManager = NSLayoutManager() return
}
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)
} }
} }

View File

@ -64,7 +64,11 @@ 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, text: actionText ?? "", hash: accessibleAction.hash)]
label.accessibilityCustomActions = [accessibleAction]
} }
} }
} }

View File

@ -8,6 +8,7 @@
//// Accessibility //// Accessibility
"AccCloseButton" = "Close"; "AccCloseButton" = "Close";
"plan_selector_int_swipe_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.";