update label for accessibility elements

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2023-08-26 10:45:15 -05:00
parent 53c2a63dbb
commit 304416afdb

View File

@ -95,7 +95,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
.sink { [weak self] notification in
self?.setNeedsUpdate()
}.store(in: &subscribers)
backgroundColor = .clear
numberOfLines = 0
lineBreakMode = .byWordWrapping
@ -106,7 +106,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
setNeedsUpdate()
}
}
open func setup() {}
/// Resets to default settings.
@ -156,6 +156,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//force a drawText
setNeedsDisplay()
setNeedsLayout()
layoutIfNeeded()
}
}
}
@ -174,7 +177,25 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//add attribute on the string
attribute.setAttribute(on: mutableAttributedString)
}
}
}
open override func layoutSubviews() {
super.layoutSubviews()
applyActions()
}
private func applyActions() {
actions = []
guard let attributedText else { return }
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
if let attributes = attributes {
//loop through the models attributes
for attribute in attributes {
//see if the attribute is Actionable
if let actionable = attribute as? any ActionLabelAttributeModel{
//create a accessibleAction
@ -184,9 +205,20 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
actions.append(LabelAction(range: actionable.range, action: actionable.action, accessibilityID: customAccessibilityAction?.hashValue ?? -1))
}
}
if let accessibilityElements, !accessibilityElements.isEmpty {
let staticText = UIAccessibilityElement(accessibilityContainer: self)
staticText.accessibilityLabel = text
staticText.accessibilityTraits = .staticText
staticText.accessibilityFrameInContainerSpace = bounds
isAccessibilityElement = false
self.accessibilityElements = accessibilityElements.compactMap{$0 as? UIAccessibilityElement}.filter { $0.accessibilityLabel != text }
self.accessibilityElements?.insert(staticText, at: 0)
}
}
}
//--------------------------------------------------
// MARK: - Actionable
//--------------------------------------------------
@ -218,10 +250,10 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
private var actions: [LabelAction] = [] {
didSet {
isUserInteractionEnabled = !actions.isEmpty
accessibilityTraits = !actions.isEmpty ? .link : .staticText
if actions.isEmpty {
tapGesture = nil
isUserInteractionEnabled = true
accessibilityTraits = .staticText
} else {
//add tap gesture
if tapGesture == nil {
@ -253,18 +285,42 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//--------------------------------------------------
private func customAccessibilityAction(text: String?, range: NSRange, accessibleText: String? = nil) -> UIAccessibilityCustomAction? {
guard let text = text else { return nil }
//TODO: accessibilityHint for Label
// if accessibilityHint == nil {
// accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
// }
guard let text = text, let attributedText else { return nil }
let actionText = accessibleText ?? NSString(string:text).substring(with: range)
// Calculate the frame of the substring
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: bounds.size)
let textStorage = NSTextStorage(attributedString: attributedText)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
var glyphRange = NSRange()
// Convert the range for the substring into a range of glyphs
layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange)
let substringBounds = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
// Create custom accessibility element
let element = UIAccessibilityElement(accessibilityContainer: self)
element.accessibilityLabel = actionText
element.accessibilityTraits = .link
element.accessibilityFrameInContainerSpace = substringBounds
//TODO: accessibilityHint for Label
// element.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
accessibilityElements = (accessibilityElements ?? []).compactMap{$0 as? UIAccessibilityElement}.filter { $0.accessibilityLabel != actionText }
accessibilityElements?.append(element)
let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:)))
accessibilityCustomActions?.append(accessibleAction)
return accessibleAction
}
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
for actionable in actions {