Skip to content
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

Added attributed versions of accessibilityLabel, accessibilityHint, accessibilityValue #554

Merged
merged 9 commits into from
Sep 18, 2017

Conversation

fruitcoder
Copy link
Contributor

First version of how we might add attributed string functionality to accessibility labels, hints and values. The properties are only available from iOS 11 and tv 11 and up so I added API_AVAILABLE to the properties in ASDisplayNode.

@ghost
Copy link

ghost commented Sep 7, 2017

🚫 CI failed with log

Copy link
Member

@nguyenhuy nguyenhuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Overall, LGTM. But let's sort out a few questions before merging.

@@ -716,8 +716,11 @@ extern NSInteger const ASDefaultDrawingPriority;
// Accessibility support
@property (nonatomic, assign) BOOL isAccessibilityElement;
@property (nonatomic, copy, nullable) NSString *accessibilityLabel;
@property (nullable, nonatomic, copy) NSAttributedString *accessibilityAttributedLabel API_AVAILABLE(ios(11.0),tvos(11.0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To follow the existing convention, let's put nullable last. So (nonatomic, copy, nullable)

NSArray *attributedLabels = [labeledNodes valueForKey:@"accessibilityAttributedLabel"];
NSMutableAttributedString *attributedLabel = [NSMutableAttributedString new];
[attributedLabels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx != 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention is to use curly brackets regardless of the number of statements followed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I thought I followed convention because I saw this in _PendingState.mm a lot for single statements (L375, L377), but I'll change my code to use curly braces.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we need to have it written down in CONTRIBUTING.md. Others may disagree, but I personally follow this convention (as much as possible).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't change it for applyToView:withSpecialPropertiesHandling: though since every flag check seems to skip the curly braces

@@ -586,9 +595,26 @@ - (NSString *)accessibilityLabel

- (void)setAccessibilityLabel:(NSString *)newAccessibilityLabel
{
_flags.setAccessibilityLabel = YES;
if (accessibilityLabel != newAccessibilityLabel) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, are we comparing strings here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I see you follow this implementation throughout your changes. I should have asked better questions, sorry.

Is there any reason to compare memory addresses here? Is the original implementation correct/acceptable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBO I would compare those Strings with isEqualToString:, since if they are equal (by means of isEqualToString), we don't have to do anything.

Copy link
Member

@nguyenhuy nguyenhuy Sep 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I'd do it the right way and not worry about the perf implication of isEqualToString, compared to ==, especially because those labels aren't changed often I believe. Would you mind taking this opportunity to do an audit in this file and switch to the formally correct compare methods?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing code makes some sense, since a common use case for accessibility is to use constants for them.

The pointer compare made more sense when (1) CPUs were way slower and (2) this was an in-house framework.

I agree we should change to use isEqualToString:.

Also just below here, we don't need to explicitly copy the newAccessibilityLabel before passing it to initWithString:. That initializer copies the contents ("initialized with the characters of aString").

{
_bridge_prologue_write;
{ _setAccessibilityToViewAndProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel); }
{ _setAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityAttributedLabel.string, accessibilityLabel, accessibilityAttributedLabel.string); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit strange. Are attributed labels supposed to overwrite non-attributed ones? Since these attributed labels are still in beta, I guess we don't know yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in fact the behaviour I'm observing. It's like UILabel's attributedText and text attributes, which override themselves (confirmed in Playground with XC9 GM).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool by me then :)

@nguyenhuy
Copy link
Member

Also, please update CHANGELOG and make sure CI builds. Thanks!

@ghost
Copy link

ghost commented Sep 15, 2017

🚫 CI failed with log

@property (nonatomic, copy, nullable) NSString *accessibilityHint;
@property (nullable, nonatomic, copy) NSAttributedString *accessibilityAttributedHint API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, copy, nullable) NSString *accessibilityValue;
@property (nullable, nonatomic, copy) NSAttributedString *accessibilityAttributedValue API_AVAILABLE(ios(11.0),tvos(11.0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the (nonatomic, copy, nullable) convention for changes in this file.

@fruitcoder
Copy link
Contributor Author

The CI Build fails can all be categorized in 2 errors in _ASPendingState.mm:
if (@available(iOS 11.0, *)) unexpected '@' in program -> affects all @availibility checks
property 'accessibilityAttributedHint' not found on object of type 'UIView *' -> affects all accesses on the new attributed versions of accessibility label/hint/value.

I don't know how to resolve those issues. Which Xcode are you using on your CI server?

@ghost
Copy link

ghost commented Sep 15, 2017

🚫 CI failed with log

@nguyenhuy
Copy link
Member

@fruitcoder We're running Xcode 8. @garrettmoon and I agreed that we shouldn't upgrade to Xcode 9 until it's out of GM. And even so, using @available in the framework will force people to use Xcode 9. So this PR either needs to wait for a while, or switch to AS_AT_LEAST_IOS11 provided in ASAvailability.h for the time being. There will come a time when we migrate to the new stuff, just not very soon I'm afraid.

@fruitcoder
Copy link
Contributor Author

Totally understand, I‘ll update my code to run on XCode 8 on Monday

@nguyenhuy
Copy link
Member

Great. Thank you!

@fruitcoder
Copy link
Contributor Author

I'm not too happy about the runtime code with setValue:forKey: and valueForKey: but it's the shortest I could come up with. I added two macros which use the runtime accessors/setters for the attributed versions, but you could still use them without checking the API Version and using bad keys. Any alternative ideas?

Copy link
Member

@nguyenhuy nguyenhuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 last iteration and this PR will be good to land!

@@ -901,6 +911,28 @@ - (void)setAccessibilityLabel:(NSString *)accessibilityLabel
{
_bridge_prologue_write;
_setAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityLabel, accessibilityLabel, accessibilityLabel);
if (AS_AT_LEAST_IOS11) {
_accessibilityAttributedLabel = [[NSAttributedString alloc] initWithString:accessibilityLabel];
BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use _setAttributedAccessibilityToViewAndProperty here?

{
_bridge_prologue_write;
{ _setAttributedAccessibilityToViewAndProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel, @"accessibilityAttributedLabel", accessibilityAttributedLabel); }
{ _setAttributedAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityAttributedLabel.string, @"accessibilityLabel", accessibilityAttributedLabel.string); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use _setAccessibilityToViewAndProperty for _accessibilityLabel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I wonder if one of the tests should have caught that. But you're definitely right!

@@ -913,6 +945,22 @@ - (void)setAccessibilityHint:(NSString *)accessibilityHint
{
_bridge_prologue_write;
_setAccessibilityToViewAndProperty(_accessibilityHint, accessibilityHint, accessibilityHint, accessibilityHint);
if (AS_AT_LEAST_IOS11) {
_setAttributedAccessibilityToViewAndProperty(_accessibilityAttributedHint, [[NSAttributedString alloc] initWithString:accessibilityHint], @"accessibilityAttributedHint", [[NSAttributedString alloc] initWithString:accessibilityHint]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: For readability and a tiny perf win, cache [[NSAttributedString alloc] initWithString:accessibilityHint] instead of allocating 2 identical objects.

{
_bridge_prologue_write;
{ _setAttributedAccessibilityToViewAndProperty(_accessibilityAttributedHint, accessibilityAttributedHint, @"accessibilityAttributedHint", accessibilityAttributedHint); }
{ _setAttributedAccessibilityToViewAndProperty(_accessibilityHint, accessibilityAttributedHint.string, @"accessibilityHint", accessibilityAttributedHint.string); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, use _setAccessibilityToViewAndProperty for _accessibilityHint.

@@ -925,6 +973,22 @@ - (void)setAccessibilityValue:(NSString *)accessibilityValue
{
_bridge_prologue_write;
_setAccessibilityToViewAndProperty(_accessibilityValue, accessibilityValue, accessibilityValue, accessibilityValue);
if (AS_AT_LEAST_IOS11) {
_setAttributedAccessibilityToViewAndProperty(_accessibilityAttributedValue, [[NSAttributedString alloc] initWithString:accessibilityValue], @"accessibilityAttributedValue", [[NSAttributedString alloc] initWithString:accessibilityValue]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Same here, cache the attributed string.

{
_bridge_prologue_write;
{ _setAttributedAccessibilityToViewAndProperty(_accessibilityAttributedValue, accessibilityAttributedValue, @"accessibilityAttributedValue", accessibilityAttributedValue); }
{ _setAttributedAccessibilityToViewAndProperty(_accessibilityValue, accessibilityAttributedValue.string, @"accessibilityValue", accessibilityAttributedValue.string); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use _setAccessibilityToViewAndProperty.

@nguyenhuy
Copy link
Member

@fruitcoder I think it's a good approach for the time being. These new macros are private so I wouldn't worry much about them being used without version checks.

…yToViewAndProperty only for attributed properties.
@nguyenhuy
Copy link
Member

Landing time 🛬 Thanks, @fruitcoder!

nguyenhuy added a commit to nguyenhuy/Texture that referenced this pull request Sep 27, 2017
nguyenhuy added a commit that referenced this pull request Sep 27, 2017
…allocating attributed strings for them #trivial (#581)

* Make sure accessibility strings are not nil before allocating attributed strings for them

- Fix crashes caused by #554

* Update tests
bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
…ccessibilityValue (TextureGroup#554)

* Added attributed versions of accessibilityLabel, accessibilityHint and accessibilityValue

* Follow conventions for property types

* Use curly braces

* Update changelog

* Follow conventions for property types in UIView+ASConvenience.h

* Add compatibility for Xcode 8

* Use isEqualToString instead of pointer comparison

* Only allocate attributed strings once. Use _setAttributedAccessibilityToViewAndProperty only for attributed properties.
bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
…allocating attributed strings for them #trivial (TextureGroup#581)

* Make sure accessibility strings are not nil before allocating attributed strings for them

- Fix crashes caused by TextureGroup#554

* Update tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants