mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-05 20:22:15 +00:00
Optimize drawing code + add examples how to round corners (#996)
* Use CoreGraphics for drawing and cropping of node content * Smaller fixes
This commit is contained in:
parent
4880b54db0
commit
eb4c21c545
@ -109,7 +109,7 @@
|
||||
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; };
|
||||
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */; };
|
||||
509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */; };
|
||||
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */; };
|
||||
636EA1A41C7FF4EC00EE152F /* NSArray+Diffing.mm in Sources */ = {isa = PBXBuildFile; fileRef = DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */; };
|
||||
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */; };
|
||||
680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@ -176,7 +176,7 @@
|
||||
7AB338671C55B3460055FDE8 /* ASRelativeLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */; };
|
||||
8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */; };
|
||||
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */; };
|
||||
81E95C141D62639600336598 /* ASTextNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */; };
|
||||
83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A7D9591D44542100BF333E /* ASWeakMap.m */; };
|
||||
83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A7D9581D44542100BF333E /* ASWeakMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
@ -621,7 +621,7 @@
|
||||
205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutController.h; sourceTree = "<group>"; };
|
||||
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewLayoutController.m; sourceTree = "<group>"; };
|
||||
205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CoreGraphics+ASConvenience.h"; sourceTree = "<group>"; };
|
||||
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CoreGraphics+ASConvenience.m"; sourceTree = "<group>"; };
|
||||
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CoreGraphics+ASConvenience.mm"; sourceTree = "<group>"; };
|
||||
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = "<group>"; };
|
||||
2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextKitTruncationTests.mm; sourceTree = "<group>"; };
|
||||
@ -734,7 +734,7 @@
|
||||
7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRelativeLayoutSpec.h; sourceTree = "<group>"; };
|
||||
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
|
||||
8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ASConvenience.h"; sourceTree = "<group>"; };
|
||||
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ASConvenience.m"; sourceTree = "<group>"; };
|
||||
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIImage+ASConvenience.mm"; sourceTree = "<group>"; };
|
||||
81E95C131D62639600336598 /* ASTextNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextNodeSnapshotTests.m; sourceTree = "<group>"; };
|
||||
81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; };
|
||||
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; };
|
||||
@ -1234,7 +1234,7 @@
|
||||
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
|
||||
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */,
|
||||
8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */,
|
||||
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */,
|
||||
8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.mm */,
|
||||
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */,
|
||||
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */,
|
||||
);
|
||||
@ -1422,7 +1422,7 @@
|
||||
CC3B20871C3F7A5400798563 /* ASWeakSet.h */,
|
||||
CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
|
||||
205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */,
|
||||
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */,
|
||||
205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.mm */,
|
||||
DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */,
|
||||
DBC452DA1C5BF64600B16017 /* NSArray+Diffing.mm */,
|
||||
CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */,
|
||||
@ -2405,7 +2405,7 @@
|
||||
CCA282C51E9EAE630037E8B7 /* ASLayerBackingTipProvider.m in Sources */,
|
||||
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */,
|
||||
B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */,
|
||||
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */,
|
||||
8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.mm in Sources */,
|
||||
CCAA0B80206ADBF30057B336 /* ASRecursiveUnfairLock.m in Sources */,
|
||||
CCBDDD0620C62A2D00CBA922 /* ASMainThreadDeallocation.mm in Sources */,
|
||||
B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */,
|
||||
@ -2504,7 +2504,7 @@
|
||||
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
|
||||
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */,
|
||||
CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */,
|
||||
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */,
|
||||
509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.mm in Sources */,
|
||||
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
|
||||
34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */,
|
||||
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,
|
||||
|
@ -26,7 +26,7 @@
|
||||
- Reduced binary size by disabling exception support (which we don't use.) [Adlai Holler](https://github.com/Adlai-Holler)
|
||||
- Create and set delegate for clip corner layers within ASDisplayNode [Michael Schneider](https://github.com/maicki) [#1029](https://github.com/TextureGroup/Texture/pull/1029)
|
||||
- Improve locking situation in ASVideoPlayerNode [Michael Schneider](https://github.com/maicki) [#1042](https://github.com/TextureGroup/Texture/pull/1042)
|
||||
|
||||
- Optimize drawing code + add examples how to round corners. [Michael Schneider](https://github.com/maicki)
|
||||
|
||||
## 2.7
|
||||
- Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877)
|
||||
|
@ -53,6 +53,7 @@
|
||||
#import <AsyncDisplayKit/ASWeakProxy.h>
|
||||
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
|
||||
#import <AsyncDisplayKit/ASTipsController.h>
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
|
||||
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
|
||||
#if TIME_DISPLAYNODE_OPS
|
||||
@ -1508,7 +1509,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
||||
__instanceLock__.lock();
|
||||
if (_placeholderLayer.superlayer && !placeholderShouldPersist) {
|
||||
void (^cleanupBlock)() = ^{
|
||||
[_placeholderLayer removeFromSuperlayer];
|
||||
[self->_placeholderLayer removeFromSuperlayer];
|
||||
};
|
||||
|
||||
if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||
@ -1666,24 +1667,29 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
CGSize size = CGSizeMake(radius + 1, radius + 1);
|
||||
ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay);
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (isRight == YES) {
|
||||
CGContextTranslateCTM(ctx, -radius + 1, 0);
|
||||
CGContextTranslateCTM(context, -radius + 1, 0);
|
||||
}
|
||||
if (isTop == YES) {
|
||||
CGContextTranslateCTM(ctx, 0, -radius + 1);
|
||||
CGContextTranslateCTM(context, 0, -radius + 1);
|
||||
}
|
||||
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, radius * 2, radius * 2) cornerRadius:radius];
|
||||
[roundedRect setUsesEvenOddFillRule:YES];
|
||||
[roundedRect appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1)]];
|
||||
[backgroundColor setFill];
|
||||
[roundedRect fill];
|
||||
|
||||
|
||||
CGMutablePathRef roundedPath = CGPathCreateMutable();
|
||||
CGPathRef addedPath = ASCGRoundedPathCreate(CGRectMake(0, 0, radius * 2, radius * 2), radius);
|
||||
CGPathAddPath(roundedPath, NULL, addedPath);
|
||||
CGPathAddRect(roundedPath, NULL, CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1));
|
||||
CGContextAddPath(context, roundedPath);
|
||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||
CGContextEOFillPath(context);
|
||||
|
||||
// No lock needed, as _clipCornerLayers is only modified on the main thread.
|
||||
CALayer *clipCornerLayer = _clipCornerLayers[idx];
|
||||
clipCornerLayer.contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage);
|
||||
clipCornerLayer.bounds = CGRectMake(0.0, 0.0, size.width, size.height);
|
||||
clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
|
||||
_clipCornerLayers[idx].contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage);
|
||||
_clipCornerLayers[idx].bounds = CGRectMake(0.0, 0.0, size.width, size.height);
|
||||
_clipCornerLayers[idx].anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
|
||||
|
||||
CGPathRelease(addedPath);
|
||||
CGPathRelease(roundedPath);
|
||||
}
|
||||
[self _layoutClipCornersIfNeeded];
|
||||
});
|
||||
@ -1868,7 +1874,10 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
[self displayDidFinish];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
- (void)displayWillStart {}
|
||||
#pragma clang diagnostic pop
|
||||
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@ -2979,11 +2988,11 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
||||
if (ASInterfaceStateIncludesVisible(self.pendingInterfaceState)) {
|
||||
void(^exitVisibleInterfaceState)(void) = ^{
|
||||
// This block intentionally retains self.
|
||||
__instanceLock__.lock();
|
||||
unsigned isStillInHierarchy = _flags.isInHierarchy;
|
||||
BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState);
|
||||
ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible);
|
||||
__instanceLock__.unlock();
|
||||
self->__instanceLock__.lock();
|
||||
unsigned isStillInHierarchy = self->_flags.isInHierarchy;
|
||||
BOOL isVisible = ASInterfaceStateIncludesVisible(self->_pendingInterfaceState);
|
||||
ASInterfaceState newState = (self->_pendingInterfaceState & ~ASInterfaceStateVisible);
|
||||
self->__instanceLock__.unlock();
|
||||
if (!isStillInHierarchy && isVisible) {
|
||||
#if ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR
|
||||
if (![self supportsRangeManagedInterfaceState]) {
|
||||
@ -3142,8 +3151,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
||||
[self setDisplaySuspended:YES];
|
||||
//schedule clear contents on next runloop
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) {
|
||||
ASDN::MutexLocker l(self->__instanceLock__);
|
||||
if (ASInterfaceStateIncludesDisplay(self->_interfaceState) == NO) {
|
||||
[self clearContents];
|
||||
}
|
||||
});
|
||||
@ -3160,8 +3169,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
||||
[[self asyncLayer] cancelAsyncDisplay];
|
||||
//schedule clear contents on next runloop
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) {
|
||||
ASDN::MutexLocker l(self->__instanceLock__);
|
||||
if (ASInterfaceStateIncludesDisplay(self->_interfaceState) == NO) {
|
||||
[self clearContents];
|
||||
}
|
||||
});
|
||||
|
@ -737,19 +737,43 @@ asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat
|
||||
{
|
||||
return ^(UIImage *originalImage) {
|
||||
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
||||
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];
|
||||
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGRect rect = (CGRect){CGPointZero, originalImage.size};
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
|
||||
CGPathAddEllipseInRect(path, NULL, rect);
|
||||
CGContextAddPath(context, path);
|
||||
|
||||
// Make the image round
|
||||
[roundOutline addClip];
|
||||
CGContextClip(context);
|
||||
|
||||
// Although drawAtPoint:blendMode: would consider the CTM already, we are using CGContext* functions for drawing
|
||||
// the image instead calling drawAtPoint:blendMode. This will save use 50% of retain calls for the image
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
CGContextTranslateCTM(context, 0, CGRectGetMaxY(rect) + CGRectGetMinY(rect));
|
||||
CGContextScaleCTM(context, originalImage.scale, -originalImage.scale);
|
||||
CGContextSetAlpha(context, 1.0);
|
||||
CGContextDrawImage(context, rect, originalImage.CGImage);
|
||||
|
||||
// Draw the original image
|
||||
[originalImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
|
||||
CGPathRelease(path);
|
||||
|
||||
// Draw a border on top.
|
||||
if (borderWidth > 0.0) {
|
||||
[borderColor setStroke];
|
||||
[roundOutline setLineWidth:borderWidth];
|
||||
[roundOutline stroke];
|
||||
// Begin a new path for the border
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGFloat strokeThickness = borderWidth;
|
||||
CGFloat strokeInset = floor((strokeThickness + 1.0f) / 2.0f) - 1.0f;
|
||||
CGPathRef path = CGPathCreateWithEllipseInRect(CGRectInset(rect, strokeInset, strokeInset), NULL);
|
||||
CGContextAddPath(context, path);
|
||||
|
||||
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
|
||||
CGContextSetLineWidth(context, borderWidth);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGPathRelease(path);
|
||||
}
|
||||
|
||||
return ASGraphicsGetImageAndEndCurrentContext();
|
||||
|
@ -130,7 +130,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ASOVERLOADABLE __attribute__((overloadable))
|
||||
#define AS_OVERLOADABLE __attribute__((overloadable))
|
||||
|
||||
|
||||
#if __has_attribute(noescape)
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <tgmath.h>
|
||||
#import <UIKit/UIBezierPath.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
@ -56,4 +56,11 @@ ASDISPLAYNODE_INLINE BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CG
|
||||
return fabs(size1.width - size2.width) < delta && fabs(size1.height - size2.height) < delta;
|
||||
};
|
||||
|
||||
AS_OVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN CGPathRef ASCGRoundedPathCreate(CGRect rect, UIRectCorner corners, CGSize cornerRadii);
|
||||
|
||||
AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGPathRef ASCGRoundedPathCreate(CGRect rect, CGFloat cornerRadius) {
|
||||
return ASCGRoundedPathCreate(rect, UIRectCornerAllCorners, CGSizeMake(cornerRadius, cornerRadius));
|
||||
}
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -1,19 +0,0 @@
|
||||
//
|
||||
// CoreGraphics+ASConvenience.m
|
||||
// Texture
|
||||
//
|
||||
// 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 /ASDK-Licenses directory of this source tree. An additional
|
||||
// grant of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
|
64
Source/Details/CoreGraphics+ASConvenience.mm
Normal file
64
Source/Details/CoreGraphics+ASConvenience.mm
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// CoreGraphics+ASConvenience.m
|
||||
// Texture
|
||||
//
|
||||
// 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 /ASDK-Licenses directory of this source tree. An additional
|
||||
// grant of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
|
||||
AS_OVERLOADABLE CGPathRef ASCGRoundedPathCreate(CGRect rect, UIRectCorner corners, CGSize cornerRadii) {
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
|
||||
const CGPoint topLeft = rect.origin;
|
||||
const CGPoint topRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
|
||||
const CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
|
||||
const CGPoint bottomLeft = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
|
||||
|
||||
if (corners & UIRectCornerTopLeft) {
|
||||
CGPathMoveToPoint(path, NULL, topLeft.x+cornerRadii.width, topLeft.y);
|
||||
} else {
|
||||
CGPathMoveToPoint(path, NULL, topLeft.x, topLeft.y);
|
||||
}
|
||||
|
||||
if (corners & UIRectCornerTopRight) {
|
||||
CGPathAddLineToPoint(path, NULL, topRight.x-cornerRadii.width, topRight.y);
|
||||
CGPathAddCurveToPoint(path, NULL, topRight.x, topRight.y, topRight.x, topRight.y+cornerRadii.height, topRight.x, topRight.y+cornerRadii.height);
|
||||
} else {
|
||||
CGPathAddLineToPoint(path, NULL, topRight.x, topRight.y);
|
||||
}
|
||||
|
||||
if (corners & UIRectCornerBottomRight) {
|
||||
CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y-cornerRadii.height);
|
||||
CGPathAddCurveToPoint(path, NULL, bottomRight.x, bottomRight.y, bottomRight.x-cornerRadii.width, bottomRight.y, bottomRight.x-cornerRadii.width, bottomRight.y);
|
||||
} else {
|
||||
CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y);
|
||||
}
|
||||
|
||||
if (corners & UIRectCornerBottomLeft) {
|
||||
CGPathAddLineToPoint(path, NULL, bottomLeft.x+cornerRadii.width, bottomLeft.y);
|
||||
CGPathAddCurveToPoint(path, NULL, bottomLeft.x, bottomLeft.y, bottomLeft.x, bottomLeft.y-cornerRadii.height, bottomLeft.x, bottomLeft.y-cornerRadii.height);
|
||||
} else {
|
||||
CGPathAddLineToPoint(path, NULL, bottomLeft.x, bottomLeft.y);
|
||||
}
|
||||
|
||||
if (corners & UIRectCornerTopLeft) {
|
||||
CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y+cornerRadii.height);
|
||||
CGPathAddCurveToPoint(path, NULL, topLeft.x, topLeft.y, topLeft.x+cornerRadii.width, topLeft.y, topLeft.x+cornerRadii.width, topLeft.y);
|
||||
} else {
|
||||
CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y);
|
||||
}
|
||||
|
||||
CGPathCloseSubpath(path);
|
||||
return path;
|
||||
}
|
@ -94,7 +94,7 @@ AS_EXTERN ASDimension const ASDimensionAuto;
|
||||
/**
|
||||
* Returns a dimension with the specified type and value.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value)
|
||||
AS_OVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value)
|
||||
{
|
||||
if (unit == ASDimensionUnitAuto ) {
|
||||
ASDisplayNodeCAssert(value == 0, @"ASDimension auto value must be 0.");
|
||||
@ -112,7 +112,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit
|
||||
/**
|
||||
* Returns a dimension with the specified points value.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMake(CGFloat points)
|
||||
AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMake(CGFloat points)
|
||||
{
|
||||
return ASDimensionMake(ASDimensionUnitPoints, points);
|
||||
}
|
||||
@ -122,7 +122,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensio
|
||||
* Examples: ASDimensionMake(@"50%") = ASDimensionMake(ASDimensionUnitFraction, 0.5)
|
||||
* ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5)
|
||||
*/
|
||||
ASOVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN ASDimension ASDimensionMake(NSString *dimension);
|
||||
AS_OVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN ASDimension ASDimensionMake(NSString *dimension);
|
||||
|
||||
/**
|
||||
* Returns a dimension with the specified points value.
|
||||
@ -244,7 +244,7 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeHasSignificantArea(AS
|
||||
/**
|
||||
* Creates an ASSizeRange with provided min and max size.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
|
||||
AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
|
||||
{
|
||||
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
|
||||
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
|
||||
@ -263,7 +263,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRang
|
||||
/**
|
||||
* Creates an ASSizeRange with provided size as both min and max.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize exactSize)
|
||||
AS_OVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize exactSize)
|
||||
{
|
||||
return ASSizeRangeMake(exactSize, exactSize);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0};
|
||||
|
||||
ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension)
|
||||
AS_OVERLOADABLE ASDimension ASDimensionMake(NSString *dimension)
|
||||
{
|
||||
if (dimension.length > 0) {
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||
#import <AsyncDisplayKit/ASSignpost.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
|
||||
|
||||
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
|
||||
@ -111,10 +112,13 @@
|
||||
|
||||
CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);
|
||||
|
||||
//support cornerRadius
|
||||
// Support cornerRadius
|
||||
if (rasterizingFromAscendent && clipsToBounds) {
|
||||
if (cornerRadius) {
|
||||
[[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius] addClip];
|
||||
CGPathRef cornerRadiusPath = ASCGRoundedPathCreate(bounds, cornerRadius);
|
||||
CGContextAddPath(context, cornerRadiusPath);
|
||||
CGContextClip(context);
|
||||
CGPathRelease(cornerRadiusPath);
|
||||
} else {
|
||||
CGContextClipToRect(context, bounds);
|
||||
}
|
||||
@ -127,13 +131,18 @@
|
||||
CGContextFillRect(context, bounds);
|
||||
}
|
||||
|
||||
// If there is a display block, call it to get the image, then copy the image into the current context (which is the rasterized container's backing store).
|
||||
// If there is a display block, call it to get the image, then copy the image into the current context (which
|
||||
// is the rasterized container's backing store).
|
||||
if (displayBlock) {
|
||||
UIImage *image = (UIImage *)displayBlock();
|
||||
if (image) {
|
||||
BOOL opaque = ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage));
|
||||
CGBlendMode blendMode = opaque ? kCGBlendModeCopy : kCGBlendModeNormal;
|
||||
[image drawInRect:bounds blendMode:blendMode alpha:1];
|
||||
CGContextSetBlendMode(context, blendMode);
|
||||
CGContextTranslateCTM(context, 0, CGRectGetMaxY(bounds) + CGRectGetMinY(bounds));
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
CGContextSetAlpha(context, 1.0);
|
||||
CGContextDrawImage(context, bounds, image.CGImage);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -295,7 +304,10 @@
|
||||
ASDisplayNodeAssert(context == UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self);
|
||||
// TODO: This clip path should be removed if we are rasterizing.
|
||||
CGRect boundingBox = CGContextGetClipBoundingBox(context);
|
||||
[[UIBezierPath bezierPathWithRoundedRect:boundingBox cornerRadius:cornerRadius] addClip];
|
||||
CGPathRef cornerRadiusPath = ASCGRoundedPathCreate(boundingBox, cornerRadius);
|
||||
CGContextAddPath(context, cornerRadiusPath);
|
||||
CGContextClip(context);
|
||||
CGPathRelease(cornerRadiusPath);
|
||||
}
|
||||
|
||||
if (willDisplayNodeContentWithRenderingContext) {
|
||||
@ -332,34 +344,58 @@
|
||||
CGFloat white = 0.0f, alpha = 0.0f;
|
||||
[backgroundColor getWhite:&white alpha:&alpha];
|
||||
ASGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale);
|
||||
context = UIGraphicsGetCurrentContext();
|
||||
[*image drawInRect:bounds];
|
||||
} else {
|
||||
bounds = CGContextGetClipBoundingBox(context);
|
||||
}
|
||||
|
||||
ASDisplayNodeAssert(UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self);
|
||||
|
||||
CGContextSaveGState(context);
|
||||
|
||||
UIBezierPath *roundedHole = [UIBezierPath bezierPathWithRect:bounds];
|
||||
[roundedHole appendPath:[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius * contentsScale]];
|
||||
roundedHole.usesEvenOddFillRule = YES;
|
||||
|
||||
UIBezierPath *roundedPath = nil;
|
||||
if (borderWidth > 0.0f) { // Don't create roundedPath and stroke if borderWidth is 0.0
|
||||
CGFloat strokeThickness = borderWidth * contentsScale;
|
||||
CGFloat strokeInset = ((strokeThickness + 1.0f) / 2.0f) - 1.0f;
|
||||
roundedPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(bounds, strokeInset, strokeInset)
|
||||
cornerRadius:_cornerRadius * contentsScale];
|
||||
roundedPath.lineWidth = strokeThickness;
|
||||
[[UIColor colorWithCGColor:borderColor] setStroke];
|
||||
}
|
||||
CGMutablePathRef roundedHole = CGPathCreateMutable();
|
||||
CGPathAddRect(roundedHole, NULL, bounds);
|
||||
|
||||
CGPathRef additionalPath = ASCGRoundedPathCreate(bounds, cornerRadius * contentsScale);
|
||||
CGPathAddPath(roundedHole, NULL, additionalPath);
|
||||
|
||||
CGContextAddPath(context, roundedHole);
|
||||
|
||||
// Punch out the corners by copying the backgroundColor over them.
|
||||
// This works for everything from clearColor to opaque colors.
|
||||
[backgroundColor setFill];
|
||||
[roundedHole fillWithBlendMode:kCGBlendModeCopy alpha:1.0f];
|
||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||
|
||||
[roundedPath stroke]; // Won't do anything if borderWidth is 0 and roundedPath is nil.
|
||||
CGContextSetAlpha(context, 1.0);
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
CGContextEOFillPath(context);
|
||||
|
||||
CGPathRelease(additionalPath);
|
||||
CGPathRelease(roundedHole);
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
// Drawing borders with ASCornerRoundingTypePrecomposited set has some problems at the moment. If the borderWidth is
|
||||
// set, besides we are drawing the border with the given corner radius, the CALayer also picks up the borderWidth
|
||||
// value and draws the border without the cornerRadius.
|
||||
if (borderWidth > 0.0f) { // Don't create roundedPath and stroke if borderWidth is 0.0
|
||||
CGContextSaveGState(context);
|
||||
|
||||
CGFloat strokeThickness = borderWidth * contentsScale;
|
||||
CGFloat strokeInset = ((strokeThickness + 1.0f) / 2.0f) - 1.0f;
|
||||
CGPathRef roundedPath = ASCGRoundedPathCreate(CGRectInset(bounds, strokeInset, strokeInset), _cornerRadius * contentsScale);
|
||||
CGContextAddPath(context, roundedPath);
|
||||
|
||||
CGContextSetLineWidth(context, strokeThickness);
|
||||
CGContextSetStrokeColorWithColor(context, borderColor);
|
||||
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGPathRelease(roundedPath);
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
if (*image) {
|
||||
*image = ASGraphicsGetImageAndEndCurrentContext();
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
|
||||
#pragma mark - ASDKFastImageNamed
|
||||
|
||||
@ -114,9 +115,9 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio
|
||||
// UIBezierPath objects are fairly small and these are equally sized. 20 should be plenty for many different parameters.
|
||||
__pathCache.countLimit = 20;
|
||||
});
|
||||
|
||||
|
||||
// Treat clear background color as no background color
|
||||
if ([cornerColor isEqual:[UIColor clearColor]]) {
|
||||
if (CGColorGetAlpha(cornerColor.CGColor) == 0) {
|
||||
cornerColor = nil;
|
||||
}
|
||||
|
||||
@ -140,33 +141,46 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio
|
||||
// We should probably check if the background color has any alpha component but that
|
||||
// might be expensive due to needing to check mulitple color spaces.
|
||||
ASGraphicsBeginImageContextWithOptions(bounds.size, cornerColor != nil, scale);
|
||||
|
||||
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
// Draw Corners
|
||||
BOOL contextIsClean = YES;
|
||||
if (cornerColor) {
|
||||
contextIsClean = NO;
|
||||
[cornerColor setFill];
|
||||
|
||||
CGContextSetFillColorWithColor(context, cornerColor.CGColor);
|
||||
// Copy "blend" mode is extra fast because it disregards any value currently in the buffer and overrides directly.
|
||||
UIRectFillUsingBlendMode(bounds, kCGBlendModeCopy);
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
CGContextFillRect(context, bounds);
|
||||
}
|
||||
|
||||
|
||||
// Draw fill
|
||||
BOOL canUseCopy = contextIsClean || (CGColorGetAlpha(fillColor.CGColor) == 1);
|
||||
[fillColor setFill];
|
||||
[path fillWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1];
|
||||
|
||||
CGContextSetFillColorWithColor(context, fillColor.CGColor);
|
||||
CGContextSetBlendMode(context, canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal);
|
||||
CGContextSetAlpha(context, 1.0);
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
// Add a border
|
||||
if (borderColor) {
|
||||
[borderColor setStroke];
|
||||
|
||||
// Inset border fully inside filled path (not halfway on each side of path)
|
||||
CGRect strokeRect = CGRectInset(bounds, borderWidth / 2.0, borderWidth / 2.0);
|
||||
|
||||
|
||||
// It is rarer to have a stroke path, and our cache key only handles rounded rects for the exact-stretchable
|
||||
// size calculated by cornerRadius, so we won't bother caching this path. Profiling validates this decision.
|
||||
UIBezierPath *strokePath = [UIBezierPath bezierPathWithRoundedRect:strokeRect
|
||||
byRoundingCorners:roundedCorners
|
||||
cornerRadii:cornerRadii];
|
||||
[strokePath setLineWidth:borderWidth];
|
||||
CGPathRef strokePath = ASCGRoundedPathCreate(strokeRect, roundedCorners, cornerRadii);
|
||||
|
||||
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
|
||||
CGContextSetLineWidth(context, borderWidth);
|
||||
CGContextSetAlpha(context, 1.0);
|
||||
BOOL canUseCopy = (CGColorGetAlpha(borderColor.CGColor) == 1);
|
||||
[strokePath strokeWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1];
|
||||
CGContextSetBlendMode(context, (canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal));
|
||||
CGContextAddPath(context, strokePath);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGPathRelease(strokePath);
|
||||
}
|
||||
|
||||
UIImage *result = ASGraphicsGetImageAndEndCurrentContext();
|
@ -83,7 +83,9 @@
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 12.0;
|
||||
layer.shadowOpacity = 0.45;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
CGPathRef shadowPath = CGPathCreateWithRect(self.layer.bounds, NULL);
|
||||
layer.shadowPath = shadowPath;
|
||||
CGPathRelease(shadowPath);
|
||||
}
|
||||
|
||||
- (void)setDefaultFocusAppearance
|
||||
@ -93,7 +95,9 @@
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 0;
|
||||
layer.shadowOpacity = 0;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
CGPathRef shadowPath = CGPathCreateWithRect(self.layer.bounds, NULL);
|
||||
layer.shadowPath = shadowPath;
|
||||
CGPathRelease(shadowPath);
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
|
||||
}
|
||||
@end
|
||||
|
@ -169,7 +169,9 @@
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 12.0;
|
||||
layer.shadowOpacity = 0.45;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
CGPathRef shadowPath = CGPathCreateWithRect(self.layer.bounds, NULL);
|
||||
layer.shadowPath = shadowPath;
|
||||
CGPathRelease(shadowPath);
|
||||
view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25);
|
||||
}
|
||||
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -79,8 +79,8 @@
|
||||
_photoImageNode.delegate = self;
|
||||
_photoImageNode.URL = photo.URL;
|
||||
_photoImageNode.layerBacked = YES;
|
||||
|
||||
_userNameLabel = [[ASTextNode alloc] init];
|
||||
|
||||
_userNameLabel = [[ASTextNode alloc] init];
|
||||
_userNameLabel.attributedText = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
|
||||
|
||||
_photoLocationLabel = [[ASTextNode alloc] init];
|
||||
|
Loading…
x
Reference in New Issue
Block a user