-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AttributedLabel Accessibility Links #537
Conversation
@@ -389,7 +364,7 @@ extension AttributedLabel { | |||
} | |||
|
|||
assert( | |||
alignments.count == 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was causing a crash in the demo app.
Someone went through the trouble of writing the nil case below, it'd be a shame never to use it.
e5ccc90
to
91e208b
Compare
91e208b
to
173630b
Compare
private var accessibilityLinkIndex = -1 | ||
|
||
/// These elements need to be retained by the view, and cannot be created inside the | ||
/// `accessibilityCustomRotors` getter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This concern about the elements being retained is still valid. We cannot pass back unretained elements created within the itemSearchBlock
.
The trick here is the elements are now retained in an array which is itself captured by the itemSearchBlock
.
Once VoiceOver releases the rotor these elements all get deallocated and then will be recreated the next time VoiceOver requests the rotor.
ece74e1
to
8ff1979
Compare
8ff1979
to
881411f
Compare
extension BidirectionalCollection where Element: Equatable, Element: NSObjectProtocol { | ||
private func itemSearch(_ predicate: UIAccessibilityCustomRotorSearchPredicate) -> UIAccessibilityCustomRotorItemResult? { | ||
guard let first else { return nil } | ||
guard let currentItem = predicate.currentItem.targetElement as? Element, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking outloud, im surprised to see both the generic constraint and this dynamic check; are both required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generic constraint is on the array contents, but the dynamic check is being run on the item that we've been passed in via the predicate, which is only guaranteed to be an NSObjectProtocol not necessarily Equatable.
We could get away with just checking for Equatable, but I thought it would be nice to ensure that all the types match by using Element instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
@@ -0,0 +1,21 @@ | |||
import UIKit | |||
|
|||
extension BidirectionalCollection where Element: Equatable, Element: NSObjectProtocol { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think you can also combine the constraints
extension BidirectionalCollection where Element: Equatable, Element: NSObjectProtocol { | |
extension BidirectionalCollection where Element: Equatable & NSObjectProtocol { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, of course!
I initially wrote Element: Equatable, NSObjectProtocol
and this was Xcode's suggested fix.
} | ||
|
||
/// Returns a UIAccessibilityCustomRotor with the provided system type which cycles through the contained elements. | ||
public func accessibilityRotor(systemType type: UIAccessibilityCustomRotor.SystemRotorType) -> UIAccessibilityCustomRotor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Put the public func at the top of the file, and the private extension after?
This PR migrates
AttributedLabel
's link rotor from a stateful to functional pattern, eliminating the need for two variables and preventing voiceover from getting out of sync.I also fixed a bug in
links(at location: CGPoint)
that could cause a crash if theAttributedString
lacked a specifiedNSTextAlignment
.