mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Add a block API to provide an ASLayoutSpec without having to subclass ASDisplayNode
This commit is contained in:
@@ -143,6 +143,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* encouraged.
|
* encouraged.
|
||||||
*
|
*
|
||||||
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
|
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
|
||||||
|
*
|
||||||
|
* @warning Overwriting layoutSpecThatFits: in a subclass and providing a layoutSpecBlock block is currently not supported
|
||||||
*/
|
*/
|
||||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
|
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode * _Nonnull node);
|
|||||||
*/
|
*/
|
||||||
typedef void (^ASDisplayNodeContextModifier)(_Nonnull CGContextRef context);
|
typedef void (^ASDisplayNodeContextModifier)(_Nonnull CGContextRef context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASDisplayNode layout spec block. This block can be used instead of implementing layoutSpecThatFits: in subclass
|
||||||
|
*/
|
||||||
|
typedef ASLayoutSpec * _Nonnull(^ASLayoutSpecBlock)(ASSizeRange constrainedSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Interface state is available on ASDisplayNode and ASViewController, and
|
Interface state is available on ASDisplayNode and ASViewController, and
|
||||||
allows checking whether a node is in an interface situation where it is prudent to trigger certain
|
allows checking whether a node is in an interface situation where it is prudent to trigger certain
|
||||||
@@ -252,6 +257,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
|
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
|
||||||
|
* implement layoutSpecThatFits:
|
||||||
|
*
|
||||||
|
* @return The block used to provide a ASLayoutSpec
|
||||||
|
*
|
||||||
|
* @warning Overwriting layoutSpecThatFits: in a subclass and providing a layoutSpecBlock block is currently not supported
|
||||||
|
*/
|
||||||
|
@property (nonatomic, readwrite, copy, nullable) ASLayoutSpecBlock layoutSpecBlock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract Return the calculated size.
|
* @abstract Return the calculated size.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -167,6 +167,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
return overrides;
|
return overrides;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At most a layoutSpecBlock or one of the three layout methods is overridden
|
||||||
|
#define __ASDisplayNodeCheckForLayoutMethodOverrides \
|
||||||
|
ASDisplayNodeAssert(_layoutSpecBlock != nil || \
|
||||||
|
(ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \
|
||||||
|
+ (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \
|
||||||
|
+ (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1, \
|
||||||
|
@"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self.class))
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self != [ASDisplayNode class]) {
|
if (self != [ASDisplayNode class]) {
|
||||||
@@ -178,12 +186,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self));
|
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self));
|
||||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self));
|
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self));
|
||||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self));
|
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self));
|
||||||
|
|
||||||
// At most one of the three layout methods is overridden
|
|
||||||
ASDisplayNodeAssert((ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateSizeThatFits:)) ? 1 : 0)
|
|
||||||
+ (ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutSpecThatFits:)) ? 1 : 0)
|
|
||||||
+ (ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1,
|
|
||||||
@"Subclass %@ must override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
|
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
|
||||||
@@ -1848,8 +1850,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
|||||||
|
|
||||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||||
{
|
{
|
||||||
|
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
||||||
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
if (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) {
|
if ((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || _layoutSpecBlock != nil) {
|
||||||
ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize];
|
ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize];
|
||||||
layoutSpec.parent = self; // This causes upward propogation of any non-default layoutable values.
|
layoutSpec.parent = self; // This causes upward propogation of any non-default layoutable values.
|
||||||
layoutSpec.isMutable = NO;
|
layoutSpec.isMutable = NO;
|
||||||
@@ -1876,13 +1880,22 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
|||||||
|
|
||||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||||
{
|
{
|
||||||
|
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
||||||
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
return _preferredFrameSize;
|
return _preferredFrameSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||||
{
|
{
|
||||||
|
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
||||||
|
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
|
||||||
|
if (_layoutSpecBlock != nil) {
|
||||||
|
return _layoutSpecBlock(constrainedSize);
|
||||||
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1904,6 +1917,14 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
|||||||
return _constrainedSize;
|
return _constrainedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setLayoutSpecThatFitsBlock:(ASLayoutSpecBlock)layoutSpecBlock
|
||||||
|
{
|
||||||
|
// For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided
|
||||||
|
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");
|
||||||
|
|
||||||
|
_layoutSpecBlock = layoutSpecBlock;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setPendingTransitionID:(int32_t)pendingTransitionID
|
- (void)setPendingTransitionID:(int32_t)pendingTransitionID
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
@interface ASSpecTestDisplayNode : ASDisplayNode
|
@interface ASSpecTestDisplayNode : ASDisplayNode
|
||||||
|
|
||||||
@property (copy, nonatomic) ASLayoutSpec * (^layoutSpecBlock)(ASSizeRange constrainedSize, NSNumber *layoutState);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Simple state identifier to allow control of current spec inside of the layoutSpecBlock
|
Simple state identifier to allow control of current spec inside of the layoutSpecBlock
|
||||||
*/
|
*/
|
||||||
@@ -37,11 +35,6 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
|
||||||
{
|
|
||||||
return self.layoutSpecBlock(constrainedSize, _layoutState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface ASDisplayNodeImplicitHierarchyTests : XCTestCase
|
@interface ASDisplayNodeImplicitHierarchyTests : XCTestCase
|
||||||
@@ -83,7 +76,7 @@
|
|||||||
ASDisplayNode *node5 = [[ASDisplayNode alloc] init];
|
ASDisplayNode *node5 = [[ASDisplayNode alloc] init];
|
||||||
|
|
||||||
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
|
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
|
||||||
node.layoutSpecBlock = ^(ASSizeRange constrainedSize, NSNumber *layoutState) {
|
node.layoutSpecBlock = ^(ASSizeRange constrainedSize) {
|
||||||
ASStaticLayoutSpec *staticLayout = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node4]];
|
ASStaticLayoutSpec *staticLayout = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node4]];
|
||||||
|
|
||||||
ASStackLayoutSpec *stack1 = [[ASStackLayoutSpec alloc] init];
|
ASStackLayoutSpec *stack1 = [[ASStackLayoutSpec alloc] init];
|
||||||
@@ -109,8 +102,9 @@
|
|||||||
ASDisplayNode *node3 = [[ASDisplayNode alloc] init];
|
ASDisplayNode *node3 = [[ASDisplayNode alloc] init];
|
||||||
|
|
||||||
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
|
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
|
||||||
node.layoutSpecBlock = ^(ASSizeRange constrainedSize, NSNumber *layoutState){
|
__weak ASSpecTestDisplayNode *weakNode = node;
|
||||||
if ([layoutState isEqualToNumber:@1]) {
|
node.layoutSpecBlock = ^(ASSizeRange constrainedSize){
|
||||||
|
if ([weakNode.layoutState isEqualToNumber:@1]) {
|
||||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node1, node2]];
|
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[node1, node2]];
|
||||||
} else {
|
} else {
|
||||||
ASStackLayoutSpec *stackLayout = [[ASStackLayoutSpec alloc] init];
|
ASStackLayoutSpec *stackLayout = [[ASStackLayoutSpec alloc] init];
|
||||||
|
|||||||
Reference in New Issue
Block a user