update label for accessibility elements
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
53c2a63dbb
commit
304416afdb
@ -95,7 +95,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
.sink { [weak self] notification in
|
.sink { [weak self] notification in
|
||||||
self?.setNeedsUpdate()
|
self?.setNeedsUpdate()
|
||||||
}.store(in: &subscribers)
|
}.store(in: &subscribers)
|
||||||
|
|
||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
numberOfLines = 0
|
numberOfLines = 0
|
||||||
lineBreakMode = .byWordWrapping
|
lineBreakMode = .byWordWrapping
|
||||||
@ -106,7 +106,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {}
|
open func setup() {}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -156,6 +156,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
|
|
||||||
//force a drawText
|
//force a drawText
|
||||||
setNeedsDisplay()
|
setNeedsDisplay()
|
||||||
|
|
||||||
|
setNeedsLayout()
|
||||||
|
layoutIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +177,25 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
|
|
||||||
//add attribute on the string
|
//add attribute on the string
|
||||||
attribute.setAttribute(on: mutableAttributedString)
|
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
|
//see if the attribute is Actionable
|
||||||
if let actionable = attribute as? any ActionLabelAttributeModel{
|
if let actionable = attribute as? any ActionLabelAttributeModel{
|
||||||
//create a accessibleAction
|
//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))
|
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
|
// MARK: - Actionable
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -218,10 +250,10 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
|
|
||||||
private var actions: [LabelAction] = [] {
|
private var actions: [LabelAction] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
isUserInteractionEnabled = !actions.isEmpty
|
|
||||||
accessibilityTraits = !actions.isEmpty ? .link : .staticText
|
|
||||||
if actions.isEmpty {
|
if actions.isEmpty {
|
||||||
tapGesture = nil
|
tapGesture = nil
|
||||||
|
isUserInteractionEnabled = true
|
||||||
|
accessibilityTraits = .staticText
|
||||||
} else {
|
} else {
|
||||||
//add tap gesture
|
//add tap gesture
|
||||||
if tapGesture == nil {
|
if tapGesture == nil {
|
||||||
@ -253,18 +285,42 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private func customAccessibilityAction(text: String?, range: NSRange, accessibleText: String? = nil) -> UIAccessibilityCustomAction? {
|
private func customAccessibilityAction(text: String?, range: NSRange, accessibleText: String? = nil) -> UIAccessibilityCustomAction? {
|
||||||
|
|
||||||
guard let text = text else { return nil }
|
guard let text = text, let attributedText else { return nil }
|
||||||
//TODO: accessibilityHint for Label
|
|
||||||
// if accessibilityHint == nil {
|
|
||||||
// accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
|
|
||||||
// }
|
|
||||||
|
|
||||||
let actionText = accessibleText ?? NSString(string:text).substring(with: range)
|
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(_:)))
|
let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:)))
|
||||||
accessibilityCustomActions?.append(accessibleAction)
|
accessibilityCustomActions?.append(accessibleAction)
|
||||||
return accessibleAction
|
return accessibleAction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
|
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
|
||||||
|
|
||||||
for actionable in actions {
|
for actionable in actions {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user