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] = [] {
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
}
}
}
}

View File

@ -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]
}
}
}

View File

@ -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.";