mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Improve _ASDisplayViewAccessibility
- Add class method to create a UIAccessibilityElement from a ASDisplayNode - Add function to iterate through a ASDisplayNode tree in bfs fashion - Add assert for _accessibleElements in accessibilityElementAtIndex:
This commit is contained in:
@@ -87,6 +87,12 @@ extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node);
|
|||||||
*/
|
*/
|
||||||
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node));
|
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node));
|
||||||
|
|
||||||
|
/**
|
||||||
|
This function will walk the node hierarchy in a breadth first fashion. It does run the block on the node provided
|
||||||
|
directly to the function call.
|
||||||
|
*/
|
||||||
|
extern void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^block)(ASDisplayNode *node));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Identical to ASDisplayNodePerformBlockOnEveryNode, except it does not run the block on the
|
Identical to ASDisplayNodePerformBlockOnEveryNode, except it does not run the block on the
|
||||||
node provided directly to the function call - only on all descendants.
|
node provided directly to the function call - only on all descendants.
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#import "ASDisplayNodeInternal.h"
|
#import "ASDisplayNodeInternal.h"
|
||||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||||
|
|
||||||
|
#import <queue>
|
||||||
|
|
||||||
extern ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *window)
|
extern ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *window)
|
||||||
{
|
{
|
||||||
ASDisplayNodeCAssert(![displayNode isLayerBacked], @"displayNode must not be layer backed as it may have a nil window");
|
ASDisplayNodeCAssert(![displayNode isLayerBacked], @"displayNode must not be layer backed as it may have a nil window");
|
||||||
@@ -63,6 +65,24 @@ extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^block)(ASDisplayNode *node))
|
||||||
|
{
|
||||||
|
// Queue used to keep track of subnodes while traversing this layout in a BFS fashion.
|
||||||
|
std::queue<ASDisplayNode *> queue;
|
||||||
|
queue.push(node);
|
||||||
|
|
||||||
|
while (!queue.empty()) {
|
||||||
|
node = queue.front();
|
||||||
|
queue.pop();
|
||||||
|
|
||||||
|
block(node);
|
||||||
|
|
||||||
|
// Add all subnodes to process in next step
|
||||||
|
for (int i = 0; i < node.subnodes.count; i++)
|
||||||
|
queue.push(node.subnodes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, void(^block)(ASDisplayNode *node))
|
extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, void(^block)(ASDisplayNode *node))
|
||||||
{
|
{
|
||||||
for (ASDisplayNode *subnode in node.subnodes) {
|
for (ASDisplayNode *subnode in node.subnodes) {
|
||||||
|
|||||||
@@ -8,23 +8,27 @@
|
|||||||
|
|
||||||
#import "_ASDisplayViewAccessiblity.h"
|
#import "_ASDisplayViewAccessiblity.h"
|
||||||
#import "_ASDisplayView.h"
|
#import "_ASDisplayView.h"
|
||||||
|
#import "ASDisplayNodeExtras.h"
|
||||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||||
|
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import <queue>
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - UIAccessibilityElement
|
#pragma mark - UIAccessibilityElement
|
||||||
|
|
||||||
static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
|
||||||
|
|
||||||
@implementation UIAccessibilityElement (_ASDisplayView)
|
@implementation UIAccessibilityElement (_ASDisplayView)
|
||||||
|
|
||||||
|
+ (UIAccessibilityElement *)accessibilityElementWithContainer:(id)container node:(ASDisplayNode *)node
|
||||||
|
{
|
||||||
|
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:container];
|
||||||
|
accessibilityElement.asyncdisplaykit_node = node;
|
||||||
|
return accessibilityElement;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
|
- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
|
||||||
{
|
{
|
||||||
objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, node, OBJC_ASSOCIATION_ASSIGN); // Weak reference to avoid cycle, since the node retains the layer.
|
objc_setAssociatedObject(self, @selector(asyncdisplaykit_node), node, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
// Update UIAccessibilityElement properties from node
|
|
||||||
self.accessibilityIdentifier = node.accessibilityIdentifier;
|
self.accessibilityIdentifier = node.accessibilityIdentifier;
|
||||||
self.accessibilityLabel = node.accessibilityLabel;
|
self.accessibilityLabel = node.accessibilityLabel;
|
||||||
self.accessibilityHint = node.accessibilityHint;
|
self.accessibilityHint = node.accessibilityHint;
|
||||||
@@ -34,13 +38,17 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
|||||||
|
|
||||||
- (ASDisplayNode *)asyncdisplaykit_node
|
- (ASDisplayNode *)asyncdisplaykit_node
|
||||||
{
|
{
|
||||||
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
|
return objc_getAssociatedObject(self, @selector(asyncdisplaykit_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - _ASDisplayView
|
#pragma mark - _ASDisplayView / UIAccessibilityContainer
|
||||||
|
|
||||||
|
static BOOL ASNodeIsAccessiblityContainer(ASDisplayNode *node) {
|
||||||
|
return (!node.isAccessibilityElement && [node accessibilityElementCount] > 0);
|
||||||
|
}
|
||||||
|
|
||||||
@interface _ASDisplayView () {
|
@interface _ASDisplayView () {
|
||||||
NSMutableArray *_accessibleElements;
|
NSMutableArray *_accessibleElements;
|
||||||
@@ -65,41 +73,29 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
|||||||
if (selfNode.shouldRasterizeDescendants) {
|
if (selfNode.shouldRasterizeDescendants) {
|
||||||
// In this case we have to go through the whole subnodes tree in BFS fashion and create all
|
// In this case we have to go through the whole subnodes tree in BFS fashion and create all
|
||||||
// accessibility elements ourselves as the view hierarchy is flattened
|
// accessibility elements ourselves as the view hierarchy is flattened
|
||||||
|
ASDisplayNodePerformBlockOnEveryNodeBFS(selfNode, ^(ASDisplayNode * _Nonnull node) {
|
||||||
// Queue used to keep track of subnodes while traversing this layout in a BFS fashion.
|
// For every subnode we have to create a UIAccessibilityElement as we cannot just add the view to the
|
||||||
std::queue<ASDisplayNode *> queue;
|
// accessibleElements as for a subnode of a node with shouldRasterizeDescendants enabled no view exists
|
||||||
queue.push(selfNode);
|
|
||||||
|
|
||||||
while (!queue.empty()) {
|
|
||||||
ASDisplayNode *node = queue.front();
|
|
||||||
queue.pop();
|
|
||||||
|
|
||||||
// Check if we have to add the node to the accessiblity nodes as it's an accessiblity element
|
|
||||||
if (node != selfNode && node.isAccessibilityElement) {
|
if (node != selfNode && node.isAccessibilityElement) {
|
||||||
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
|
[_accessibleElements addObject:[UIAccessibilityElement accessibilityElementWithContainer:self node:node]];
|
||||||
accessibilityElement.asyncdisplaykit_node = node;
|
|
||||||
[_accessibleElements addObject:accessibilityElement];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all subnodes to process in next step
|
|
||||||
for (int i = 0; i < node.subnodes.count; i++)
|
|
||||||
queue.push(node.subnodes[i]);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return _accessibleElements;
|
return _accessibleElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle not rasterize case
|
// Handle not rasterize case
|
||||||
// Create UI accessiblity elements for each subnode that represent an elment within the accessibility container
|
// Create UI accessiblity elements for each subnode that represent an elment within the accessibility container
|
||||||
for (ASDisplayNode *subnode in selfNode.subnodes) {
|
for (ASDisplayNode *subnode in selfNode.subnodes) {
|
||||||
// Check if this subnode is a UIAccessibilityContainer
|
if (subnode.isAccessibilityElement) {
|
||||||
if (!subnode.isAccessibilityElement && [subnode accessibilityElementCount] > 0) {
|
if (subnode.isLayerBacked) {
|
||||||
// We are good and the view is an UIAccessibilityContainer so add it
|
// The same comment for layer backed subnodes is true as for subnodes within a shouldRasterizeDescendants node.
|
||||||
|
// See details above
|
||||||
|
[_accessibleElements addObject:[UIAccessibilityElement accessibilityElementWithContainer:self node:subnode]];
|
||||||
|
} else {
|
||||||
|
[_accessibleElements addObject:subnode.view];
|
||||||
|
}
|
||||||
|
} else if (ASNodeIsAccessiblityContainer(subnode)) {
|
||||||
[_accessibleElements addObject:subnode.view];
|
[_accessibleElements addObject:subnode.view];
|
||||||
} else if (subnode.isAccessibilityElement) {
|
|
||||||
// Create a accessiblity element from the subnode
|
|
||||||
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
|
|
||||||
accessibilityElement.asyncdisplaykit_node = subnode;
|
|
||||||
[_accessibleElements addObject:accessibilityElement];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +109,7 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
|||||||
|
|
||||||
- (id)accessibilityElementAtIndex:(NSInteger)index
|
- (id)accessibilityElementAtIndex:(NSInteger)index
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertNotNil(_accessibleElements, @"At this point _accessibleElements should be created.");
|
||||||
if (_accessibleElements == nil) {
|
if (_accessibleElements == nil) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user