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));
|
||||
|
||||
/**
|
||||
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
|
||||
node provided directly to the function call - only on all descendants.
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
#import <queue>
|
||||
|
||||
extern ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *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))
|
||||
{
|
||||
for (ASDisplayNode *subnode in node.subnodes) {
|
||||
|
||||
@@ -8,23 +8,27 @@
|
||||
|
||||
#import "_ASDisplayViewAccessiblity.h"
|
||||
#import "_ASDisplayView.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <queue>
|
||||
|
||||
|
||||
#pragma mark - UIAccessibilityElement
|
||||
|
||||
static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
||||
|
||||
@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
|
||||
{
|
||||
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.accessibilityLabel = node.accessibilityLabel;
|
||||
self.accessibilityHint = node.accessibilityHint;
|
||||
@@ -34,13 +38,17 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
||||
|
||||
- (ASDisplayNode *)asyncdisplaykit_node
|
||||
{
|
||||
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
|
||||
return objc_getAssociatedObject(self, @selector(asyncdisplaykit_node));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - _ASDisplayView
|
||||
#pragma mark - _ASDisplayView / UIAccessibilityContainer
|
||||
|
||||
static BOOL ASNodeIsAccessiblityContainer(ASDisplayNode *node) {
|
||||
return (!node.isAccessibilityElement && [node accessibilityElementCount] > 0);
|
||||
}
|
||||
|
||||
@interface _ASDisplayView () {
|
||||
NSMutableArray *_accessibleElements;
|
||||
@@ -65,41 +73,29 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
|
||||
if (selfNode.shouldRasterizeDescendants) {
|
||||
// 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
|
||||
|
||||
// Queue used to keep track of subnodes while traversing this layout in a BFS fashion.
|
||||
std::queue<ASDisplayNode *> queue;
|
||||
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
|
||||
ASDisplayNodePerformBlockOnEveryNodeBFS(selfNode, ^(ASDisplayNode * _Nonnull node) {
|
||||
// For every subnode we have to create a UIAccessibilityElement as we cannot just add the view to the
|
||||
// accessibleElements as for a subnode of a node with shouldRasterizeDescendants enabled no view exists
|
||||
if (node != selfNode && node.isAccessibilityElement) {
|
||||
UIAccessibilityElement *accessibilityElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
|
||||
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]);
|
||||
[_accessibleElements addObject:[UIAccessibilityElement accessibilityElementWithContainer:self node:node]];
|
||||
}
|
||||
});
|
||||
return _accessibleElements;
|
||||
}
|
||||
|
||||
// Handle not rasterize case
|
||||
// Create UI accessiblity elements for each subnode that represent an elment within the accessibility container
|
||||
for (ASDisplayNode *subnode in selfNode.subnodes) {
|
||||
// Check if this subnode is a UIAccessibilityContainer
|
||||
if (!subnode.isAccessibilityElement && [subnode accessibilityElementCount] > 0) {
|
||||
// We are good and the view is an UIAccessibilityContainer so add it
|
||||
if (subnode.isAccessibilityElement) {
|
||||
if (subnode.isLayerBacked) {
|
||||
// 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];
|
||||
} 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
|
||||
{
|
||||
ASDisplayNodeAssertNotNil(_accessibleElements, @"At this point _accessibleElements should be created.");
|
||||
if (_accessibleElements == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user