Skip to content

Commit

Permalink
[ASDisplayNode] Allow setting stretchable contents on nodes; add brid…
Browse files Browse the repository at this point in the history
…ged properties. (#429)

This makes it much easier to use the ASCoreAnimationExtras method which offers by
far the most efficient way to display a stretchable image.

In the future, we should move that function to UIImage+ASConvenience or another
header where it can be more easily found and enjoyed!
  • Loading branch information
appleguy committed Jul 9, 2017
1 parent 554c688 commit bc361ca
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 34 deletions.
46 changes: 27 additions & 19 deletions Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -621,29 +621,32 @@ extern NSInteger const ASDefaultDrawingPriority;
*/
- (void)layoutIfNeeded;

@property (nonatomic, strong, nullable) id contents; // default=nil
@property (nonatomic, assign) CGRect frame; // default=CGRectZero
@property (nonatomic, assign) CGRect bounds; // default=CGRectZero
@property (nonatomic, assign) CGPoint position; // default=CGPointZero
@property (nonatomic, assign) CGFloat alpha; // default=1.0f

@property (nonatomic, assign) BOOL clipsToBounds; // default==NO
@property (nonatomic, getter=isHidden) BOOL hidden; // default==NO
@property (nonatomic, getter=isOpaque) BOOL opaque; // default==YES

@property (nonatomic, assign) BOOL allowsGroupOpacity;
@property (nonatomic, assign) BOOL allowsEdgeAntialiasing;
@property (nonatomic, assign) unsigned int edgeAntialiasingMask; // default==all values from CAEdgeAntialiasingMask
@property (nonatomic, strong, nullable) id contents; // default=nil
@property (nonatomic, assign) CGRect contentsRect; // default={0,0,1,1}. @see CALayer.h for details.
@property (nonatomic, assign) CGRect contentsCenter; // default={0,0,1,1}. @see CALayer.h for details.
@property (nonatomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for details.
@property (nonatomic, assign) CGFloat rasterizationScale; // default=1.0f.

@property (nonatomic, getter=isHidden) BOOL hidden; // default==NO
@property (nonatomic, assign) BOOL needsDisplayOnBoundsChange; // default==NO
@property (nonatomic, assign) BOOL autoresizesSubviews; // default==YES (undefined for layer-backed nodes)
@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; // default==UIViewAutoresizingNone (undefined for layer-backed nodes)
@property (nonatomic, assign) CGFloat alpha; // default=1.0f
@property (nonatomic, assign) CGRect bounds; // default=CGRectZero
@property (nonatomic, assign) CGRect frame; // default=CGRectZero
@property (nonatomic, assign) CGPoint anchorPoint; // default={0.5, 0.5}
@property (nonatomic, assign) CGFloat zPosition; // default=0.0
@property (nonatomic, assign) CGPoint position; // default=CGPointZero
@property (nonatomic, assign) CGFloat cornerRadius; // default=0.0
@property (nonatomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for more info
@property (nonatomic, assign) CATransform3D transform; // default=CATransform3DIdentity
@property (nonatomic, assign) CATransform3D subnodeTransform; // default=CATransform3DIdentity

@property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes)
#if TARGET_OS_IOS
@property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO
#endif

/**
* @abstract The node view's background color.
*
Expand All @@ -652,8 +655,8 @@ extern NSInteger const ASDefaultDrawingPriority;
*/
@property (nonatomic, strong, nullable) UIColor *backgroundColor; // default=nil

@property (nonatomic, strong, null_resettable) UIColor *tintColor; // default=Blue
- (void)tintColorDidChange; // Notifies the node when the tintColor has changed.
@property (nonatomic, strong, null_resettable) UIColor *tintColor; // default=Blue
- (void)tintColorDidChange; // Notifies the node when the tintColor has changed.

/**
* @abstract A flag used to determine how a node lays out its content when its bounds change.
Expand All @@ -664,19 +667,24 @@ extern NSInteger const ASDefaultDrawingPriority;
* contentMode for your content while it's being re-rendered.
*/
@property (nonatomic, assign) UIViewContentMode contentMode; // default=UIViewContentModeScaleToFill
@property (nonatomic, copy) NSString *contentsGravity; // Use .contentMode in preference when possible.
@property (nonatomic, assign) UISemanticContentAttribute semanticContentAttribute; // default=Unspecified

@property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes)
#if TARGET_OS_IOS
@property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO
#endif
@property (nonatomic, nullable) CGColorRef shadowColor; // default=opaque rgb black
@property (nonatomic, assign) CGFloat shadowOpacity; // default=0.0
@property (nonatomic, assign) CGSize shadowOffset; // default=(0, -3)
@property (nonatomic, assign) CGFloat shadowRadius; // default=3
@property (nonatomic, assign) CGFloat borderWidth; // default=0
@property (nonatomic, nullable) CGColorRef borderColor; // default=opaque rgb black

@property (nonatomic, assign) BOOL allowsGroupOpacity;
@property (nonatomic, assign) BOOL allowsEdgeAntialiasing;
@property (nonatomic, assign) unsigned int edgeAntialiasingMask; // default==all values from CAEdgeAntialiasingMask

@property (nonatomic, assign) BOOL needsDisplayOnBoundsChange; // default==NO
@property (nonatomic, assign) BOOL autoresizesSubviews; // default==YES (undefined for layer-backed nodes)
@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; // default==UIViewAutoresizingNone (undefined for layer-backed nodes)

// UIResponder methods
// By default these fall through to the underlying view, but can be overridden.
- (BOOL)canBecomeFirstResponder; // default==NO
Expand Down
2 changes: 1 addition & 1 deletion Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2442,7 +2442,7 @@ - (void)_locked_setupPlaceholderLayerIfNeeded
if (_placeholderImage) {
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(_placeholderImage.capInsets, UIEdgeInsetsZero);
if (stretchable) {
ASDisplayNodeSetupLayerContentsWithResizableImage(_placeholderLayer, _placeholderImage);
ASDisplayNodeSetResizableContents(_placeholderLayer, _placeholderImage);
} else {
_placeholderLayer.contentsScale = self.contentsScale;
_placeholderLayer.contents = (id)_placeholderImage.CGImage;
Expand Down
6 changes: 5 additions & 1 deletion Source/Details/UIView+ASConvenience.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) CGPoint position;
@property (nonatomic, assign) CGFloat zPosition;
@property (nonatomic, assign) CGPoint anchorPoint;
@property (nullable, nonatomic, strong) id contents;
@property (nonatomic, assign) CGFloat cornerRadius;
@property (nullable, nonatomic, strong) id contents;
@property (nonatomic, copy) NSString *contentsGravity;
@property (nonatomic, assign) CGRect contentsRect;
@property (nonatomic, assign) CGRect contentsCenter;
@property (nonatomic, assign) CGFloat contentsScale;
@property (nonatomic, assign) CGFloat rasterizationScale;
@property (nonatomic, assign) CATransform3D transform;
@property (nonatomic, assign) CATransform3D sublayerTransform;
@property (nonatomic, assign) BOOL needsDisplayOnBoundsChange;
Expand Down
2 changes: 1 addition & 1 deletion Source/Private/ASDisplayNode+AsyncDisplay.mm
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ - (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asy
UIImage *image = (UIImage *)value;
BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero));
if (stretchable) {
ASDisplayNodeSetupLayerContentsWithResizableImage(layer, image);
ASDisplayNodeSetResizableContents(layer, image);
} else {
layer.contentsScale = self.contentsScale;
layer.contents = (id)image.CGImage;
Expand Down
48 changes: 48 additions & 0 deletions Source/Private/ASDisplayNode+UIViewBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,42 @@ - (void)setCornerRadius:(CGFloat)newCornerRadius
_setToLayer(cornerRadius, newCornerRadius);
}

- (NSString *)contentsGravity
{
_bridge_prologue_read;
return _getFromLayer(contentsGravity);
}

- (void)setContentsGravity:(NSString *)newContentsGravity
{
_bridge_prologue_write;
_setToLayer(contentsGravity, newContentsGravity);
}

- (CGRect)contentsRect
{
_bridge_prologue_read;
return _getFromLayer(contentsRect);
}

- (void)setContentsRect:(CGRect)newContentsRect
{
_bridge_prologue_write;
_setToLayer(contentsRect, newContentsRect);
}

- (CGRect)contentsCenter
{
_bridge_prologue_read;
return _getFromLayer(contentsCenter);
}

- (void)setContentsCenter:(CGRect)newContentsCenter
{
_bridge_prologue_write;
_setToLayer(contentsCenter, newContentsCenter);
}

- (CGFloat)contentsScale
{
_bridge_prologue_read;
Expand All @@ -207,6 +243,18 @@ - (void)setContentsScale:(CGFloat)newContentsScale
_setToLayer(contentsScale, newContentsScale);
}

- (CGFloat)rasterizationScale
{
_bridge_prologue_read;
return _getFromLayer(rasterizationScale);
}

- (void)setRasterizationScale:(CGFloat)newRasterizationScale
{
_bridge_prologue_write;
_setToLayer(rasterizationScale, newRasterizationScale);
}

- (CGRect)bounds
{
_bridge_prologue_read;
Expand Down
23 changes: 22 additions & 1 deletion Source/Private/_ASCoreAnimationExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,28 @@
#import <UIKit/UIKit.h>

#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNode.h>

ASDISPLAYNODE_EXTERN_C_BEGIN

extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image);
// This protocol defines the core properties that ASDisplayNode and CALayer share, for managing contents.
@protocol ASResizableContents
@required
@property id contents;
@property CGRect contentsRect;
@property CGRect contentsCenter;
@property CGFloat contentsScale;
@property CGFloat rasterizationScale;
@property NSString *contentsGravity;
@end

@interface CALayer (ASResizableContents) <ASResizableContents>
@end
@interface ASDisplayNode (ASResizableContents) <ASResizableContents>
@end

// This function can operate on either an ASDisplayNode (including un-loaded) or CALayer directly.
extern void ASDisplayNodeSetResizableContents(id<ASResizableContents> obj, UIImage *image);

/**
Turns a value of UIViewContentMode to a string for debugging or serialization
Expand Down Expand Up @@ -67,4 +85,7 @@ extern UIImage *ASDisplayNodeStretchableBoxContentsWithColor(UIColor *color, CGS
*/
extern BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer);

// This function is a less generalized version of ASDisplayNodeSetResizableContents.
extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) ASDISPLAYNODE_DEPRECATED;

ASDISPLAYNODE_EXTERN_C_END
18 changes: 11 additions & 7 deletions Source/Private/_ASCoreAnimationExtras.mm
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@
#import <AsyncDisplayKit/ASAssert.h>

extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image)
{
ASDisplayNodeSetResizableContents(layer, image);
}

extern void ASDisplayNodeSetResizableContents(id<ASResizableContents> obj, UIImage *image)
{
// FIXME: This method does not currently handle UIImageResizingModeTile, which is the default on iOS 6.
// I'm not sure of a way to use CALayer directly to perform such tiling on the GPU, though the stretch is handled by the GPU,
// and CALayer.h documents the fact that contentsCenter is used to stretch the pixels.

if (image) {

// Image may not actually be stretchable in one or both dimensions; this is handled
layer.contents = (id)[image CGImage];
layer.contentsScale = [image scale];
layer.rasterizationScale = [image scale];
obj.contents = (id)[image CGImage];
obj.contentsScale = [image scale];
obj.rasterizationScale = [image scale];
CGSize imageSize = [image size];

ASDisplayNodeCAssert(image.resizingMode == UIImageResizingModeStretch || UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero),
Expand All @@ -51,11 +55,11 @@ extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UI
contentsCenter.origin.y = ((insets.top + halfPixelFudge) / imageSize.height);
contentsCenter.size.height = (imageSize.height - (insets.top + insets.bottom + 1.f) + otherPixelFudge) / imageSize.height;
}
layer.contentsGravity = kCAGravityResize;
layer.contentsCenter = contentsCenter;
obj.contentsGravity = kCAGravityResize;
obj.contentsCenter = contentsCenter;

} else {
layer.contents = nil;
obj.contents = nil;
}
}

Expand Down
36 changes: 32 additions & 4 deletions Source/Private/_ASPendingState.mm
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@
int setAnchorPoint:1;
int setPosition:1;
int setZPosition:1;
int setContentsGravity:1;
int setContentsRect:1;
int setContentsCenter:1;
int setContentsScale:1;
int setRasterizationScale:1;
int setTransform:1;
int setSublayerTransform:1;
int setUserInteractionEnabled:1;
Expand Down Expand Up @@ -101,7 +105,11 @@ @implementation _ASPendingState
CGPoint anchorPoint;
CGPoint position;
CGFloat zPosition;
NSString *contentsGravity;
CGRect contentsRect;
CGRect contentsCenter;
CGFloat contentsScale;
CGFloat rasterizationScale;
CATransform3D transform;
CATransform3D sublayerTransform;
CGColorRef shadowColor;
Expand Down Expand Up @@ -176,7 +184,11 @@ ASDISPLAYNODE_INLINE void ASPendingStateApplyMetricsToLayer(_ASPendingState *sta
@synthesize anchorPoint=anchorPoint;
@synthesize position=position;
@synthesize zPosition=zPosition;
@synthesize contentsGravity=contentsGravity;
@synthesize contentsRect=contentsRect;
@synthesize contentsCenter=contentsCenter;
@synthesize contentsScale=contentsScale;
@synthesize rasterizationScale=rasterizationScale;
@synthesize transform=transform;
@synthesize sublayerTransform=sublayerTransform;
@synthesize userInteractionEnabled=userInteractionEnabled;
Expand Down Expand Up @@ -827,9 +839,6 @@ - (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)specialPr
if (flags.setBounds)
view.bounds = bounds;

if (flags.setContentsScale)
layer.contentsScale = contentsScale;

if (flags.setTransform)
layer.transform = transform;

Expand All @@ -839,6 +848,21 @@ - (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)specialPr
if (flags.setContents)
layer.contents = contents;

if (flags.setContentsGravity)
layer.contentsGravity = contentsGravity;

if (flags.setContentsRect)
layer.contentsRect = contentsRect;

if (flags.setContentsCenter)
layer.contentsCenter = contentsCenter;

if (flags.setContentsScale)
layer.contentsScale = contentsScale;

if (flags.setRasterizationScale)
layer.rasterizationScale = rasterizationScale;

if (flags.setClipsToBounds)
view.clipsToBounds = clipsToBounds;

Expand Down Expand Up @@ -1037,10 +1061,14 @@ + (_ASPendingState *)pendingViewStateFromView:(UIView *)view
pendingState.position = layer.position;
pendingState.zPosition = layer.zPosition;
pendingState.bounds = view.bounds;
pendingState.contentsScale = layer.contentsScale;
pendingState.transform = layer.transform;
pendingState.sublayerTransform = layer.sublayerTransform;
pendingState.contents = layer.contents;
pendingState.contentsGravity = layer.contentsGravity;
pendingState.contentsRect = layer.contentsRect;
pendingState.contentsCenter = layer.contentsCenter;
pendingState.contentsScale = layer.contentsScale;
pendingState.rasterizationScale = layer.rasterizationScale;
pendingState.clipsToBounds = view.clipsToBounds;
pendingState.backgroundColor = layer.backgroundColor;
pendingState.tintColor = view.tintColor;
Expand Down

0 comments on commit bc361ca

Please sign in to comment.