Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

[ASViewController] Support optional node #2810

Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions AsyncDisplayKit/ASViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,25 @@ NS_ASSUME_NONNULL_BEGIN
typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitCollectionBlock)(UITraitCollection *traitCollection);
typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(CGSize windowSize);

/**
* ASViewController allows you to have a completely node backed hierarchy. It automatically
* handles @c ASVisibilityDepth, automatic range mode and propogating @c ASDisplayTraits to contained nodes.
*
* You can opt-out of node backed hierarchy and use it like a normal UIViewController.
* More importantly, you can use it as a base class for all of your view controllers among which some use a node hierarchy and some don't.
* See examples/ASDKgram project for actual implementation.
*/
@interface ASViewController<__covariant DisplayNodeType : ASDisplayNode *> : UIViewController <ASVisibilityDepth>

/**
* ASViewController Designated initializer.
*
* @discussion ASViewController allows you to have a completely node backed heirarchy. It automatically
* handles @c ASVisibilityDepth, automatic range mode and propogating @c ASDisplayTraits to contained nodes.
* ASViewController initializer.
*
* @param node An ASDisplayNode which will provide the root view (self.view)
* @return An ASViewController instance whose root view will be backed by the provided ASDisplayNode.
*
* @see ASVisibilityDepth
*/
- (instancetype)initWithNode:(DisplayNodeType)node NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNode:(DisplayNodeType)node;

/**
* @return node Returns the ASDisplayNode which provides the backing view to the view controller.
Expand Down Expand Up @@ -91,12 +96,4 @@ typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(C

@end

@interface ASViewController (Unavailable)

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil AS_UNAVAILABLE("ASViewController requires using -initWithNode:");

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder AS_UNAVAILABLE("ASViewController requires using -initWithNode:");

@end

NS_ASSUME_NONNULL_END
40 changes: 31 additions & 9 deletions AsyncDisplayKit/ASViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,30 @@ @implementation ASViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
ASDisplayNodeAssert(NO, @"ASViewController requires using -initWithNode:");
return [self initWithNode:[[ASDisplayNode alloc] init]];
if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
return nil;
}

_node = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
return [[UIView alloc] init];
}];
[self _initializeInstance];

return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
ASDisplayNodeAssert(NO, @"ASViewController requires using -initWithNode:");
return [self initWithNode:[[ASDisplayNode alloc] init]];
if (!(self = [super initWithCoder:aDecoder])) {
return nil;
}

_node = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
return [[UIView alloc] init];
}];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use simply [[ASDisplayNode alloc] init] here because using a view block results in a wrapper node, which has limited capacity compared to a node using _ASDisplayView.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Thanks. Fixing.

[self _initializeInstance];

return self;
}

- (instancetype)initWithNode:(ASDisplayNode *)node
Expand All @@ -50,10 +66,17 @@ - (instancetype)initWithNode:(ASDisplayNode *)node
return nil;
}

ASDisplayNodeAssertNotNil(node, @"Node must not be nil");
ASDisplayNodeAssertTrue(!node.layerBacked);
_node = node;
[self _initializeInstance];

return self;
}

- (void)_initializeInstance
{
ASDisplayNodeAssertNotNil(_node, @"Node must not be nil");
ASDisplayNodeAssertTrue(!_node.layerBacked);

_selfConformsToRangeModeProtocol = [self conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)];
_nodeConformsToRangeModeProtocol = [_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)];
_automaticallyAdjustRangeModeBasedOnViewEvents = _selfConformsToRangeModeProtocol || _nodeConformsToRangeModeProtocol;
Expand All @@ -63,7 +86,7 @@ - (instancetype)initWithNode:(ASDisplayNode *)node
// Node already loaded the view
[self view];
} else {
// If the node didn't load yet add ourselves as on did load observer to laod the view in case the node gets loaded
// If the node didn't load yet add ourselves as on did load observer to load the view in case the node gets loaded
// before the view controller
__weak __typeof__(self) weakSelf = self;
[_node onDidLoad:^(__kindof ASDisplayNode * _Nonnull node) {
Expand All @@ -72,8 +95,6 @@ - (instancetype)initWithNode:(ASDisplayNode *)node
}
}];
}

return self;
}

- (void)dealloc
Expand All @@ -90,6 +111,7 @@ - (void)loadView
// we can avoid that here. Enabling layerBacking on a single node in the hierarchy
// will have a greater performance benefit than the impact of this transient view.
[super loadView];

UIView *view = self.view;
CGRect frame = view.frame;
UIViewAutoresizing autoresizingMask = view.autoresizingMask;
Expand Down
6 changes: 6 additions & 0 deletions examples/ASDKgram/Sample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
768843931CAA37EF00D8629E /* UserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7688437B1CAA37EF00D8629E /* UserModel.m */; };
768843961CAA37EF00D8629E /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7688437E1CAA37EF00D8629E /* Utilities.m */; };
B13424EE6D36C2EC5D1030B6 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AD5DDA0A29B0F32AA5CC47BA /* libPods-Sample.a */; };
E5F128F01E09625400B4335F /* PhotoFeedBaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -81,6 +82,8 @@
97A9B1BAF4265967672F9EA3 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
AD5DDA0A29B0F32AA5CC47BA /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
D09B5DF0BFB37583DE8F3142 /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
E5F128EE1E09612700B4335F /* PhotoFeedBaseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoFeedBaseController.h; sourceTree = "<group>"; };
E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotoFeedBaseController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -167,6 +170,8 @@
767A5F141CAA3D8A004CDA8D /* Controller */ = {
isa = PBXGroup;
children = (
E5F128EE1E09612700B4335F /* PhotoFeedBaseController.h */,
E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */,
767A5F161CAA3D96004CDA8D /* UIKit */,
767A5F151CAA3D90004CDA8D /* ASDK */,
);
Expand Down Expand Up @@ -377,6 +382,7 @@
768843821CAA37EF00D8629E /* CommentModel.m in Sources */,
768843831CAA37EF00D8629E /* CommentsNode.m in Sources */,
768843961CAA37EF00D8629E /* Utilities.m in Sources */,
E5F128F01E09625400B4335F /* PhotoFeedBaseController.m in Sources */,
768843931CAA37EF00D8629E /* UserModel.m in Sources */,
768843801CAA37EF00D8629E /* AppDelegate.m in Sources */,
768843811CAA37EF00D8629E /* CommentFeedModel.m in Sources */,
Expand Down
38 changes: 38 additions & 0 deletions examples/ASDKgram/Sample/PhotoFeedBaseController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// PhotoFeedBaseController.h
// Sample
//
// Created by Huy Nguyen on 20/12/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "AppDelegate.h"

@class PhotoFeedModel;

@interface PhotoFeedBaseController : ASViewController <PhotoFeedControllerProtocol>

@property (nonatomic, strong, readonly) PhotoFeedModel *photoFeed;
@property (nonatomic, strong, readonly) UITableView *tableView;

- (void)refreshFeed;
- (void)insertNewRows:(NSArray *)newPhotos;

#pragma mark - Subclasses must override these methods

- (void)loadPage;
- (void)requestCommentsForPhotos:(NSArray *)newPhotos;

@end
123 changes: 123 additions & 0 deletions examples/ASDKgram/Sample/PhotoFeedBaseController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// PhotoFeedBaseController.m
// Sample
//
// Created by Huy Nguyen on 20/12/16.
// Copyright © 2016 Facebook. All rights reserved.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import "PhotoFeedBaseController.h"
#import "PhotoFeedModel.h"

@implementation PhotoFeedBaseController
{
UIActivityIndicatorView *_activityIndicatorView;
}

// -loadView is guaranteed to be called on the main thread and is the appropriate place to
// set up an UIKit objects you may be using.
- (void)loadView
{
[super loadView];

_activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];

_photoFeed = [[PhotoFeedModel alloc] initWithPhotoFeedModelType:PhotoFeedModelTypePopular imageSize:[self imageSizeForScreenWidth]];
[self refreshFeed];

CGSize boundSize = self.view.bounds.size;
[_activityIndicatorView sizeToFit];
CGRect refreshRect = _activityIndicatorView.frame;
refreshRect.origin = CGPointMake((boundSize.width - _activityIndicatorView.frame.size.width) / 2.0,
(boundSize.height - _activityIndicatorView.frame.size.height) / 2.0);
_activityIndicatorView.frame = refreshRect;
[self.view addSubview:_activityIndicatorView];

self.tableView.allowsSelection = NO;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

self.view.backgroundColor = [UIColor whiteColor];
}

- (void)refreshFeed
{
[_activityIndicatorView startAnimating];
// small first batch
[_photoFeed refreshFeedWithCompletionBlock:^(NSArray *newPhotos){

[_activityIndicatorView stopAnimating];

[self insertNewRows:newPhotos];
[self requestCommentsForPhotos:newPhotos];

// immediately start second larger fetch
[self loadPage];

} numResultsToReturn:4];
}

- (void)insertNewRows:(NSArray *)newPhotos
{
NSInteger section = 0;
NSMutableArray *indexPaths = [NSMutableArray array];

NSInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed];
for (NSInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section];
[indexPaths addObject:path];
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
}

- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}

- (CGSize)imageSizeForScreenWidth
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenScale = [[UIScreen mainScreen] scale];
return CGSizeMake(screenRect.size.width * screenScale, screenRect.size.width * screenScale);
}

#pragma mark - PhotoFeedViewControllerProtocol

- (void)resetAllData
{
[_photoFeed clearFeed];
[self.tableView reloadData];
[self refreshFeed];
}

#pragma mark - Subclassing

- (UITableView *)tableView
{
NSAssert(NO, @"Subclasses must override this method");
return nil;
}

- (void)loadPage
{
NSAssert(NO, @"Subclasses must override this method");
}

- (void)requestCommentsForPhotos:(NSArray *)newPhotos
{
NSAssert(NO, @"Subclasses must override this method");
}

@end
5 changes: 2 additions & 3 deletions examples/ASDKgram/Sample/PhotoFeedNodeController.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "AppDelegate.h"
#import "PhotoFeedBaseController.h"

@interface PhotoFeedNodeController : ASViewController <PhotoFeedControllerProtocol>
@interface PhotoFeedNodeController : PhotoFeedBaseController

@end
Loading