Skip to content

Commit

Permalink
Fallback to regular usage of UIAccessibilityElement when the accessib…
Browse files Browse the repository at this point in the history
…ility container is changed externally.

NIViewAccessibilityElement overrides UIAccessibilityElement behavior by
delaying computing its accessibility frame (which is in screen corrdinates).
In that case, the accessibility frame is computed upon request based on the
position at that moment of the actual visual component made accessible.

This holds true until the accessibility container is changed externally. For
example, when the accessibility element is in a table view hierarchy). In that
case, the initial accessibility container is changed to a
UITableViewCellAccessibilityElement and frameInContainer becomes invalid.

This commit invalidates NIViewAccessibilityElement's special treatment and
fallback to the regular behavior.
  • Loading branch information
Arcank committed Apr 23, 2015
1 parent 94e8c69 commit a206f17
Showing 1 changed file with 50 additions and 10 deletions.
60 changes: 50 additions & 10 deletions src/attributedlabel/src/NIAttributedLabel.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,25 +106,59 @@ CGSize NISizeOfAttributedStringConstrainedToSize(NSAttributedString* attributedS
* - The accessibilityContainer must be a UIView.
* - The accessibilityFrame is recomputed every time from the frameInContainer
* and the accessibilityContainer.
*
* These differences cease to be as soon as the initial accessibility container
* is changed externally, which is internally tracked by isFrameInContainerValid.
*/
@interface NIViewAccessibilityElement : UIAccessibilityElement

// Designated initializer.
- (instancetype)initWithAccessibilityContainer:(id)container frameInContainer:(CGRect)frameInContainer;

// This frame is in the accessibilityContainer coordinates.
@property (nonatomic) CGRect frameInContainer;
@property (nonatomic, readonly) CGRect frameInContainer;

@end

@interface NIViewAccessibilityElement ()

// Whether frameInContainer is valid, that is, the container is still the one
// used to compute frameInContainer.
@property (nonatomic) BOOL isFrameInContainerValid;

@end

@implementation NIViewAccessibilityElement

- (instancetype)initWithAccessibilityContainer:(id)container {
- (instancetype)initWithAccessibilityContainer:(id)container frameInContainer:(CGRect)frameInContainer {
NIDASSERT([container isKindOfClass:[UIView class]]);
return [super initWithAccessibilityContainer:container];
if ((self = [super initWithAccessibilityContainer:container])) {
_frameInContainer = frameInContainer;
_isFrameInContainerValid = YES;
}
return self;
}

- (instancetype)initWithAccessibilityContainer:(id)container {
if ((self = [self initWithAccessibilityContainer:container frameInContainer:CGRectZero])) {
self.isFrameInContainerValid = NO;
}
return self;
}

- (void)setAccessibilityContainer:(id)accessibilityContainer {
self.isFrameInContainerValid = NO;
[super setAccessibilityContainer:accessibilityContainer];
}

- (CGRect)accessibilityFrame {
UIView* view = [self accessibilityContainer];
CGRect frame = [view convertRect:self.frameInContainer toView:nil];
return [view.window convertRect:frame toWindow:nil];
if (self.isFrameInContainerValid) {
UIView* view = [self accessibilityContainer];
NIDASSERT([view isKindOfClass:[UIView class]]);
CGRect frame = [view convertRect:self.frameInContainer toView:nil];
return [view.window convertRect:frame toWindow:nil];
}
return [super accessibilityFrame];
}

@end
Expand Down Expand Up @@ -1435,19 +1469,25 @@ - (NSArray *)accessibleElements {

NSString* label = [self.mutableAttributedString.string substringWithRange:result.range];
for (NSValue* rectValue in rectsForLink) {
NIViewAccessibilityElement* element = [[NIViewAccessibilityElement alloc] initWithAccessibilityContainer:self];
NIViewAccessibilityElement* element = [[NIViewAccessibilityElement alloc] initWithAccessibilityContainer:self frameInContainer:rectValue.CGRectValue];
element.accessibilityLabel = label;
element.frameInContainer = rectValue.CGRectValue;
// Set the frame to fallback on if |element|'s accessibility container is changed externally.
CGRect rectValueInWindowCoordinates = [self convertRect:rectValue.CGRectValue toView:nil];
CGRect rectValueInScreenCoordinates = [self.window convertRect:rectValueInWindowCoordinates toWindow:nil];
element.accessibilityFrame = rectValueInScreenCoordinates;
element.accessibilityTraits = UIAccessibilityTraitLink;
[accessibleElements addObject:element];
}
}

// Add this label's text as the "bottom-most" accessibility element, i.e. the last element in the
// array. This gives link priorities.
NIViewAccessibilityElement* element = [[NIViewAccessibilityElement alloc] initWithAccessibilityContainer:self];
NIViewAccessibilityElement* element = [[NIViewAccessibilityElement alloc] initWithAccessibilityContainer:self frameInContainer:self.bounds];
element.accessibilityLabel = self.attributedText.string;
element.frameInContainer = self.bounds;
// Set the frame to fallback on if |element|'s accessibility container is changed externally.
CGRect boundsInWindowCoordinates = [self convertRect:self.bounds toView:nil];
CGRect boundsInScreenCoordinates = [self.window convertRect:boundsInWindowCoordinates toWindow:nil];
element.accessibilityFrame = boundsInScreenCoordinates;
element.accessibilityTraits = UIAccessibilityTraitNone;
[accessibleElements addObject:element];

Expand Down

0 comments on commit a206f17

Please sign in to comment.