Initial attempt at implementing Display Traits

Initial attempt to get display traits working with ASEnvironment.

To get proper ASDisplayTraits support, you must use an ASViewController. The ASViewController implements UITraitCollection-related methods (`traitCollectionDidChange:`, `willTransitionToTraitCollection:withTransitionCoordinator:`, viewWillTransitionToSize:withTransitionCoordinator`) to update the internal ASDisplayTraits and propagate them to subnodes.

ASTableNode and ASCollectionNode don't actually have their cells as subnodes, so a little bit of trickery is involved (on `setEnvironment:` the table/collection node gets its data controllers completedNodes and propagates the new traits. see `ASDisplayTraitsCollectionTableSetEnvironmentState`). The data controller also passes the current display traits when creating new cells.

ASViewController also supports the ability to return a custom set of display traits. So if you have a modal dialog that should always be told it is in a compact size class, you can set the override block before displaying the VC.

A new example, called Display Traits, has been added. It shows how display traits can be used in a ASViewController with a normal ASDisplayNode as its root, as well as in ASViewControllers hosting table nodes and collection nodes. There is also an example of overriding the default display traits of a VC.

Please provide feedback!
This commit is contained in:
rcancro
2016-04-25 16:59:00 -07:00
parent 495f32f0ef
commit 8bb4eba080
37 changed files with 1535 additions and 68 deletions

View File

@@ -0,0 +1,164 @@
//
// ASTableNode.m
// AsyncDisplayKit
//
// Created by Steven Ramkumar on 11/4/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import "ASEnvironmentInternal.h"
#import "ASFlowLayoutController.h"
#import "ASTableViewInternal.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASRangeControllerUpdateRangeProtocol+Beta.h"
@interface _ASTablePendingState : NSObject
@property (weak, nonatomic) id <ASTableDelegate> delegate;
@property (weak, nonatomic) id <ASTableDataSource> dataSource;
@end
@implementation _ASTablePendingState
@end
@interface ASTableNode ()
@property (nonatomic, strong) _ASTablePendingState *pendingState;
@end
@interface ASTableView ()
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
@end
@implementation ASTableNode
- (instancetype)_initWithTableView:(ASTableView *)tableView
{
// Avoid a retain cycle. In this case, the ASTableView is creating us, and strongly retains us.
ASTableView * __weak weakTableView = tableView;
if (self = [super initWithViewBlock:^UIView *{ return weakTableView; }]) {
__unused __weak ASTableView *view = [self view];
return self;
}
return nil;
}
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
{
ASDisplayNodeViewBlock tableViewBlock = ^UIView *{
return [[ASTableView alloc] _initWithFrame:frame style:style dataControllerClass:dataControllerClass ownedByNode:YES];
};
if (self = [super initWithViewBlock:tableViewBlock]) {
return self;
}
return nil;
}
- (instancetype)initWithStyle:(UITableViewStyle)style
{
return [self _initWithFrame:CGRectZero style:style dataControllerClass:nil];
}
- (instancetype)init
{
return [self _initWithFrame:CGRectZero style:UITableViewStylePlain dataControllerClass:nil];
}
- (void)didLoad
{
[super didLoad];
ASTableView *view = self.view;
view.tableNode = self;
if (_pendingState) {
_ASTablePendingState *pendingState = _pendingState;
self.pendingState = nil;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
}
}
- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode
{
if (!self.isNodeLoaded) {
return;
}
[self.view.rangeController updateCurrentRangeWithMode:rangeMode];
}
- (_ASTablePendingState *)pendingState
{
if (!_pendingState && ![self isNodeLoaded]) {
self.pendingState = [[_ASTablePendingState alloc] init];
}
ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASTableNode should not have a pendingState once it is loaded");
return _pendingState;
}
- (void)setDelegate:(id <ASTableDelegate>)delegate
{
if ([self pendingState]) {
_pendingState.delegate = delegate;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
self.view.asyncDelegate = delegate;
}
}
- (id <ASTableDelegate>)delegate
{
if ([self pendingState]) {
return _pendingState.delegate;
} else {
return self.view.asyncDelegate;
}
}
- (void)setDataSource:(id <ASTableDataSource>)dataSource
{
if ([self pendingState]) {
_pendingState.dataSource = dataSource;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
self.view.asyncDataSource = dataSource;
}
}
- (id <ASTableDataSource>)dataSource
{
if ([self pendingState]) {
return _pendingState.dataSource;
} else {
return self.view.asyncDataSource;
}
}
- (ASTableView *)view
{
return (ASTableView *)[super view];
}
#if ASRangeControllerLoggingEnabled
- (void)visibilityDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
NSLog(@"%@ - visible: %d", self, isVisible);
}
#endif
- (void)clearContents
{
[super clearContents];
[self.view clearContents];
}
- (void)clearFetchedData
{
[super clearFetchedData];
[self.view clearFetchedData];
}
ASDisplayTraitsCollectionTableSetEnvironmentState
@end