Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
685f103
Update scripts to publish react-native-macos-init
tom-un Apr 4, 2020
8ccd26c
Merge remote-tracking branch 'ms/master'
tom-un Apr 4, 2020
45d2ee2
Merge remote-tracking branch 'ms/master'
tom-un Apr 4, 2020
1e2c329
Clean up merge markers
tom-un Apr 4, 2020
408dae3
Merge remote-tracking branch 'ms/master'
tom-un Apr 6, 2020
bdaa8ad
Merge remote-tracking branch 'ms/master'
tom-un Apr 7, 2020
5a67ae0
Restored ios:macos RNTester parity except for InputAccessoryView.
tom-un Apr 13, 2020
e5a6df2
Revert "Restored ios:macos RNTester parity except for InputAccessoryV…
tom-un Apr 13, 2020
4bca23c
Merge remote-tracking branch 'ms/master'
tom-un Apr 16, 2020
d067629
Merge remote-tracking branch 'ms/master'
tom-un Apr 21, 2020
6ed01c8
Merge remote-tracking branch 'ms/master'
tom-un Apr 28, 2020
ae5cc57
Merge remote-tracking branch 'ms/master'
tom-un Apr 29, 2020
caf0975
Merge remote-tracking branch 'ms/master'
tom-un May 1, 2020
e3fbee9
Merge remote-tracking branch 'ms/master'
tom-un May 1, 2020
a75050c
Merge remote-tracking branch 'ms/master'
tom-un May 2, 2020
3a77e37
Merge remote-tracking branch 'ms/master'
tom-un May 2, 2020
d10ff74
Merge remote-tracking branch 'ms/master'
tom-un May 3, 2020
dd11d73
Merge remote-tracking branch 'ms/master'
tom-un May 4, 2020
ba2730b
Merge remote-tracking branch 'ms/master'
tom-un May 4, 2020
5da14a9
Remove unnecessary android builds and tar file upload.
tom-un May 5, 2020
b1a873b
Merge remote-tracking branch 'ms/master'
rnbot May 5, 2020
bca9ae9
Merge remote-tracking branch 'ms/master'
rnbot May 5, 2020
7ff5624
Merge remote-tracking branch 'ms/master'
rnbot May 5, 2020
1056600
Merge remote-tracking branch 'ms/master'
rnbot May 6, 2020
d3422e3
Merge remote-tracking branch 'ms/master'
rnbot May 6, 2020
92c66ac
Merge remote-tracking branch 'ms/master'
rnbot May 7, 2020
b295454
Merge remote-tracking branch 'ms/master'
rnbot May 8, 2020
1bdf8c7
Merge remote-tracking branch 'ms/master'
rnbot May 8, 2020
b880837
Merge remote-tracking branch 'ms/master'
rnbot May 8, 2020
3c7c7c3
Merge remote-tracking branch 'ms/master'
rnbot May 8, 2020
93c5ecb
Merge remote-tracking branch 'ms/master'
rnbot May 11, 2020
04ba713
Merge remote-tracking branch 'ms/master'
rnbot May 12, 2020
b3be546
Merge remote-tracking branch 'ms/master'
tom-un May 18, 2020
046bde3
Merge remote-tracking branch 'ms/master'
tom-un May 18, 2020
302493e
Merge remote-tracking branch 'ms/master'
tom-un May 20, 2020
4ebb937
Merge remote-tracking branch 'ms/master'
tom-un May 20, 2020
5bdb247
Merge remote-tracking branch 'ms/master'
tom-un May 22, 2020
07bea18
Merge remote-tracking branch 'ms/master'
tom-un May 26, 2020
51f76ed
Merge remote-tracking branch 'ms/master'
tom-un Jun 1, 2020
6f814b4
Merge remote-tracking branch 'ms/master'
tom-un Jun 11, 2020
e69efe4
Merge remote-tracking branch 'ms/master'
tom-un Jun 17, 2020
76449c6
Merge remote-tracking branch 'ms/master'
tom-un Jun 23, 2020
6746b18
Merge remote-tracking branch 'ms/master'
tom-un Jul 1, 2020
e822b86
Merge remote-tracking branch 'ms/master'
tom-un Jul 13, 2020
e6f9e2d
Merge remote-tracking branch 'ms/master'
tom-un Jul 14, 2020
0676b93
Merge remote-tracking branch 'ms/master'
tom-un Jul 17, 2020
ab4a6d9
Merge remote-tracking branch 'ms/master'
tom-un Jul 22, 2020
ca7617f
Merge remote-tracking branch 'ms/master' into master
tom-un Aug 20, 2020
f226a7d
Merge remote-tracking branch 'ms/master' into master
tom-un Sep 1, 2020
582cfba
Merge remote-tracking branch 'ms/master' into master
tom-un Sep 4, 2020
98a37d6
RN .62 accessiblity fixes.
tom-un Sep 17, 2020
274a1c1
Fix regression in recursive accessiblityLabel text.
tom-un Sep 17, 2020
0d9af6a
Merge remote-tracking branch 'ms/master' into tomun/rn62-ax-fixes
tom-un Sep 17, 2020
c7829d7
Changed per PR feedback.
tom-un Sep 18, 2020
b94713f
Add "disclosure" role for mac mapping to NSAccessibilityDisclosureTri…
tom-un Sep 20, 2020
4f19714
CocoaPods does not like podspec version numbers that are 0.0.0. The…
tom-un Sep 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ado/templates/react-native-macos-init.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ steps:
- task: CmdLine@2
displayName: Bump package version
inputs:
script: node scripts/bump-oss-version.js --nightly
script: node scripts/bump-oss-version.js --testing

- script: |
npm publish --registry http://localhost:4873
Expand Down
58 changes: 43 additions & 15 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -1145,22 +1145,50 @@ + (NSPropertyList)NSPropertyList:(id)json
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
+ (NSString*)accessibilityRoleFromTrait:(NSString*)trait
{
// a subset of iOS accessibilityTraits map to macOS accessiblityRoles:
if ([trait isEqualToString:@"button"]) {
return NSAccessibilityButtonRole;
} else if ([trait isEqualToString:@"text"]) {
return NSAccessibilityStaticTextRole;
} else if ([trait isEqualToString:@"link"]) {
return NSAccessibilityLinkRole;
} else if ([trait isEqualToString:@"image"]) {
return NSAccessibilityImageRole;
// a set of RN accessibilityTraits are macOS specific accessiblity roles:
} else if ([trait isEqualToString:@"group"]) {
return NSAccessibilityGroupRole;
} else if ([trait isEqualToString:@"list"]) {
return NSAccessibilityListRole;
static NSDictionary<NSString *, NSString *> *traitOrRoleToAccessibilityRole;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
traitOrRoleToAccessibilityRole = @{
// from https://reactnative.dev/docs/accessibility#accessibilityrole
@"adjustable": NSAccessibilitySliderRole,
@"alert": NSAccessibilityStaticTextRole, // no exact match on macOS
@"button": NSAccessibilityButtonRole, // also a legacy iOS accessibilityTraits
@"checkbox": NSAccessibilityCheckBoxRole,
@"combobox": NSAccessibilityComboBoxRole,
@"header": NSAccessibilityStaticTextRole, // no exact match on macOS
@"image": NSAccessibilityImageRole, // also a legacy iOS accessibilityTraits
@"imagebutton": NSAccessibilityButtonRole, // no exact match on macOS
@"keyboardkey": NSAccessibilityButtonRole, // no exact match on macOS
@"link": NSAccessibilityLinkRole, // also a legacy iOS accessibilityTraits
@"menu": NSAccessibilityMenuRole,
@"menubar": NSAccessibilityMenuBarRole,
@"menuitem": NSAccessibilityMenuBarItemRole,
@"none": NSAccessibilityUnknownRole,
@"progressbar": NSAccessibilityProgressIndicatorRole,
@"radio": NSAccessibilityRadioButtonRole,
@"radiogroup": NSAccessibilityRadioGroupRole,
@"scrollbar": NSAccessibilityScrollBarRole,
@"search": NSAccessibilityTextFieldRole, // no exact match on macOS
@"spinbutton": NSAccessibilityIncrementorRole,
@"summary": NSAccessibilityStaticTextRole, // no exact match on macOS
@"switch": NSAccessibilityCheckBoxRole, // no exact match on macOS
@"tab": NSAccessibilityButtonRole, // no exact match on macOS
@"tablist": NSAccessibilityTabGroupRole,
@"text": NSAccessibilityStaticTextRole, // also a legacy iOS accessibilityTraits
@"timer": NSAccessibilityStaticTextRole, // no exact match on macOS
@"toolbar": NSAccessibilityToolbarRole,
// Roles/traits that are macOS specific and are used by some of the core components (Lists):
@"disclosure": NSAccessibilityDisclosureTriangleRole,
@"group": NSAccessibilityGroupRole,
@"list": NSAccessibilityListRole,
};
});

NSString *role = [traitOrRoleToAccessibilityRole valueForKey:trait];
if (role == nil) {
role = NSAccessibilityUnknownRole;
}
return NSAccessibilityUnknownRole;
return role;
}

+ (NSString *)accessibilityRoleFromTraits:(id)json
Expand Down
3 changes: 3 additions & 0 deletions React/Base/RCTUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ void UIGraphicsEndImageContext(void);
// semantically equivalent types
//

// UIAccessibility.h/NSAccessibility.h
@compatibility_alias UIAccessibilityCustomAction NSAccessibilityCustomAction;
Copy link
Member

Choose a reason for hiding this comment

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

@tom-un When we move RCTUIKit upstream at some point, do you think it makes sense to rename these symbols to be platform agnostic? E.g. in this case RCTAccessibilityCustomAction?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, they should all be normalized to a consistent convention if/when we contribute upstream. I'm partial to things like RCTUIView -> UIView/NSView, because there are already several UIKit subclass upstream that use just the RCT prefix, like RCTView.


// UIColor.h/NSColor.h
#define RCTUIColor NSColor

Expand Down
203 changes: 188 additions & 15 deletions React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#import "RCTUtils.h"
#import "UIView+React.h"
#import "RCTI18nUtil.h"
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
#import "RCTTextView.h"
#endif // ]TODO(macOS ISS#2323203)

#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
UIAccessibilityTraits const SwitchAccessibilityTrait = 0x20000000000001;
Expand Down Expand Up @@ -92,7 +95,18 @@ - (RCTPlatformView *)react_findClipView // TODO(macOS ISS#2323203)
{
NSMutableString *str = [NSMutableString stringWithString:@""];
for (RCTUIView *subview in view.subviews) { // TODO(macOS ISS#3536887)
#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
NSString *label = subview.accessibilityLabel;
#else // [TODO(macOS ISS#2323203)
NSString *label;
if ([subview isKindOfClass:[RCTTextView class]]) {
// on macOS VoiceOver a text element will always have its accessibilityValue read, but will only read it's accessibilityLabel if it's value is set.
// the macOS RCTTextView accessibilityValue will return its accessibilityLabel if set otherwise return its text.
label = subview.accessibilityValue;
} else {
label = subview.accessibilityLabel;
}
#endif // ]TODO(macOS ISS#2323203)
if (!label) {
label = RCTRecursiveAccessibilityLabel(subview);
}
Expand Down Expand Up @@ -188,10 +202,14 @@ - (NSString *)accessibilityLabel
if (label) {
return label;
}
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
// calling super.accessibilityLabel above on macOS causes the return value of this accessor to be ignored by VoiceOver.
// Calling the super's setAccessibilityLabel with nil ensures that the return value of this accessor is used by VoiceOver.
Copy link
Member

Choose a reason for hiding this comment

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

Does the nil setting break things on iOS or can we remove the platform checks and upstream it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It probably is fine to nil it on iOS too -- but AX is such a touchy area with lots of undocumented behavior in VoiceOver that I'd rather not risk any deltas to the iOS path.

Copy link
Member

Choose a reason for hiding this comment

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

I’m going to unhide this one, just in case I ever want to come back to this while unforking.

[super setAccessibilityLabel:nil];
#endif // ]TODO(macOS ISS#2323203)
return RCTRecursiveAccessibilityLabel(self);
}

#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
- (NSArray <UIAccessibilityCustomAction *> *)accessibilityCustomActions
{
if (!self.accessibilityActions.count) {
Expand Down Expand Up @@ -233,11 +251,10 @@ - (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)acti
}
return YES;
}
#endif // TODO(macOS ISS#2323203)

#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
- (NSString *)accessibilityValue
{
#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
if ((self.accessibilityTraits & SwitchAccessibilityTrait) == SwitchAccessibilityTrait) {
for (NSString *state in self.accessibilityState) {
id val = self.accessibilityState[state];
Expand All @@ -248,17 +265,7 @@ - (NSString *)accessibilityValue
return [val boolValue] ? @"1" : @"0";
}
}
for (NSString *state in self.accessibilityState) {
id val = self.accessibilityState[state];
if (!val) {
continue;
}
if ([state isEqualToString:@"checked"] && [val isKindOfClass:[NSNumber class]]) {
return [val boolValue] ? @"1" : @"0";
}
}
}
#endif // TODO(macOS ISS#2323203)
NSMutableArray *valueComponents = [NSMutableArray new];
static NSDictionary<NSString *, NSString *> *roleDescriptions = nil;
static dispatch_once_t onceToken1;
Expand Down Expand Up @@ -294,7 +301,7 @@ - (NSString *)accessibilityValue
@"mixed": @"mixed",
};
});
NSString *roleDescription = self.accessibilityRole ? roleDescriptions[self.accessibilityRole]: nil;
NSString *roleDescription = self.accessibilityRoleInternal ? roleDescriptions[self.accessibilityRoleInternal]: nil; // TODO(OSS Candidate ISS#2710739): renamed prop so it doesn't conflict with -[NSAccessibility accessibilityRole].
if (roleDescription) {
[valueComponents addObject:roleDescription];
}
Expand Down Expand Up @@ -341,6 +348,147 @@ - (NSString *)accessibilityValue
}
return nil;
}
#else // [TODO(macOS ISS#2323203)
- (id)accessibilityValue {
id accessibilityValue = nil;
NSAccessibilityRole role = [self accessibilityRole];
if (role == NSAccessibilityCheckBoxRole ||
role == NSAccessibilityRadioButtonRole ||
role == NSAccessibilityDisclosureTriangleRole) {
for (NSString *state in [self accessibilityState]) {
id val = [self accessibilityState][state];
if (val != nil) {

Choose a reason for hiding this comment

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

This and the if statement below can be combined into one

if ([state isEqualToString:@"checked"]) {
if ([val isKindOfClass:[NSNumber class]]) {
accessibilityValue = @([val boolValue]);
} else if ([val isKindOfClass:[NSString class]] && [val isEqualToString:@"mixed"]) {
accessibilityValue = @(2); // undocumented by Apple: @(2) is the accessibilityValue an NSButton has when its state is NSMixedState (-1) and causes VoiceOver to announced "mixed".
}
}
}
}
} else if ([self accessibilityRole] == NSAccessibilityStaticTextRole) {
// On macOS if the role is static text, VoiceOver will only read the text returned by accessibilityValue.
// So return accessibilityLabel which has the logic to return either either the ivar or a computed value of all the children's text.
// If the accessibilityValueInternal "text" is present, it will override this value below.
accessibilityValue = [self accessibilityLabel];
}

// handle accessibilityValue

id accessibilityValueInternal = [self accessibilityValueInternal];
if (accessibilityValueInternal != nil) {
id now = accessibilityValueInternal[@"now"];
id text = accessibilityValueInternal[@"text"];
if (text != nil && [text isKindOfClass:[NSString class]]) {
accessibilityValue = text;
} else if (now != nil && [now isKindOfClass:[NSNumber class]]) {
accessibilityValue = now;
}
}

return accessibilityValue;
}

- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector {
BOOL isAllowed = NO;
if (selector == @selector(isAccessibilityEnabled)) {
if (self.accessibilityState != nil) {
id disabled = self.accessibilityState[@"disabled"];
if ([disabled isKindOfClass:[NSNumber class]]) {
isAllowed = YES;
}
}
} else if (selector == @selector(isAccessibilitySelected)) {
if (self.accessibilityState != nil) {
id selected = self.accessibilityState[@"selected"];
if ([selected isKindOfClass:[NSNumber class]]) {
isAllowed = YES;
}
}
} else if (selector == @selector(isAccessibilityExpanded)) {
if (self.accessibilityState != nil) {
id expanded = self.accessibilityState[@"expanded"];
if ([expanded isKindOfClass:[NSNumber class]]) {
isAllowed = YES;
}
}
} else if (selector == @selector(accessibilityPerformPress)) {
if (_onAccessibilityTap != nil ||
(_onAccessibilityAction != nil && accessibilityActionsNameMap[@"activate"]) ||
_onClick != nil) {
isAllowed = YES;
}
} else if (selector == @selector(accessibilityPerformIncrement)) {
if (_onAccessibilityAction != nil && accessibilityActionsNameMap[@"increment"]) {
isAllowed = YES;
}
} else if (selector == @selector(accessibilityPerformDecrement)) {
if (_onAccessibilityAction != nil && accessibilityActionsNameMap[@"decrement"]) {
isAllowed = YES;
}
} else {
isAllowed = YES;
}
return isAllowed;
}

- (BOOL)isAccessibilityEnabled {
BOOL isAccessibilityEnabled = YES;
if (self.accessibilityState != nil) {
id disabled = self.accessibilityState[@"disabled"];
if ([disabled isKindOfClass:[NSNumber class]]) {
isAccessibilityEnabled = [disabled boolValue] ? NO : YES;
}
}
return isAccessibilityEnabled;
}

- (BOOL)isAccessibilitySelected {
BOOL isAccessibilitySelected = NO;
if (self.accessibilityState != nil) {
id selected = self.accessibilityState[@"selected"];
if ([selected isKindOfClass:[NSNumber class]]) {
isAccessibilitySelected = [selected boolValue];
}
}
return isAccessibilitySelected;
}

- (BOOL)isAccessibilityExpanded {
BOOL isAccessibilityExpanded = NO;
if (self.accessibilityState != nil) {
id expanded = self.accessibilityState[@"expanded"];
if ([expanded isKindOfClass:[NSNumber class]]) {
isAccessibilityExpanded = [expanded boolValue];
}
}
return isAccessibilityExpanded;
}

- (id)accessibilityMinValue {
id accessibilityMinValue = nil;
if (self.accessibilityValueInternal != nil) {
id min = self.accessibilityValueInternal[@"min"];
if ([min isKindOfClass:[NSNumber class]]) {
accessibilityMinValue = min;
}
}
return accessibilityMinValue;
}
Copy link
Member

Choose a reason for hiding this comment

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

Just for my understanding, what else can these values be that they can be ignored?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The accessibilityMinValue and accessibilityMaxValue are used for sliders and steppers roles. When the appropriate role, like slider, is set, VoiceOver will automatically compute percentages and announce them. i.e. if accessibilityMinValue=0, accessibilityMaxValue=100, and accessibilityValue = 50, voiceover will say "50%"

Copy link
Member

Choose a reason for hiding this comment

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

Unhiding for posterity.


- (id)accessibilityMaxValue {
id accessibilityMaxValue = nil;
if (self.accessibilityValueInternal != nil) {
id max = self.accessibilityValueInternal[@"max"];
if ([max isKindOfClass:[NSNumber class]]) {
accessibilityMaxValue = max;
}
}
return accessibilityMaxValue;
}

#endif // ]TODO(macOS ISS#2323203)

- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
{
Expand Down Expand Up @@ -445,17 +593,28 @@ - (BOOL)performAccessibilityAction:(NSString *) name
return NO;
}

#if !TARGET_OS_OSX // ]TODO(macOS ISS#2323203)
#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
- (BOOL)accessibilityActivate
#else // [TODO(macOS ISS#2323203)
- (BOOL)accessibilityPerformPress
#endif // ]TODO(macOS ISS#2323203)
{
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
if ([self isAccessibilityEnabled] == NO) {

Choose a reason for hiding this comment

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

This method is plagued by early returns. Would be awesome if we could fix those up instead of adding one more to it :)

return NO;
}
#endif // ]TODO(macOS ISS#2323203)
if ([self performAccessibilityAction:@"activate"]) {
return YES;
} else if (_onAccessibilityTap) {
_onAccessibilityTap(nil);
return YES;
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
} else if (_onClick != nil) {
// macOS is not simulating a click if there is no onAccessibilityAction like it does on iOS, so we simulate it here.
_onClick(nil);
return YES;
#endif // ]TODO(macOS ISS#2323203)
Copy link
Member

Choose a reason for hiding this comment

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

Do you imagine that at some point we might be able to upstream things like this and just have it not do anything on platforms that don’t have the concept of clicks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a weird one. On iOS returning NO from accessibilityActivate can make the system simulate a touch on the view instead which ends up indirectly causing an onPress on a touchable get called. On macOS this doesn't happen. There are a number of cases where a View or Touchable does not have an onAccessiblityAction handler but does have on onPress and expects a VoiceOver double tap to call it. It's an important case to support and this was the most obvious way to simulate a click -- we do it in other places such as performKeyEquivalent:.

Copy link
Member

Choose a reason for hiding this comment

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

Good to know, thanks for the update! (I’ll actually unresolve this thread, as it’s just good general info for posterity.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

BTW, I think I found how mac VoiceOver can simulate a click on VO activate: if the accessibilityActivationPoint prop is set https://developer.apple.com/documentation/appkit/nsaccessibility/1535149-accessibilityactivationpoint?language=objc. Might be worth further investigation in the future, but don't want to add risk to this change by introducing another property.

} else {
return NO;
}
Expand Down Expand Up @@ -487,15 +646,29 @@ - (BOOL)accessibilityPerformEscape
}
}

#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
- (void)accessibilityIncrement
{
[self performAccessibilityAction:@"increment"];
}
#else // [TODO(macOS ISS#2323203)
- (BOOL)accessibilityPerformIncrement
{
return [self performAccessibilityAction:@"increment"];
}
#endif // ]TODO(macOS ISS#2323203)

#if !TARGET_OS_OSX // TODO(macOS ISS#2323203)
- (void)accessibilityDecrement
{
[self performAccessibilityAction:@"decrement"];
}
#else // [TODO(macOS ISS#2323203)
- (BOOL)accessibilityPerformDecrement
{
return [self performAccessibilityAction:@"decrement"];
}
#endif // ]TODO(macOS ISS#2323203)

- (NSString *)description
{
Expand Down
Loading