Michael Schneider 8897614f0e [Layout] Layout API based on content area (#2110)
* Initial commit for adding a size constraint to ASLayoutable's

* Some more commits

* Fix sample projects in extra/

* Remove preferredFrameSize test of ASEditableTextNode

* Remove preferredFrameSize from examples_extra

* Add deprecation warning to -[ASDisplayNode preferredFrameSize]

* Add deprecation warning to -[ASDisplayNode measureWithSizeRange:]

* Commit

* Commit

* Remove ASRelativeSizeRange

* Make ASRelativeSize private

* Adjust examples

* Improve setting of -[ASLayoutable size] with points or fractions

* Add ASWrapperLayoutSpec

* Improve creation of ASRelativeDimension

* Add `preferredFrameSize` back and add deprecated logging

* Add `layoutSpecBlock` setter and getter and add locking for it

* Add better documentation and fix macros to create ASRelativeDimension

* Create new ASSizeRangeMake with just a CGSize as parameter

* Update Kitten and Social App Layout example

* Add layoutThatFits: and deprecate measure:

* Rename ASRelativeDimension to ASDimension

* Fix examples for ASDimension renaming

* Remove fancy height and width setter

* Fix ASDimension helper

* Rename -[ASLayout layoutableObject] to -[ASLayout layoutable]

* Update layout related methods and more clearer documentation around how to use it

* Deprecate old ASLayout class constructors

* Don't unnecessary recalculate layout if constrained or parent size did not change

* Use shared pointer for ASDisplayNodeLayout

* Fix rebase conflicts

* Add documentation and move implementation in mm file of ASDisplayNodeLayout

* Fix test errors

* Rename ASSize to ASLayoutableSize

* Address comments

* Rename setSizeFromCGSize to setSizeWithCGSize

* Improve inline functions in ASDimension

* Fix rebase conflicts
2016-09-07 08:44:48 -07:00

200 lines
6.7 KiB
Objective-C

//
// ViewController.m
// Sample
//
// 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 "ViewController.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#pragma mark - TransitionNode
#define USE_CUSTOM_LAYOUT_TRANSITION 0
@interface TransitionNode : ASDisplayNode
@property (nonatomic, assign) BOOL enabled;
@property (nonatomic, strong) ASButtonNode *buttonNode;
@property (nonatomic, strong) ASTextNode *textNodeOne;
@property (nonatomic, strong) ASTextNode *textNodeTwo;
@end
@implementation TransitionNode
#pragma mark - Lifecycle
- (instancetype)init
{
self = [super init];
if (self == nil) { return self; }
self.automaticallyManagesSubnodes = YES;
// Define the layout transition duration for the default transition
self.defaultLayoutTransitionDuration = 1.0;
_enabled = NO;
// Setup text nodes
_textNodeOne = [[ASTextNode alloc] init];
_textNodeOne.attributedText = [[NSAttributedString alloc] initWithString:@"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled"];
_textNodeTwo = [[ASTextNode alloc] init];
_textNodeTwo.attributedText = [[NSAttributedString alloc] initWithString:@"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English."];
// Setup button
NSString *buttonTitle = @"Start Layout Transition";
UIFont *buttonFont = [UIFont systemFontOfSize:16.0];
UIColor *buttonColor = [UIColor blueColor];
_buttonNode = [[ASButtonNode alloc] init];
[_buttonNode setTitle:buttonTitle withFont:buttonFont withColor:buttonColor forState:ASControlStateNormal];
// Note: Currently we have to set all the button properties to the same one as for ASControlStateNormal. Otherwise
// if the button is involved in the layout transition it would break the transition as it does a layout pass
// while changing the title. This needs and will be fixed in the future!
[_buttonNode setTitle:buttonTitle withFont:buttonFont withColor:buttonColor forState:ASControlStateHighlighted];
// Some debug colors
_textNodeOne.backgroundColor = [UIColor orangeColor];
_textNodeTwo.backgroundColor = [UIColor greenColor];
return self;
}
- (void)didLoad
{
[super didLoad];
[self.buttonNode addTarget:self action:@selector(buttonPressed:) forControlEvents:ASControlNodeEventTouchDown];
}
#pragma mark - Actions
- (void)buttonPressed:(id)sender
{
self.enabled = !self.enabled;
[self transitionLayoutWithAnimation:YES shouldMeasureAsync:NO measurementCompletion:nil];
}
#pragma mark - Layout
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASTextNode *nextTextNode = self.enabled ? self.textNodeTwo : self.textNodeOne;
nextTextNode.flexGrow = YES;
nextTextNode.flexShrink = YES;
ASStackLayoutSpec *horizontalStackLayout = [ASStackLayoutSpec horizontalStackLayoutSpec];
horizontalStackLayout.children = @[nextTextNode];
self.buttonNode.alignSelf = ASStackLayoutAlignSelfCenter;
ASStackLayoutSpec *verticalStackLayout = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackLayout.spacing = 10.0;
verticalStackLayout.children = @[horizontalStackLayout, self.buttonNode];
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(15.0, 15.0, 15.0, 15.0) child:verticalStackLayout];
}
#pragma mark - Transition
#if USE_CUSTOM_LAYOUT_TRANSITION
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
{
ASDisplayNode *fromNode = [[context removedSubnodes] objectAtIndex:0];
ASDisplayNode *toNode = [[context insertedSubnodes] objectAtIndex:0];
ASButtonNode *buttonNode = nil;
for (ASDisplayNode *node in [context subnodesForKey:ASTransitionContextToLayoutKey]) {
if ([node isKindOfClass:[ASButtonNode class]]) {
buttonNode = (ASButtonNode *)node;
break;
}
}
CGRect toNodeFrame = [context finalFrameForNode:toNode];
toNodeFrame.origin.x += (self.enabled ? toNodeFrame.size.width : -toNodeFrame.size.width);
toNode.frame = toNodeFrame;
toNode.alpha = 0.0;
CGRect fromNodeFrame = fromNode.frame;
fromNodeFrame.origin.x += (self.enabled ? -fromNodeFrame.size.width : fromNodeFrame.size.width);
// We will use the same transition duration as the default transition
[UIView animateWithDuration:self.defaultLayoutTransitionDuration animations:^{
toNode.frame = [context finalFrameForNode:toNode];
toNode.alpha = 1.0;
fromNode.frame = fromNodeFrame;
fromNode.alpha = 0.0;
// Update frame of self
CGSize fromSize = [context layoutForKey:ASTransitionContextFromLayoutKey].size;
CGSize toSize = [context layoutForKey:ASTransitionContextToLayoutKey].size;
BOOL isResized = (CGSizeEqualToSize(fromSize, toSize) == NO);
if (isResized == YES) {
CGPoint position = self.frame.origin;
self.frame = CGRectMake(position.x, position.y, toSize.width, toSize.height);
}
buttonNode.frame = [context finalFrameForNode:buttonNode];
} completion:^(BOOL finished) {
[context completeTransition:finished];
}];
}
#endif
@end
#pragma mark - ViewController
@interface ViewController ()
@property (nonatomic, strong) TransitionNode *transitionNode;
@end
@implementation ViewController
#pragma mark - UIViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_transitionNode = [TransitionNode new];
[self.view addSubnode:_transitionNode];
// Some debug colors
_transitionNode.backgroundColor = [UIColor grayColor];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGSize size = [self.transitionNode layoutThatFits:ASSizeRangeMake(CGSizeZero, self.view.frame.size)].size;
self.transitionNode.frame = CGRectMake(0, 20, size.width, size.height);
}
@end