mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-10 00:01:44 +00:00
no message
This commit is contained in:
parent
a085123ec1
commit
42f860600e
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
<<<<<<< HEAD
|
||||
LastUpgradeVersion = "0900"
|
||||
=======
|
||||
LastUpgradeVersion = "0940"
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B35061D91B010EDF0018CF92"
|
||||
BuildableName = "AsyncDisplayKit.framework"
|
||||
BlueprintName = "AsyncDisplayKit"
|
||||
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "058D09BB195D04C000B7D73C"
|
||||
BuildableName = "AsyncDisplayKitTests.xctest"
|
||||
BlueprintName = "AsyncDisplayKitTests"
|
||||
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
<Test
|
||||
Identifier = "ASTextNodePerformanceTests">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B35061D91B010EDF0018CF92"
|
||||
BuildableName = "AsyncDisplayKit.framework"
|
||||
BlueprintName = "AsyncDisplayKit"
|
||||
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B35061D91B010EDF0018CF92"
|
||||
BuildableName = "AsyncDisplayKit.framework"
|
||||
BlueprintName = "AsyncDisplayKit"
|
||||
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "FB_REFERENCE_IMAGE_DIR"
|
||||
value = "$(SOURCE_ROOT)/Tests/ReferenceImages"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B35061D91B010EDF0018CF92"
|
||||
BuildableName = "AsyncDisplayKit.framework"
|
||||
BlueprintName = "AsyncDisplayKit"
|
||||
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
3927
Source/ASDisplayNode.mm.orig
Normal file
3927
Source/ASDisplayNode.mm.orig
Normal file
File diff suppressed because it is too large
Load Diff
221
Source/ASEditableTextNode.h.orig
Normal file
221
Source/ASEditableTextNode.h.orig
Normal file
@ -0,0 +1,221 @@
|
||||
//
|
||||
// ASEditableTextNode.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol ASEditableTextNodeDelegate;
|
||||
@class ASTextKitComponents;
|
||||
|
||||
@interface ASEditableTextNodeTargetForAction: NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) id _Nullable target;
|
||||
|
||||
- (instancetype)initWithTarget:(id _Nullable)target;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
@abstract Implements a node that supports text editing.
|
||||
@discussion Does not support layer backing.
|
||||
*/
|
||||
@interface ASEditableTextNode : ASDisplayNode <UITextInputTraits>
|
||||
|
||||
/**
|
||||
* @abstract Initializes an editable text node using default TextKit components.
|
||||
*
|
||||
* @return An initialized ASEditableTextNode.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* @abstract Initializes an editable text node using the provided TextKit components.
|
||||
*
|
||||
* @param textKitComponents The TextKit stack used to render text.
|
||||
* @param placeholderTextKitComponents The TextKit stack used to render placeholder text.
|
||||
*
|
||||
* @return An initialized ASEditableTextNode.
|
||||
*/
|
||||
- (instancetype)initWithTextKitComponents:(ASTextKitComponents *)textKitComponents
|
||||
placeholderTextKitComponents:(ASTextKitComponents *)placeholderTextKitComponents;
|
||||
|
||||
//! @abstract The text node's delegate, which must conform to the <ASEditableTextNodeDelegate> protocol.
|
||||
@property (nullable, weak) id <ASEditableTextNodeDelegate> delegate;
|
||||
|
||||
#pragma mark - Configuration
|
||||
|
||||
/**
|
||||
@abstract Enable scrolling on the textView
|
||||
@default true
|
||||
*/
|
||||
@property (nonatomic) BOOL scrollEnabled;
|
||||
|
||||
/**
|
||||
@abstract Access to underlying UITextView for more configuration options.
|
||||
@warning This property should only be used on the main thread and should not be accessed before the editable text node's view is created.
|
||||
*/
|
||||
@property (nonatomic, readonly) UITextView *textView;
|
||||
|
||||
//! @abstract The attributes to apply to new text being entered by the user.
|
||||
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id> *typingAttributes;
|
||||
|
||||
//! @abstract The range of text currently selected. If length is zero, the range is the cursor location.
|
||||
@property NSRange selectedRange;
|
||||
|
||||
#pragma mark - Placeholder
|
||||
/**
|
||||
@abstract Indicates if the receiver is displaying the placeholder text.
|
||||
@discussion To update the placeholder, see the <attributedPlaceholderText> property.
|
||||
@result YES if the placeholder is currently displayed; NO otherwise.
|
||||
*/
|
||||
- (BOOL)isDisplayingPlaceholder AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
@abstract The styled placeholder text displayed by the text node while no text is entered
|
||||
@discussion The placeholder is displayed when the user has not entered any text and the keyboard is not visible.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *attributedPlaceholderText;
|
||||
|
||||
#pragma mark - Modifying User Text
|
||||
/**
|
||||
@abstract The styled text displayed by the receiver.
|
||||
@discussion When the placeholder is displayed (as indicated by -isDisplayingPlaceholder), this value is nil. Otherwise, this value is the attributed text the user has entered. This value can be modified regardless of whether the receiver is the first responder (and thus, editing) or not. Changing this value from nil to non-nil will result in the placeholder being hidden, and the new value being displayed.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;
|
||||
|
||||
#pragma mark - Managing The Keyboard
|
||||
//! @abstract The text input mode used by the receiver's keyboard, if it is visible. This value is undefined if the receiver is not the first responder.
|
||||
@property (nonatomic, readonly) UITextInputMode *textInputMode;
|
||||
|
||||
/**
|
||||
@abstract The textContainerInset of both the placeholder and typed textView. This value defaults to UIEdgeInsetsZero.
|
||||
*/
|
||||
@property (nonatomic) UIEdgeInsets textContainerInset;
|
||||
|
||||
/**
|
||||
@abstract The maximum number of lines to display. Additional lines will require scrolling.
|
||||
@default 0 (No limit)
|
||||
*/
|
||||
@property (nonatomic) NSUInteger maximumLinesToDisplay;
|
||||
|
||||
/**
|
||||
@abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user.
|
||||
@result YES if the receiver's text view is the first-responder; NO otherwise.
|
||||
*/
|
||||
- (BOOL)isFirstResponder AS_WARN_UNUSED_RESULT;
|
||||
|
||||
//! @abstract Makes the receiver's text view the first responder.
|
||||
- (BOOL)becomeFirstResponder;
|
||||
|
||||
//! @abstract Resigns the receiver's text view from first-responder status, if it has it.
|
||||
- (BOOL)resignFirstResponder;
|
||||
|
||||
#pragma mark - Geometry
|
||||
/**
|
||||
@abstract Returns the frame of the given range of characters.
|
||||
@param textRange A range of characters.
|
||||
@discussion This method raises an exception if `textRange` is not a valid range of characters within the receiver's attributed text.
|
||||
@result A CGRect that is the bounding box of the glyphs covered by the given range of characters, in the coordinate system of the receiver.
|
||||
*/
|
||||
- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
@abstract <UITextInputTraits> properties.
|
||||
*/
|
||||
@property (nonatomic) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences
|
||||
@property (nonatomic) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault
|
||||
@property (nonatomic) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault;
|
||||
@property (nonatomic) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault
|
||||
@property (nonatomic) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault
|
||||
@property (nonatomic) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum)
|
||||
@property (nonatomic) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents)
|
||||
@property (nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO
|
||||
|
||||
- (void)dropAutocorrection;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@interface ASEditableTextNode (Unavailable)
|
||||
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
/**
|
||||
* The methods declared by the ASEditableTextNodeDelegate protocol allow the adopting delegate to
|
||||
* respond to notifications such as began and finished editing, selection changed and text updated;
|
||||
* and manage whether a specified text should be replaced.
|
||||
*/
|
||||
@protocol ASEditableTextNodeDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/**
|
||||
@abstract Asks the delegate if editing should begin for the text node.
|
||||
@param editableTextNode An editable text node.
|
||||
@discussion YES if editing should begin; NO if editing should not begin -- the default returns YES.
|
||||
*/
|
||||
- (BOOL)editableTextNodeShouldBeginEditing:(ASEditableTextNode *)editableTextNode;
|
||||
|
||||
/**
|
||||
@abstract Indicates to the delegate that the text node began editing.
|
||||
@param editableTextNode An editable text node.
|
||||
@discussion The invocation of this method coincides with the keyboard animating to become visible.
|
||||
*/
|
||||
- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode;
|
||||
|
||||
/**
|
||||
@abstract Asks the delegate whether the specified text should be replaced in the editable text node.
|
||||
@param editableTextNode An editable text node.
|
||||
@param range The current selection range. If the length of the range is 0, range reflects the current insertion point. If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character.
|
||||
@param text The text to insert.
|
||||
@discussion YES if the old text should be replaced by the new text; NO if the replacement operation should be aborted.
|
||||
@result The text node calls this method whenever the user types a new character or deletes an existing character. Implementation of this method is optional -- the default implementation returns YES.
|
||||
*/
|
||||
- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
|
||||
|
||||
/**
|
||||
@abstract Indicates to the delegate that the text node's selection has changed.
|
||||
@param editableTextNode An editable text node.
|
||||
@param fromSelectedRange The previously selected range.
|
||||
@param toSelectedRange The current selected range. Equivalent to the <selectedRange> property.
|
||||
@param dueToEditing YES if the selection change was due to editing; NO otherwise.
|
||||
@discussion You can access the selection of the receiver via <selectedRange>.
|
||||
*/
|
||||
- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing;
|
||||
|
||||
/**
|
||||
@abstract Indicates to the delegate that the text node's text was updated.
|
||||
@param editableTextNode An editable text node.
|
||||
@discussion This method is called each time the user updated the text node's text. It is not called for programmatic changes made to the text via the <attributedText> property.
|
||||
*/
|
||||
- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode;
|
||||
|
||||
/**
|
||||
@abstract Indicates to the delegate that the text node has finished editing.
|
||||
@param editableTextNode An editable text node.
|
||||
@discussion The invocation of this method coincides with the keyboard animating to become hidden.
|
||||
*/
|
||||
- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode;
|
||||
|
||||
<<<<<<< HEAD
|
||||
- (BOOL)editableTextNodeShouldPaste:(ASEditableTextNode *)editableTextNode;
|
||||
- (ASEditableTextNodeTargetForAction * _Nullable)editableTextNodeTargetForAction:(SEL)action;
|
||||
- (BOOL)editableTextNodeShouldReturn:(ASEditableTextNode *)editableTextNode;
|
||||
|
||||
=======
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -257,7 +257,7 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
|
||||
if (_displayWithoutProcessing && ASDisplayNodeThreadIsMain()) {
|
||||
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
|
||||
if (stretchable) {
|
||||
ASDisplayNodeSetupLayerContentsWithResizableImage(self.layer, image);
|
||||
ASDisplayNodeSetResizableContents(self, image);
|
||||
} else {
|
||||
self.contents = (id)image.CGImage;
|
||||
}
|
||||
|
||||
791
Source/ASImageNode.mm.orig
Normal file
791
Source/ASImageNode.mm.orig
Normal file
@ -0,0 +1,791 @@
|
||||
//
|
||||
// ASImageNode.mm
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASImageNode.h>
|
||||
|
||||
#import <tgmath.h>
|
||||
|
||||
#import <AsyncDisplayKit/_ASDisplayLayer.h>
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
|
||||
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASTextNode.h>
|
||||
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
|
||||
#import <AsyncDisplayKit/ASImageNode+CGExtras.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||
#import <AsyncDisplayKit/ASEqualityHelpers.h>
|
||||
#import <AsyncDisplayKit/ASHashing.h>
|
||||
#import <AsyncDisplayKit/ASWeakMap.h>
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
#import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
|
||||
|
||||
// TODO: It would be nice to remove this dependency; it's the only subclass using more than +FrameworkSubclasses.h
|
||||
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
|
||||
|
||||
static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
||||
|
||||
typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
|
||||
|
||||
@interface ASImageNodeDrawParameters : NSObject {
|
||||
@package
|
||||
UIImage *_image;
|
||||
BOOL _opaque;
|
||||
CGRect _bounds;
|
||||
CGFloat _contentsScale;
|
||||
UIColor *_backgroundColor;
|
||||
UIViewContentMode _contentMode;
|
||||
BOOL _cropEnabled;
|
||||
BOOL _forceUpscaling;
|
||||
CGSize _forcedSize;
|
||||
CGRect _cropRect;
|
||||
CGRect _cropDisplayBounds;
|
||||
asimagenode_modification_block_t _imageModificationBlock;
|
||||
ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext;
|
||||
ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext;
|
||||
ASImageNodeDrawParametersBlock _didDrawBlock;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASImageNodeDrawParameters
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Contains all data that is needed to generate the content bitmap.
|
||||
*/
|
||||
@interface ASImageNodeContentsKey : NSObject
|
||||
|
||||
@property (nonatomic) UIImage *image;
|
||||
@property CGSize backingSize;
|
||||
@property CGRect imageDrawRect;
|
||||
@property BOOL isOpaque;
|
||||
@property (nonatomic, copy) UIColor *backgroundColor;
|
||||
@property (nonatomic) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext;
|
||||
@property (nonatomic) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
|
||||
@property (nonatomic) asimagenode_modification_block_t imageModificationBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASImageNodeContentsKey
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Optimization opportunity: The `isKindOfClass` call here could be avoided by not using the NSObject `isEqual:`
|
||||
// convention and instead using a custom comparison function that assumes all items are heterogeneous.
|
||||
// However, profiling shows that our entire `isKindOfClass` expression is only ~1/40th of the total
|
||||
// overheard of our caching, so it's likely not high-impact.
|
||||
if ([object isKindOfClass:[ASImageNodeContentsKey class]]) {
|
||||
ASImageNodeContentsKey *other = (ASImageNodeContentsKey *)object;
|
||||
return [_image isEqual:other.image]
|
||||
&& CGSizeEqualToSize(_backingSize, other.backingSize)
|
||||
&& CGRectEqualToRect(_imageDrawRect, other.imageDrawRect)
|
||||
&& _isOpaque == other.isOpaque
|
||||
&& [_backgroundColor isEqual:other.backgroundColor]
|
||||
&& _willDisplayNodeContentWithRenderingContext == other.willDisplayNodeContentWithRenderingContext
|
||||
&& _didDisplayNodeContentWithRenderingContext == other.didDisplayNodeContentWithRenderingContext
|
||||
&& _imageModificationBlock == other.imageModificationBlock;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Wpadded"
|
||||
struct {
|
||||
NSUInteger imageHash;
|
||||
CGSize backingSize;
|
||||
CGRect imageDrawRect;
|
||||
NSInteger isOpaque;
|
||||
NSUInteger backgroundColorHash;
|
||||
void *willDisplayNodeContentWithRenderingContext;
|
||||
void *didDisplayNodeContentWithRenderingContext;
|
||||
void *imageModificationBlock;
|
||||
#pragma clang diagnostic pop
|
||||
} data = {
|
||||
_image.hash,
|
||||
_backingSize,
|
||||
_imageDrawRect,
|
||||
_isOpaque,
|
||||
_backgroundColor.hash,
|
||||
(void *)_willDisplayNodeContentWithRenderingContext,
|
||||
(void *)_didDisplayNodeContentWithRenderingContext,
|
||||
(void *)_imageModificationBlock
|
||||
};
|
||||
return ASHashBytes(&data, sizeof(data));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ASImageNode
|
||||
{
|
||||
@private
|
||||
UIImage *_image;
|
||||
ASWeakMapEntry *_weakCacheEntry; // Holds a reference that keeps our contents in cache.
|
||||
UIColor *_placeholderColor;
|
||||
|
||||
void (^_displayCompletionBlock)(BOOL canceled);
|
||||
|
||||
// Drawing
|
||||
ASTextNode *_debugLabelNode;
|
||||
|
||||
// Cropping.
|
||||
BOOL _cropEnabled; // Defaults to YES.
|
||||
BOOL _forceUpscaling; //Defaults to NO.
|
||||
CGSize _forcedSize; //Defaults to CGSizeZero, indicating no forced size.
|
||||
CGRect _cropRect; // Defaults to CGRectMake(0.5, 0.5, 0, 0)
|
||||
CGRect _cropDisplayBounds; // Defaults to CGRectNull
|
||||
}
|
||||
|
||||
@synthesize image = _image;
|
||||
@synthesize imageModificationBlock = _imageModificationBlock;
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
// TODO can this be removed?
|
||||
self.contentsScale = ASScreenScale();
|
||||
self.contentMode = UIViewContentModeScaleAspectFill;
|
||||
self.opaque = NO;
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
// If no backgroundColor is set to the image node and it's a subview of UITableViewCell, UITableView is setting
|
||||
// the opaque value of all subviews to YES if highlighting / selection is happening and does not set it back to the
|
||||
// initial value. With setting a explicit backgroundColor we can prevent that change.
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
_cropEnabled = YES;
|
||||
_forceUpscaling = NO;
|
||||
_cropRect = CGRectMake(0.5, 0.5, 0, 0);
|
||||
_cropDisplayBounds = CGRectNull;
|
||||
_placeholderColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||
#ifndef MINIMAL_ASDK
|
||||
_animatedImageRunLoopMode = ASAnimatedImageDefaultRunLoopMode;
|
||||
#endif
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Invalidate all components around animated images
|
||||
#ifndef MINIMAL_ASDK
|
||||
[self invalidateAnimatedImage];
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Placeholder
|
||||
|
||||
- (UIImage *)placeholderImage
|
||||
{
|
||||
// FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set.
|
||||
// This would completely eliminate the memory and performance cost of the backing store.
|
||||
CGSize size = self.calculatedSize;
|
||||
if ((size.width * size.height) < CGFLOAT_EPSILON) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
ASGraphicsBeginImageContextWithOptions(size, NO, 1);
|
||||
[self.placeholderColor setFill];
|
||||
UIRectFill(CGRectMake(0, 0, size.width, size.height));
|
||||
UIImage *image = ASGraphicsGetImageAndEndCurrentContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
let image = ASLockedSelf(_image);
|
||||
|
||||
if (image == nil) {
|
||||
return [super calculateSizeThatFits:constrainedSize];
|
||||
}
|
||||
|
||||
return image.size;
|
||||
}
|
||||
|
||||
#pragma mark - Setter / Getter
|
||||
|
||||
- (void)setImage:(UIImage *)image
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
[self _locked_setImage:image];
|
||||
}
|
||||
|
||||
- (void)_locked_setImage:(UIImage *)image
|
||||
{
|
||||
ASAssertLocked(__instanceLock__);
|
||||
if (ASObjectIsEqual(_image, image)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIImage *oldImage = _image;
|
||||
_image = image;
|
||||
|
||||
if (image != nil) {
|
||||
// We explicitly call setNeedsDisplay in this case, although we know setNeedsDisplay will be called with lock held.
|
||||
// Therefore we have to be careful in methods that are involved with setNeedsDisplay to not run into a deadlock
|
||||
[self setNeedsDisplay];
|
||||
|
||||
<<<<<<< HEAD
|
||||
if (_displayWithoutProcessing && ASDisplayNodeThreadIsMain()) {
|
||||
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
|
||||
if (stretchable) {
|
||||
ASDisplayNodeSetupLayerContentsWithResizableImage(self.layer, image);
|
||||
} else {
|
||||
self.contents = (id)image.CGImage;
|
||||
}
|
||||
return;
|
||||
=======
|
||||
// For debugging purposes we don't care about locking for now
|
||||
if ([ASImageNode shouldShowImageScalingOverlay] && _debugLabelNode == nil) {
|
||||
// do not use ASPerformBlockOnMainThread here, if it performs the block synchronously it will continue
|
||||
// holding the lock while calling addSubnode.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_debugLabelNode = [[ASTextNode alloc] init];
|
||||
_debugLabelNode.layerBacked = YES;
|
||||
[self addSubnode:_debugLabelNode];
|
||||
});
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
}
|
||||
} else {
|
||||
self.contents = nil;
|
||||
}
|
||||
|
||||
// Destruction of bigger images on the main thread can be expensive
|
||||
// and can take some time, so we dispatch onto a bg queue to
|
||||
// actually dealloc.
|
||||
CGSize oldImageSize = oldImage.size;
|
||||
BOOL shouldReleaseImageOnBackgroundThread = oldImageSize.width > kMinReleaseImageOnBackgroundSize.width
|
||||
|| oldImageSize.height > kMinReleaseImageOnBackgroundSize.height;
|
||||
if (shouldReleaseImageOnBackgroundThread) {
|
||||
ASPerformBackgroundDeallocation(&oldImage);
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)image
|
||||
{
|
||||
return ASLockedSelf(_image);
|
||||
}
|
||||
|
||||
- (UIColor *)placeholderColor
|
||||
{
|
||||
return ASLockedSelf(_placeholderColor);
|
||||
}
|
||||
|
||||
- (void)setPlaceholderColor:(UIColor *)placeholderColor
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
if (ASCompareAssignCopy(_placeholderColor, placeholderColor)) {
|
||||
_placeholderEnabled = (placeholderColor != nil);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
|
||||
ASImageNodeDrawParameters *drawParameters = [[ASImageNodeDrawParameters alloc] init];
|
||||
drawParameters->_image = _image;
|
||||
drawParameters->_bounds = [self threadSafeBounds];
|
||||
drawParameters->_opaque = self.opaque;
|
||||
drawParameters->_contentsScale = _contentsScaleForDisplay;
|
||||
drawParameters->_backgroundColor = self.backgroundColor;
|
||||
drawParameters->_contentMode = self.contentMode;
|
||||
drawParameters->_cropEnabled = _cropEnabled;
|
||||
drawParameters->_forceUpscaling = _forceUpscaling;
|
||||
drawParameters->_forcedSize = _forcedSize;
|
||||
drawParameters->_cropRect = _cropRect;
|
||||
drawParameters->_cropDisplayBounds = _cropDisplayBounds;
|
||||
drawParameters->_imageModificationBlock = _imageModificationBlock;
|
||||
drawParameters->_willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext;
|
||||
drawParameters->_didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext;
|
||||
|
||||
// Hack for now to retain the weak entry that was created while this drawing happened
|
||||
drawParameters->_didDrawBlock = ^(ASWeakMapEntry *entry){
|
||||
ASLockScopeSelf();
|
||||
_weakCacheEntry = entry;
|
||||
};
|
||||
|
||||
return drawParameters;
|
||||
}
|
||||
|
||||
+ (UIImage *)displayWithParameters:(id<NSObject>)parameter isCancelled:(NS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelled
|
||||
{
|
||||
ASImageNodeDrawParameters *drawParameter = (ASImageNodeDrawParameters *)parameter;
|
||||
|
||||
UIImage *image = drawParameter->_image;
|
||||
if (image == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (true) {
|
||||
return image;
|
||||
}
|
||||
|
||||
CGRect drawParameterBounds = drawParameter->_bounds;
|
||||
BOOL forceUpscaling = drawParameter->_forceUpscaling;
|
||||
CGSize forcedSize = drawParameter->_forcedSize;
|
||||
BOOL cropEnabled = drawParameter->_cropEnabled;
|
||||
BOOL isOpaque = drawParameter->_opaque;
|
||||
UIColor *backgroundColor = drawParameter->_backgroundColor;
|
||||
UIViewContentMode contentMode = drawParameter->_contentMode;
|
||||
CGFloat contentsScale = drawParameter->_contentsScale;
|
||||
CGRect cropDisplayBounds = drawParameter->_cropDisplayBounds;
|
||||
CGRect cropRect = drawParameter->_cropRect;
|
||||
asimagenode_modification_block_t imageModificationBlock = drawParameter->_imageModificationBlock;
|
||||
ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = drawParameter->_willDisplayNodeContentWithRenderingContext;
|
||||
ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = drawParameter->_didDisplayNodeContentWithRenderingContext;
|
||||
|
||||
BOOL hasValidCropBounds = cropEnabled && !CGRectIsEmpty(cropDisplayBounds);
|
||||
CGRect bounds = (hasValidCropBounds ? cropDisplayBounds : drawParameterBounds);
|
||||
|
||||
|
||||
ASDisplayNodeAssert(contentsScale > 0, @"invalid contentsScale at display time");
|
||||
|
||||
// if the image is resizable, bail early since the image has likely already been configured
|
||||
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
|
||||
if (stretchable) {
|
||||
if (imageModificationBlock != NULL) {
|
||||
image = imageModificationBlock(image);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
CGSize imageSize = image.size;
|
||||
CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale);
|
||||
CGSize boundsSizeInPixels = CGSizeMake(std::floor(bounds.size.width * contentsScale), std::floor(bounds.size.height * contentsScale));
|
||||
|
||||
BOOL contentModeSupported = contentMode == UIViewContentModeScaleAspectFill ||
|
||||
contentMode == UIViewContentModeScaleAspectFit ||
|
||||
contentMode == UIViewContentModeCenter;
|
||||
|
||||
CGSize backingSize = CGSizeZero;
|
||||
CGRect imageDrawRect = CGRectZero;
|
||||
|
||||
if (boundsSizeInPixels.width * contentsScale < 1.0f || boundsSizeInPixels.height * contentsScale < 1.0f ||
|
||||
imageSizeInPixels.width < 1.0f || imageSizeInPixels.height < 1.0f) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
// If we're not supposed to do any cropping, just decode image at original size
|
||||
if (!cropEnabled || !contentModeSupported || stretchable) {
|
||||
backingSize = imageSizeInPixels;
|
||||
imageDrawRect = (CGRect){.size = backingSize};
|
||||
} else {
|
||||
if (CGSizeEqualToSize(CGSizeZero, forcedSize) == NO) {
|
||||
//scale forced size
|
||||
forcedSize.width *= contentsScale;
|
||||
forcedSize.height *= contentsScale;
|
||||
}
|
||||
ASCroppedImageBackingSizeAndDrawRectInBounds(imageSizeInPixels,
|
||||
boundsSizeInPixels,
|
||||
contentMode,
|
||||
cropRect,
|
||||
forceUpscaling,
|
||||
forcedSize,
|
||||
&backingSize,
|
||||
&imageDrawRect);
|
||||
}
|
||||
|
||||
if (backingSize.width <= 0.0f || backingSize.height <= 0.0f ||
|
||||
imageDrawRect.size.width <= 0.0f || imageDrawRect.size.height <= 0.0f) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
ASImageNodeContentsKey *contentsKey = [[ASImageNodeContentsKey alloc] init];
|
||||
contentsKey.image = image;
|
||||
contentsKey.backingSize = backingSize;
|
||||
contentsKey.imageDrawRect = imageDrawRect;
|
||||
contentsKey.isOpaque = isOpaque;
|
||||
contentsKey.backgroundColor = backgroundColor;
|
||||
contentsKey.willDisplayNodeContentWithRenderingContext = willDisplayNodeContentWithRenderingContext;
|
||||
contentsKey.didDisplayNodeContentWithRenderingContext = didDisplayNodeContentWithRenderingContext;
|
||||
contentsKey.imageModificationBlock = imageModificationBlock;
|
||||
|
||||
if (isCancelled()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
ASWeakMapEntry<UIImage *> *entry = [self.class contentsForkey:contentsKey
|
||||
drawParameters:parameter
|
||||
isCancelled:isCancelled];
|
||||
// If nil, we were cancelled.
|
||||
if (entry == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (drawParameter->_didDrawBlock) {
|
||||
drawParameter->_didDrawBlock(entry);
|
||||
}
|
||||
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
static ASWeakMap<ASImageNodeContentsKey *, UIImage *> *cache = nil;
|
||||
// Allocate cacheLock on the heap to prevent destruction at app exit (https://github.com/TextureGroup/Texture/issues/136)
|
||||
static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex;
|
||||
|
||||
+ (ASWeakMapEntry *)contentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
|
||||
{
|
||||
{
|
||||
ASDN::StaticMutexLocker l(cacheLock);
|
||||
if (!cache) {
|
||||
cache = [[ASWeakMap alloc] init];
|
||||
}
|
||||
ASWeakMapEntry *entry = [cache entryForKey:key];
|
||||
if (entry != nil) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// cache miss
|
||||
UIImage *contents = [self createContentsForkey:key drawParameters:drawParameters isCancelled:isCancelled];
|
||||
if (contents == nil) { // If nil, we were cancelled
|
||||
return nil;
|
||||
}
|
||||
|
||||
{
|
||||
ASDN::StaticMutexLocker l(cacheLock);
|
||||
return [cache setObject:contents forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
+ (UIImage *)createContentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
|
||||
{
|
||||
// The following `ASGraphicsBeginImageContextWithOptions` call will sometimes take take longer than 5ms on an
|
||||
// A5 processor for a 400x800 backingSize.
|
||||
// Check for cancellation before we call it.
|
||||
if (isCancelled()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds
|
||||
// will do its rounding on pixel instead of point boundaries
|
||||
ASGraphicsBeginImageContextWithOptions(key.backingSize, key.isOpaque, 1.0);
|
||||
|
||||
BOOL contextIsClean = YES;
|
||||
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (context && key.willDisplayNodeContentWithRenderingContext) {
|
||||
key.willDisplayNodeContentWithRenderingContext(context, drawParameters);
|
||||
contextIsClean = NO;
|
||||
}
|
||||
|
||||
// if view is opaque, fill the context with background color
|
||||
if (key.isOpaque && key.backgroundColor) {
|
||||
[key.backgroundColor setFill];
|
||||
UIRectFill({ .size = key.backingSize });
|
||||
contextIsClean = NO;
|
||||
}
|
||||
|
||||
// iOS 9 appears to contain a thread safety regression when drawing the same CGImageRef on
|
||||
// multiple threads concurrently. In fact, instead of crashing, it appears to deadlock.
|
||||
// The issue is present in Mac OS X El Capitan and has been seen hanging Pro apps like Adobe Premiere,
|
||||
// as well as iOS games, and a small number of ASDK apps that provide the same image reference
|
||||
// to many separate ASImageNodes. A workaround is to set .displaysAsynchronously = NO for the nodes
|
||||
// that may get the same pointer for a given UI asset image, etc.
|
||||
// FIXME: We should replace @synchronized here, probably using a global, locked NSMutableSet, and
|
||||
// only if the object already exists in the set we should create a semaphore to signal waiting threads
|
||||
// upon removal of the object from the set when the operation completes.
|
||||
// Another option is to have ASDisplayNode+AsyncDisplay coordinate these cases, and share the decoded buffer.
|
||||
// Details tracked in https://github.com/facebook/AsyncDisplayKit/issues/1068
|
||||
|
||||
UIImage *image = key.image;
|
||||
BOOL canUseCopy = (contextIsClean || ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage)));
|
||||
CGBlendMode blendMode = canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal;
|
||||
|
||||
@synchronized(image) {
|
||||
[image drawInRect:key.imageDrawRect blendMode:blendMode alpha:1];
|
||||
}
|
||||
|
||||
if (context && key.didDisplayNodeContentWithRenderingContext) {
|
||||
key.didDisplayNodeContentWithRenderingContext(context, drawParameters);
|
||||
}
|
||||
|
||||
// Check cancellation one last time before forming image.
|
||||
if (isCancelled()) {
|
||||
ASGraphicsEndImageContext();
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIImage *result = ASGraphicsGetImageAndEndCurrentContext();
|
||||
|
||||
if (key.imageModificationBlock) {
|
||||
result = key.imageModificationBlock(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)displayDidFinish
|
||||
{
|
||||
[super displayDidFinish];
|
||||
|
||||
__instanceLock__.lock();
|
||||
void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock;
|
||||
UIImage *image = _image;
|
||||
BOOL hasDebugLabel = (_debugLabelNode != nil);
|
||||
__instanceLock__.unlock();
|
||||
|
||||
// Update the debug label if necessary
|
||||
if (hasDebugLabel) {
|
||||
// For debugging purposes we don't care about locking for now
|
||||
CGSize imageSize = image.size;
|
||||
CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale);
|
||||
CGSize boundsSizeInPixels = CGSizeMake(std::floor(self.bounds.size.width * self.contentsScale), std::floor(self.bounds.size.height * self.contentsScale));
|
||||
CGFloat pixelCountRatio = (imageSizeInPixels.width * imageSizeInPixels.height) / (boundsSizeInPixels.width * boundsSizeInPixels.height);
|
||||
if (pixelCountRatio != 1.0) {
|
||||
NSString *scaleString = [NSString stringWithFormat:@"%.2fx", pixelCountRatio];
|
||||
_debugLabelNode.attributedText = [[NSAttributedString alloc] initWithString:scaleString attributes:[self debugLabelAttributes]];
|
||||
_debugLabelNode.hidden = NO;
|
||||
} else {
|
||||
_debugLabelNode.hidden = YES;
|
||||
_debugLabelNode.attributedText = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've got a block to perform after displaying, do it.
|
||||
if (image && displayCompletionBlock) {
|
||||
|
||||
displayCompletionBlock(NO);
|
||||
|
||||
__instanceLock__.lock();
|
||||
_displayCompletionBlock = nil;
|
||||
__instanceLock__.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setNeedsDisplayWithCompletion:(void (^ _Nullable)(BOOL canceled))displayCompletionBlock
|
||||
{
|
||||
if (self.displaySuspended) {
|
||||
if (displayCompletionBlock)
|
||||
displayCompletionBlock(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
// Stash the block and call-site queue. We'll invoke it in -displayDidFinish.
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (_displayCompletionBlock != displayCompletionBlock) {
|
||||
_displayCompletionBlock = displayCompletionBlock;
|
||||
}
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
#pragma mark Interface State
|
||||
|
||||
- (void)clearContents
|
||||
{
|
||||
[super clearContents];
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_weakCacheEntry = nil; // release contents from the cache.
|
||||
}
|
||||
|
||||
#pragma mark - Cropping
|
||||
|
||||
- (BOOL)isCropEnabled
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _cropEnabled;
|
||||
}
|
||||
|
||||
- (void)setCropEnabled:(BOOL)cropEnabled
|
||||
{
|
||||
[self setCropEnabled:cropEnabled recropImmediately:NO inBounds:self.bounds];
|
||||
}
|
||||
|
||||
- (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds
|
||||
{
|
||||
__instanceLock__.lock();
|
||||
if (_cropEnabled == cropEnabled) {
|
||||
__instanceLock__.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
_cropEnabled = cropEnabled;
|
||||
_cropDisplayBounds = cropBounds;
|
||||
|
||||
UIImage *image = _image;
|
||||
__instanceLock__.unlock();
|
||||
|
||||
// If we have an image to display, display it, respecting our recrop flag.
|
||||
if (image != nil) {
|
||||
ASPerformBlockOnMainThread(^{
|
||||
if (recropImmediately)
|
||||
[self displayImmediately];
|
||||
else
|
||||
[self setNeedsDisplay];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)cropRect
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _cropRect;
|
||||
}
|
||||
|
||||
- (void)setCropRect:(CGRect)cropRect
|
||||
{
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (CGRectEqualToRect(_cropRect, cropRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cropRect = cropRect;
|
||||
}
|
||||
|
||||
// TODO: this logic needs to be updated to respect cropRect.
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
CGSize imageSize = self.image.size;
|
||||
|
||||
BOOL isCroppingImage = ((boundsSize.width < imageSize.width) || (boundsSize.height < imageSize.height));
|
||||
|
||||
// Re-display if we need to.
|
||||
ASPerformBlockOnMainThread(^{
|
||||
if (self.nodeLoaded && self.contentMode == UIViewContentModeScaleAspectFill && isCroppingImage)
|
||||
[self setNeedsDisplay];
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)forceUpscaling
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _forceUpscaling;
|
||||
}
|
||||
|
||||
- (void)setForceUpscaling:(BOOL)forceUpscaling
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_forceUpscaling = forceUpscaling;
|
||||
}
|
||||
|
||||
- (CGSize)forcedSize
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _forcedSize;
|
||||
}
|
||||
|
||||
- (void)setForcedSize:(CGSize)forcedSize
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_forcedSize = forcedSize;
|
||||
}
|
||||
|
||||
- (asimagenode_modification_block_t)imageModificationBlock
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _imageModificationBlock;
|
||||
}
|
||||
|
||||
- (void)setImageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_imageModificationBlock = imageModificationBlock;
|
||||
}
|
||||
|
||||
#pragma mark - Debug
|
||||
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
|
||||
if (_debugLabelNode) {
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
CGSize debugLabelSize = [_debugLabelNode layoutThatFits:ASSizeRangeMake(CGSizeZero, boundsSize)].size;
|
||||
CGPoint debugLabelOrigin = CGPointMake(boundsSize.width - debugLabelSize.width,
|
||||
boundsSize.height - debugLabelSize.height);
|
||||
_debugLabelNode.frame = (CGRect) {debugLabelOrigin, debugLabelSize};
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)debugLabelAttributes
|
||||
{
|
||||
return @{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:15.0],
|
||||
NSForegroundColorAttributeName: [UIColor redColor]
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Extras
|
||||
|
||||
asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat borderWidth, UIColor *borderColor)
|
||||
{
|
||||
return ^(UIImage *originalImage) {
|
||||
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
||||
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];
|
||||
|
||||
// Make the image round
|
||||
[roundOutline addClip];
|
||||
|
||||
// Draw the original image
|
||||
[originalImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
|
||||
|
||||
// Draw a border on top.
|
||||
if (borderWidth > 0.0) {
|
||||
[borderColor setStroke];
|
||||
[roundOutline setLineWidth:borderWidth];
|
||||
[roundOutline stroke];
|
||||
}
|
||||
|
||||
return ASGraphicsGetImageAndEndCurrentContext();
|
||||
};
|
||||
}
|
||||
|
||||
asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color)
|
||||
{
|
||||
return ^(UIImage *originalImage) {
|
||||
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
||||
|
||||
// Set color and render template
|
||||
[color setFill];
|
||||
UIImage *templateImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
[templateImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
|
||||
|
||||
UIImage *modifiedImage = ASGraphicsGetImageAndEndCurrentContext();
|
||||
|
||||
// if the original image was stretchy, keep it stretchy
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) {
|
||||
modifiedImage = [modifiedImage resizableImageWithCapInsets:originalImage.capInsets resizingMode:originalImage.resizingMode];
|
||||
}
|
||||
|
||||
return modifiedImage;
|
||||
};
|
||||
}
|
||||
99
Source/ASMapNode.h.orig
Normal file
99
Source/ASMapNode.h.orig
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// ASMapNode.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifndef MINIMAL_ASDK
|
||||
|
||||
=======
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AsyncDisplayKit/ASAvailability.h>
|
||||
|
||||
#if TARGET_OS_IOS && AS_USE_MAPKIT
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
#import <AsyncDisplayKit/ASImageNode.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Map Annotation options.
|
||||
* The default behavior is to ignore the annotations' positions, use the region or options specified instead.
|
||||
* Swift: to select the default behavior, use [].
|
||||
*/
|
||||
typedef NS_OPTIONS(NSUInteger, ASMapNodeShowAnnotationsOptions)
|
||||
{
|
||||
/** The annotations' positions are ignored, use the region or options specified instead. */
|
||||
ASMapNodeShowAnnotationsOptionsIgnored = 0,
|
||||
/** The annotations' positions are used to calculate the region to show in the map, equivalent to showAnnotations:animated. */
|
||||
ASMapNodeShowAnnotationsOptionsZoomed = 1 << 0,
|
||||
/** This will only have an effect if combined with the Zoomed state with liveMap turned on.*/
|
||||
ASMapNodeShowAnnotationsOptionsAnimated = 1 << 1
|
||||
};
|
||||
|
||||
@interface ASMapNode : ASImageNode
|
||||
|
||||
/**
|
||||
The current options of ASMapNode. This can be set at any time and ASMapNode will animate the change.<br><br>This property may be set from a background thread before the node is loaded, and will automatically be applied to define the behavior of the static snapshot (if .liveMap = NO) or the internal MKMapView (otherwise).<br><br> Changes to the region and camera options will only be animated when when the liveMap mode is enabled, otherwise these options will be applied statically to the new snapshot. <br><br> The options object is used to specify properties even when the liveMap mode is enabled, allowing seamless transitions between the snapshot and liveMap (as well as back to the snapshot).
|
||||
*/
|
||||
@property (nonatomic) MKMapSnapshotOptions *options;
|
||||
|
||||
/** The region is simply the sub-field on the options object. If the objects object is reset,
|
||||
this will in effect be overwritten and become the value of the .region property on that object.
|
||||
Defaults to MKCoordinateRegionForMapRect(MKMapRectWorld).
|
||||
*/
|
||||
@property (nonatomic) MKCoordinateRegion region;
|
||||
|
||||
/**
|
||||
This is the MKMapView that is the live map part of ASMapNode. This will be nil if .liveMap = NO. Note, MKMapView is *not* thread-safe.
|
||||
*/
|
||||
@property (nullable, readonly) MKMapView *mapView;
|
||||
|
||||
/**
|
||||
Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. This property may be set on a background thread before the node is loaded, and will automatically be actioned, once the node is loaded.
|
||||
*/
|
||||
@property (getter=isLiveMap) BOOL liveMap;
|
||||
|
||||
/**
|
||||
@abstract Whether ASMapNode should automatically request a new map snapshot to correspond to the new node size.
|
||||
@default Default value is YES.
|
||||
@discussion If mapSize is set then this will be set to NO, since the size will be the same in all orientations.
|
||||
*/
|
||||
@property BOOL needsMapReloadOnBoundsChange;
|
||||
|
||||
/**
|
||||
Set the delegate of the MKMapView. This can be set even before mapView is created and will be set on the map in the case that the liveMap mode is engaged.
|
||||
|
||||
If the live map view has been created, this may only be set on the main thread.
|
||||
*/
|
||||
@property (nonatomic, weak) id <MKMapViewDelegate> mapDelegate;
|
||||
|
||||
/**
|
||||
* @abstract The annotations to display on the map.
|
||||
*/
|
||||
@property (copy) NSArray<id<MKAnnotation>> *annotations;
|
||||
|
||||
/**
|
||||
* @abstract This property specifies how to show the annotations.
|
||||
* @default Default value is ASMapNodeShowAnnotationsIgnored
|
||||
*/
|
||||
@property ASMapNodeShowAnnotationsOptions showAnnotationsOptions;
|
||||
|
||||
/**
|
||||
* @abstract The block which should return annotation image for static map based on provided annotation.
|
||||
* @discussion This block is executed on an arbitrary serial queue. If this block is nil, standard pin is used.
|
||||
*/
|
||||
@property (nullable) UIImage * _Nullable (^imageForStaticMapAnnotationBlock)(id<MKAnnotation> annotation, CGPoint *centerOffset);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
456
Source/ASMapNode.mm.orig
Normal file
456
Source/ASMapNode.mm.orig
Normal file
@ -0,0 +1,456 @@
|
||||
//
|
||||
// ASMapNode.mm
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifndef MINIMAL_ASDK
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
=======
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
#import <AsyncDisplayKit/ASMapNode.h>
|
||||
|
||||
#if TARGET_OS_IOS && AS_USE_MAPKIT
|
||||
|
||||
#import <tgmath.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASThread.h>
|
||||
|
||||
@interface ASMapNode()
|
||||
{
|
||||
MKMapSnapshotter *_snapshotter;
|
||||
BOOL _snapshotAfterLayout;
|
||||
NSArray *_annotations;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ASMapNode
|
||||
|
||||
@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange;
|
||||
@synthesize mapDelegate = _mapDelegate;
|
||||
@synthesize options = _options;
|
||||
@synthesize liveMap = _liveMap;
|
||||
@synthesize showAnnotationsOptions = _showAnnotationsOptions;
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
self.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||
self.clipsToBounds = YES;
|
||||
self.userInteractionEnabled = YES;
|
||||
|
||||
_needsMapReloadOnBoundsChange = YES;
|
||||
_liveMap = NO;
|
||||
_annotations = @[];
|
||||
_showAnnotationsOptions = ASMapNodeShowAnnotationsOptionsIgnored;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
if (self.isLiveMap) {
|
||||
[self addLiveMap];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self destroySnapshotter];
|
||||
}
|
||||
|
||||
- (void)setLayerBacked:(BOOL)layerBacked
|
||||
{
|
||||
ASDisplayNodeAssert(!self.isLiveMap, @"ASMapNode can not be layer backed whilst .liveMap = YES, set .liveMap = NO to use layer backing.");
|
||||
[super setLayerBacked:layerBacked];
|
||||
}
|
||||
|
||||
- (void)didEnterPreloadState
|
||||
{
|
||||
[super didEnterPreloadState];
|
||||
ASPerformBlockOnMainThread(^{
|
||||
if (self.isLiveMap) {
|
||||
[self addLiveMap];
|
||||
} else {
|
||||
[self takeSnapshot];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)didExitPreloadState
|
||||
{
|
||||
[super didExitPreloadState];
|
||||
ASPerformBlockOnMainThread(^{
|
||||
if (self.isLiveMap) {
|
||||
[self removeLiveMap];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Settings
|
||||
|
||||
- (BOOL)isLiveMap
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
return _liveMap;
|
||||
}
|
||||
|
||||
- (void)setLiveMap:(BOOL)liveMap
|
||||
{
|
||||
ASDisplayNodeAssert(!self.isLayerBacked, @"ASMapNode can not use the interactive map feature whilst .isLayerBacked = YES, set .layerBacked = NO to use the interactive map feature.");
|
||||
ASLockScopeSelf();
|
||||
if (liveMap == _liveMap) {
|
||||
return;
|
||||
}
|
||||
_liveMap = liveMap;
|
||||
if (self.nodeLoaded) {
|
||||
liveMap ? [self addLiveMap] : [self removeLiveMap];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)needsMapReloadOnBoundsChange
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
return _needsMapReloadOnBoundsChange;
|
||||
}
|
||||
|
||||
- (void)setNeedsMapReloadOnBoundsChange:(BOOL)needsMapReloadOnBoundsChange
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
_needsMapReloadOnBoundsChange = needsMapReloadOnBoundsChange;
|
||||
}
|
||||
|
||||
- (MKMapSnapshotOptions *)options
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
if (!_options) {
|
||||
_options = [[MKMapSnapshotOptions alloc] init];
|
||||
_options.region = MKCoordinateRegionForMapRect(MKMapRectWorld);
|
||||
CGSize calculatedSize = self.calculatedSize;
|
||||
if (!CGSizeEqualToSize(calculatedSize, CGSizeZero)) {
|
||||
_options.size = calculatedSize;
|
||||
}
|
||||
}
|
||||
return _options;
|
||||
}
|
||||
|
||||
- (void)setOptions:(MKMapSnapshotOptions *)options
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
if (!_options || ![options isEqual:_options]) {
|
||||
_options = options;
|
||||
if (self.isLiveMap) {
|
||||
[self applySnapshotOptions];
|
||||
} else if (_snapshotter) {
|
||||
[self destroySnapshotter];
|
||||
[self takeSnapshot];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (MKCoordinateRegion)region
|
||||
{
|
||||
return self.options.region;
|
||||
}
|
||||
|
||||
- (void)setRegion:(MKCoordinateRegion)region
|
||||
{
|
||||
MKMapSnapshotOptions * options = [self.options copy];
|
||||
options.region = region;
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
- (id<MKMapViewDelegate>)mapDelegate
|
||||
{
|
||||
return ASLockedSelf(_mapDelegate);
|
||||
}
|
||||
|
||||
- (void)setMapDelegate:(id<MKMapViewDelegate>)mapDelegate {
|
||||
ASLockScopeSelf();
|
||||
_mapDelegate = mapDelegate;
|
||||
|
||||
if (_mapView) {
|
||||
ASDisplayNodeAssertMainThread();
|
||||
_mapView.delegate = mapDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Snapshotter
|
||||
|
||||
- (void)takeSnapshot
|
||||
{
|
||||
// If our size is zero, we want to avoid calling a default sized snapshot. Set _snapshotAfterLayout to YES
|
||||
// so if layout changes in the future, we'll try snapshotting again.
|
||||
ASLayout *layout = self.calculatedLayout;
|
||||
if (layout == nil || CGSizeEqualToSize(CGSizeZero, layout.size)) {
|
||||
_snapshotAfterLayout = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
_snapshotAfterLayout = NO;
|
||||
|
||||
if (!_snapshotter) {
|
||||
[self setUpSnapshotter];
|
||||
}
|
||||
|
||||
if (_snapshotter.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
[_snapshotter startWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||
completionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
UIImage *image = snapshot.image;
|
||||
NSArray *annotations = strongSelf.annotations;
|
||||
if (annotations.count > 0) {
|
||||
// Only create a graphics context if we have annotations to draw.
|
||||
// The MKMapSnapshotter is currently not capable of rendering annotations automatically.
|
||||
|
||||
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
|
||||
|
||||
ASGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
|
||||
[image drawAtPoint:CGPointZero];
|
||||
|
||||
UIImage *pinImage;
|
||||
CGPoint pinCenterOffset = CGPointZero;
|
||||
|
||||
// Get a standard annotation view pin if there is no custom annotation block.
|
||||
if (!strongSelf.imageForStaticMapAnnotationBlock) {
|
||||
pinImage = [strongSelf.class defaultPinImageWithCenterOffset:&pinCenterOffset];
|
||||
}
|
||||
|
||||
for (id<MKAnnotation> annotation in annotations) {
|
||||
if (strongSelf.imageForStaticMapAnnotationBlock) {
|
||||
// Get custom annotation image from custom annotation block.
|
||||
pinImage = strongSelf.imageForStaticMapAnnotationBlock(annotation, &pinCenterOffset);
|
||||
if (!pinImage) {
|
||||
// just for case block returned nil, which can happen
|
||||
pinImage = [strongSelf.class defaultPinImageWithCenterOffset:&pinCenterOffset];
|
||||
}
|
||||
}
|
||||
|
||||
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
|
||||
if (CGRectContainsPoint(finalImageRect, point)) {
|
||||
CGSize pinSize = pinImage.size;
|
||||
point.x -= pinSize.width / 2.0;
|
||||
point.y -= pinSize.height / 2.0;
|
||||
point.x += pinCenterOffset.x;
|
||||
point.y += pinCenterOffset.y;
|
||||
[pinImage drawAtPoint:point];
|
||||
}
|
||||
}
|
||||
|
||||
image = ASGraphicsGetImageAndEndCurrentContext();
|
||||
}
|
||||
|
||||
strongSelf.image = image;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (UIImage *)defaultPinImageWithCenterOffset:(CGPoint *)centerOffset NS_RETURNS_RETAINED
|
||||
{
|
||||
static MKAnnotationView *pin;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
|
||||
});
|
||||
*centerOffset = pin.centerOffset;
|
||||
return pin.image;
|
||||
}
|
||||
|
||||
- (void)setUpSnapshotter
|
||||
{
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:self.options];
|
||||
}
|
||||
|
||||
- (void)destroySnapshotter
|
||||
{
|
||||
[_snapshotter cancel];
|
||||
_snapshotter = nil;
|
||||
}
|
||||
|
||||
- (void)applySnapshotOptions
|
||||
{
|
||||
MKMapSnapshotOptions *options = self.options;
|
||||
[_mapView setCamera:options.camera animated:YES];
|
||||
[_mapView setRegion:options.region animated:YES];
|
||||
[_mapView setMapType:options.mapType];
|
||||
_mapView.showsBuildings = options.showsBuildings;
|
||||
_mapView.showsPointsOfInterest = options.showsPointsOfInterest;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
- (void)addLiveMap
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (!_mapView) {
|
||||
__weak ASMapNode *weakSelf = self;
|
||||
_mapView = [[MKMapView alloc] initWithFrame:CGRectZero];
|
||||
_mapView.delegate = weakSelf.mapDelegate;
|
||||
[weakSelf applySnapshotOptions];
|
||||
[_mapView addAnnotations:_annotations];
|
||||
[weakSelf setNeedsLayout];
|
||||
[weakSelf.view addSubview:_mapView];
|
||||
|
||||
ASMapNodeShowAnnotationsOptions showAnnotationsOptions = self.showAnnotationsOptions;
|
||||
if (showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsZoomed) {
|
||||
BOOL const animated = showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsAnimated;
|
||||
[_mapView showAnnotations:_mapView.annotations animated:animated];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeLiveMap
|
||||
{
|
||||
[_mapView removeFromSuperview];
|
||||
_mapView = nil;
|
||||
}
|
||||
|
||||
- (NSArray *)annotations
|
||||
{
|
||||
ASLockScopeSelf();
|
||||
return _annotations;
|
||||
}
|
||||
|
||||
- (void)setAnnotations:(NSArray *)annotations
|
||||
{
|
||||
annotations = [annotations copy] ? : @[];
|
||||
|
||||
ASLockScopeSelf();
|
||||
_annotations = annotations;
|
||||
ASMapNodeShowAnnotationsOptions showAnnotationsOptions = self.showAnnotationsOptions;
|
||||
if (self.isLiveMap) {
|
||||
[_mapView removeAnnotations:_mapView.annotations];
|
||||
[_mapView addAnnotations:annotations];
|
||||
|
||||
if (showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsZoomed) {
|
||||
BOOL const animated = showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsAnimated;
|
||||
[_mapView showAnnotations:_mapView.annotations animated:animated];
|
||||
}
|
||||
} else {
|
||||
if (showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsZoomed) {
|
||||
self.region = [self regionToFitAnnotations:annotations];
|
||||
}
|
||||
else {
|
||||
[self takeSnapshot];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (MKCoordinateRegion)regionToFitAnnotations:(NSArray<id<MKAnnotation>> *)annotations
|
||||
{
|
||||
if([annotations count] == 0)
|
||||
return MKCoordinateRegionForMapRect(MKMapRectWorld);
|
||||
|
||||
CLLocationCoordinate2D topLeftCoord = CLLocationCoordinate2DMake(-90, 180);
|
||||
CLLocationCoordinate2D bottomRightCoord = CLLocationCoordinate2DMake(90, -180);
|
||||
|
||||
for (id<MKAnnotation> annotation in annotations) {
|
||||
topLeftCoord = CLLocationCoordinate2DMake(std::fmax(topLeftCoord.latitude, annotation.coordinate.latitude),
|
||||
std::fmin(topLeftCoord.longitude, annotation.coordinate.longitude));
|
||||
bottomRightCoord = CLLocationCoordinate2DMake(std::fmin(bottomRightCoord.latitude, annotation.coordinate.latitude),
|
||||
std::fmax(bottomRightCoord.longitude, annotation.coordinate.longitude));
|
||||
}
|
||||
|
||||
MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5,
|
||||
topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5),
|
||||
MKCoordinateSpanMake(std::fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 2,
|
||||
std::fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 2));
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
-(ASMapNodeShowAnnotationsOptions)showAnnotationsOptions {
|
||||
return ASLockedSelf(_showAnnotationsOptions);
|
||||
}
|
||||
|
||||
-(void)setShowAnnotationsOptions:(ASMapNodeShowAnnotationsOptions)showAnnotationsOptions {
|
||||
ASLockScopeSelf();
|
||||
_showAnnotationsOptions = showAnnotationsOptions;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
- (void)setSnapshotSizeWithReloadIfNeeded:(CGSize)snapshotSize
|
||||
{
|
||||
if (snapshotSize.height > 0 && snapshotSize.width > 0 && !CGSizeEqualToSize(self.options.size, snapshotSize)) {
|
||||
_options.size = snapshotSize;
|
||||
if (_snapshotter) {
|
||||
[self destroySnapshotter];
|
||||
[self takeSnapshot];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
// FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc)
|
||||
// These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value.
|
||||
if (!ASIsCGSizeValidForLayout(constrainedSize)) {
|
||||
//ASDisplayNodeAssert(NO, @"Invalid width or height in ASMapNode");
|
||||
constrainedSize = CGSizeZero;
|
||||
}
|
||||
[self setSnapshotSizeWithReloadIfNeeded:constrainedSize];
|
||||
return constrainedSize;
|
||||
}
|
||||
|
||||
- (void)calculatedLayoutDidChange
|
||||
{
|
||||
[super calculatedLayoutDidChange];
|
||||
|
||||
if (_snapshotAfterLayout) {
|
||||
[self takeSnapshot];
|
||||
}
|
||||
}
|
||||
|
||||
// -layout isn't usually needed over -layoutSpecThatFits, but this way we can avoid a needless node wrapper for MKMapView.
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
if (self.isLiveMap) {
|
||||
_mapView.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height);
|
||||
} else {
|
||||
// If our bounds.size is different from our current snapshot size, then let's request a new image from MKMapSnapshotter.
|
||||
if (_needsMapReloadOnBoundsChange) {
|
||||
[self setSnapshotSizeWithReloadIfNeeded:self.bounds.size];
|
||||
// FIXME: Adding a check for Preload here seems to cause intermittent map load failures, but shouldn't.
|
||||
// if (ASInterfaceStateIncludesPreload(self.interfaceState)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)supportsLayerBacking
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
<<<<<<< HEAD
|
||||
#endif
|
||||
|
||||
#endif
|
||||
=======
|
||||
#endif // TARGET_OS_IOS && AS_USE_MAPKIT
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
@ -913,3 +913,4 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
||||
|
||||
@end
|
||||
#endif
|
||||
#endif
|
||||
|
||||
72
Source/Base/ASAssert.m.orig
Normal file
72
Source/Base/ASAssert.m.orig
Normal file
@ -0,0 +1,72 @@
|
||||
//
|
||||
// ASAssert.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
#import <AsyncDisplayKit/ASAvailability.h>
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifndef MINIMAL_ASDK
|
||||
static _Thread_local int tls_mainThreadAssertionsDisabledCount;
|
||||
#endif
|
||||
=======
|
||||
#if AS_TLS_AVAILABLE
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
|
||||
static _Thread_local int tls_mainThreadAssertionsDisabledCount;
|
||||
BOOL ASMainThreadAssertionsAreDisabled() {
|
||||
#ifdef MINIMAL_ASDK
|
||||
return false;
|
||||
#else
|
||||
return tls_mainThreadAssertionsDisabledCount > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ASPushMainThreadAssertionsDisabled() {
|
||||
#ifndef MINIMAL_ASDK
|
||||
tls_mainThreadAssertionsDisabledCount += 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ASPopMainThreadAssertionsDisabled() {
|
||||
#ifndef MINIMAL_ASDK
|
||||
tls_mainThreadAssertionsDisabledCount -= 1;
|
||||
ASDisplayNodeCAssert(tls_mainThreadAssertionsDisabledCount >= 0, @"Attempt to pop thread assertion-disabling without corresponding push.");
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#import <dispatch/once.h>
|
||||
|
||||
static pthread_key_t ASMainThreadAssertionsDisabledKey() {
|
||||
static pthread_key_t k;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
pthread_key_create(&k, NULL);
|
||||
});
|
||||
return k;
|
||||
}
|
||||
|
||||
BOOL ASMainThreadAssertionsAreDisabled() {
|
||||
return (pthread_getspecific(ASMainThreadAssertionsDisabledKey()) > 0);
|
||||
}
|
||||
|
||||
void ASPushMainThreadAssertionsDisabled() {
|
||||
let key = ASMainThreadAssertionsDisabledKey();
|
||||
let oldVal = pthread_getspecific(key);
|
||||
pthread_setspecific(key, oldVal + 1);
|
||||
}
|
||||
|
||||
void ASPopMainThreadAssertionsDisabled() {
|
||||
let key = ASMainThreadAssertionsDisabledKey();
|
||||
let oldVal = pthread_getspecific(key);
|
||||
pthread_setspecific(key, oldVal - 1);
|
||||
ASDisplayNodeCAssert(oldVal > 0, @"Attempt to pop thread assertion-disabling without corresponding push.");
|
||||
}
|
||||
|
||||
#endif // AS_TLS_AVAILABLE
|
||||
@ -11,11 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __i386__
|
||||
#define AS_TLS_AVAILABLE 0
|
||||
#else
|
||||
#define AS_TLS_AVAILABLE 1
|
||||
#endif
|
||||
#define AS_TLS_AVAILABLE 0
|
||||
|
||||
#ifndef AS_USE_PHOTOS
|
||||
# define AS_USE_PHOTOS 0
|
||||
|
||||
81
Source/Details/ASPhotosFrameworkImageRequest.h.orig
Normal file
81
Source/Details/ASPhotosFrameworkImageRequest.h.orig
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// ASPhotosFrameworkImageRequest.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifndef MINIMAL_ASDK
|
||||
=======
|
||||
#import <AsyncDisplayKit/ASAvailability.h>
|
||||
|
||||
#if AS_USE_PHOTOS
|
||||
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
AS_EXTERN NSString *const ASPhotosURLScheme;
|
||||
|
||||
/**
|
||||
@abstract Use ASPhotosFrameworkImageRequest to encapsulate all the information needed to request an image from
|
||||
the Photos framework and store it in a URL.
|
||||
*/
|
||||
API_AVAILABLE(ios(8.0), tvos(10.0))
|
||||
@interface ASPhotosFrameworkImageRequest : NSObject <NSCopying>
|
||||
|
||||
- (instancetype)initWithAssetIdentifier:(NSString *)assetIdentifier NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
@return A new image request deserialized from `url`, or nil if `url` is not a valid photos URL.
|
||||
*/
|
||||
+ (nullable ASPhotosFrameworkImageRequest *)requestWithURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
@abstract The asset identifier for this image request provided during initialization.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSString *assetIdentifier;
|
||||
|
||||
/**
|
||||
@abstract The target size for this image request. Defaults to `PHImageManagerMaximumSize`.
|
||||
*/
|
||||
@property (nonatomic) CGSize targetSize;
|
||||
|
||||
/**
|
||||
@abstract The content mode for this image request. Defaults to `PHImageContentModeDefault`.
|
||||
|
||||
@see `PHImageManager`
|
||||
*/
|
||||
@property (nonatomic) PHImageContentMode contentMode;
|
||||
|
||||
/**
|
||||
@abstract The options specified for this request. Default value is the result of `[PHImageRequestOptions new]`.
|
||||
|
||||
@discussion Some properties of this object are ignored when converting this request into a URL.
|
||||
As of iOS SDK 9.0, these properties are `progressHandler` and `synchronous`.
|
||||
*/
|
||||
@property (nonatomic) PHImageRequestOptions *options;
|
||||
|
||||
/**
|
||||
@return A new URL converted from this request.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSURL *url;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
<<<<<<< HEAD
|
||||
#endif
|
||||
=======
|
||||
|
||||
#endif // AS_USE_PHOTOS
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
163
Source/Details/ASPhotosFrameworkImageRequest.m.orig
Normal file
163
Source/Details/ASPhotosFrameworkImageRequest.m.orig
Normal file
@ -0,0 +1,163 @@
|
||||
//
|
||||
// ASPhotosFrameworkImageRequest.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
#ifndef MINIMAL_ASDK
|
||||
#import <AsyncDisplayKit/ASPhotosFrameworkImageRequest.h>
|
||||
|
||||
#if AS_USE_PHOTOS
|
||||
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
NSString *const ASPhotosURLScheme = @"ph";
|
||||
|
||||
static NSString *const _ASPhotosURLQueryKeyWidth = @"width";
|
||||
static NSString *const _ASPhotosURLQueryKeyHeight = @"height";
|
||||
|
||||
// value is PHImageContentMode value
|
||||
static NSString *const _ASPhotosURLQueryKeyContentMode = @"contentmode";
|
||||
|
||||
// value is PHImageRequestOptionsResizeMode value
|
||||
static NSString *const _ASPhotosURLQueryKeyResizeMode = @"resizemode";
|
||||
|
||||
// value is PHImageRequestOptionsDeliveryMode value
|
||||
static NSString *const _ASPhotosURLQueryKeyDeliveryMode = @"deliverymode";
|
||||
|
||||
// value is PHImageRequestOptionsVersion value
|
||||
static NSString *const _ASPhotosURLQueryKeyVersion = @"version";
|
||||
|
||||
// value is 0 or 1
|
||||
static NSString *const _ASPhotosURLQueryKeyAllowNetworkAccess = @"network";
|
||||
|
||||
static NSString *const _ASPhotosURLQueryKeyCropOriginX = @"crop_x";
|
||||
static NSString *const _ASPhotosURLQueryKeyCropOriginY = @"crop_y";
|
||||
static NSString *const _ASPhotosURLQueryKeyCropWidth = @"crop_w";
|
||||
static NSString *const _ASPhotosURLQueryKeyCropHeight = @"crop_h";
|
||||
|
||||
@implementation ASPhotosFrameworkImageRequest
|
||||
|
||||
- (instancetype)initWithAssetIdentifier:(NSString *)assetIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_assetIdentifier = assetIdentifier;
|
||||
_options = [PHImageRequestOptions new];
|
||||
_contentMode = PHImageContentModeDefault;
|
||||
_targetSize = PHImageManagerMaximumSize;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSCopying
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
ASPhotosFrameworkImageRequest *copy = [[ASPhotosFrameworkImageRequest alloc] initWithAssetIdentifier:self.assetIdentifier];
|
||||
copy.options = [self.options copy];
|
||||
copy.targetSize = self.targetSize;
|
||||
copy.contentMode = self.contentMode;
|
||||
return copy;
|
||||
}
|
||||
|
||||
#pragma mark Converting to URL
|
||||
|
||||
- (NSURL *)url
|
||||
{
|
||||
NSURLComponents *comp = [NSURLComponents new];
|
||||
comp.scheme = ASPhotosURLScheme;
|
||||
comp.host = _assetIdentifier;
|
||||
NSMutableArray *queryItems = [NSMutableArray arrayWithObjects:
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyWidth value:@(_targetSize.width).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyHeight value:@(_targetSize.height).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyVersion value:@(_options.version).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyContentMode value:@(_contentMode).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyAllowNetworkAccess value:@(_options.networkAccessAllowed).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyResizeMode value:@(_options.resizeMode).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyDeliveryMode value:@(_options.deliveryMode).stringValue]
|
||||
, nil];
|
||||
|
||||
CGRect cropRect = _options.normalizedCropRect;
|
||||
if (!CGRectIsEmpty(cropRect)) {
|
||||
[queryItems addObjectsFromArray:@[
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropOriginX value:@(cropRect.origin.x).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropOriginY value:@(cropRect.origin.y).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropWidth value:@(cropRect.size.width).stringValue],
|
||||
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropHeight value:@(cropRect.size.height).stringValue]
|
||||
]];
|
||||
}
|
||||
comp.queryItems = queryItems;
|
||||
return comp.URL;
|
||||
}
|
||||
|
||||
#pragma mark Converting from URL
|
||||
|
||||
+ (ASPhotosFrameworkImageRequest *)requestWithURL:(NSURL *)url
|
||||
{
|
||||
// not a photos URL
|
||||
if (![url.scheme isEqualToString:ASPhotosURLScheme]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSURLComponents *comp = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
|
||||
ASPhotosFrameworkImageRequest *request = [[ASPhotosFrameworkImageRequest alloc] initWithAssetIdentifier:url.host];
|
||||
|
||||
CGRect cropRect = CGRectZero;
|
||||
CGSize targetSize = PHImageManagerMaximumSize;
|
||||
for (NSURLQueryItem *item in comp.queryItems) {
|
||||
if ([_ASPhotosURLQueryKeyAllowNetworkAccess isEqualToString:item.name]) {
|
||||
request.options.networkAccessAllowed = item.value.boolValue;
|
||||
} else if ([_ASPhotosURLQueryKeyWidth isEqualToString:item.name]) {
|
||||
targetSize.width = item.value.doubleValue;
|
||||
} else if ([_ASPhotosURLQueryKeyHeight isEqualToString:item.name]) {
|
||||
targetSize.height = item.value.doubleValue;
|
||||
} else if ([_ASPhotosURLQueryKeyContentMode isEqualToString:item.name]) {
|
||||
request.contentMode = (PHImageContentMode)item.value.integerValue;
|
||||
} else if ([_ASPhotosURLQueryKeyVersion isEqualToString:item.name]) {
|
||||
request.options.version = (PHImageRequestOptionsVersion)item.value.integerValue;
|
||||
} else if ([_ASPhotosURLQueryKeyCropOriginX isEqualToString:item.name]) {
|
||||
cropRect.origin.x = item.value.doubleValue;
|
||||
} else if ([_ASPhotosURLQueryKeyCropOriginY isEqualToString:item.name]) {
|
||||
cropRect.origin.y = item.value.doubleValue;
|
||||
} else if ([_ASPhotosURLQueryKeyCropWidth isEqualToString:item.name]) {
|
||||
cropRect.size.width = item.value.doubleValue;
|
||||
} else if ([_ASPhotosURLQueryKeyCropHeight isEqualToString:item.name]) {
|
||||
cropRect.size.height = item.value.doubleValue;
|
||||
} else if ([_ASPhotosURLQueryKeyResizeMode isEqualToString:item.name]) {
|
||||
request.options.resizeMode = (PHImageRequestOptionsResizeMode)item.value.integerValue;
|
||||
} else if ([_ASPhotosURLQueryKeyDeliveryMode isEqualToString:item.name]) {
|
||||
request.options.deliveryMode = (PHImageRequestOptionsDeliveryMode)item.value.integerValue;
|
||||
}
|
||||
}
|
||||
request.targetSize = targetSize;
|
||||
request.options.normalizedCropRect = cropRect;
|
||||
return request;
|
||||
}
|
||||
|
||||
#pragma mark NSObject
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (![object isKindOfClass:ASPhotosFrameworkImageRequest.class]) {
|
||||
return NO;
|
||||
}
|
||||
ASPhotosFrameworkImageRequest *other = object;
|
||||
return [other.assetIdentifier isEqualToString:self.assetIdentifier] &&
|
||||
other.contentMode == self.contentMode &&
|
||||
CGSizeEqualToSize(other.targetSize, self.targetSize) &&
|
||||
CGRectEqualToRect(other.options.normalizedCropRect, self.options.normalizedCropRect) &&
|
||||
other.options.resizeMode == self.options.resizeMode &&
|
||||
other.options.version == self.options.version;
|
||||
}
|
||||
|
||||
@end
|
||||
<<<<<<< HEAD
|
||||
#endif
|
||||
=======
|
||||
|
||||
#endif // AS_USE_PHOTOS
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
@ -66,7 +66,6 @@ void ASLayoutElementPopContext()
|
||||
ASDisplayNodeCAssertNotNil(tls_context, @"Attempt to pop context when there wasn't a context!");
|
||||
CFRelease((__bridge CFTypeRef)tls_context);
|
||||
tls_context = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
869
Source/Layout/ASLayoutElement.mm.orig
Normal file
869
Source/Layout/ASLayoutElement.mm.orig
Normal file
@ -0,0 +1,869 @@
|
||||
//
|
||||
// ASLayoutElement.mm
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
|
||||
#import <AsyncDisplayKit/ASAvailability.h>
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASLayoutElement.h>
|
||||
#import <AsyncDisplayKit/ASThread.h>
|
||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||
|
||||
#import <atomic>
|
||||
#include <pthread.h>
|
||||
|
||||
#if YOGA
|
||||
#import YOGA_HEADER_PATH
|
||||
#import <AsyncDisplayKit/ASYogaUtilities.h>
|
||||
#endif
|
||||
|
||||
#pragma mark - ASLayoutElementContext
|
||||
|
||||
@implementation ASLayoutElementContext
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_transitionID = ASLayoutElementContextDefaultTransitionID;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
CGFloat const ASLayoutElementParentDimensionUndefined = NAN;
|
||||
CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensionUndefined, ASLayoutElementParentDimensionUndefined};
|
||||
|
||||
int32_t const ASLayoutElementContextInvalidTransitionID = 0;
|
||||
int32_t const ASLayoutElementContextDefaultTransitionID = ASLayoutElementContextInvalidTransitionID + 1;
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifdef MINIMAL_ASDK
|
||||
static ASLayoutElementContext *mainThreadTlsContext = nil;
|
||||
|
||||
static ASLayoutElementContext *get_tls_context() {
|
||||
if ([NSThread isMainThread]) {
|
||||
return mainThreadTlsContext;
|
||||
} else {
|
||||
return [NSThread currentThread].threadDictionary[@"ASDK_tls_context"];
|
||||
}
|
||||
}
|
||||
|
||||
static void set_tls_context(ASLayoutElementContext *value) {
|
||||
if ([NSThread isMainThread]) {
|
||||
mainThreadTlsContext = value;
|
||||
} else {
|
||||
if (value != nil) {
|
||||
[NSThread currentThread].threadDictionary[@"ASDK_tls_context"] = value;
|
||||
} else {
|
||||
[[NSThread currentThread].threadDictionary removeObjectForKey:@"ASDK_tls_context"];
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
=======
|
||||
#if AS_TLS_AVAILABLE
|
||||
|
||||
>>>>>>> 565da7d4935740d12fc204aa061faf093831da1e
|
||||
static _Thread_local __unsafe_unretained ASLayoutElementContext *tls_context;
|
||||
#endif
|
||||
|
||||
void ASLayoutElementPushContext(ASLayoutElementContext *context)
|
||||
{
|
||||
#ifdef MINIMAL_ASDK
|
||||
// NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
|
||||
ASDisplayNodeCAssertNil(get_tls_context(), @"Nested ASLayoutElementContexts aren't supported.");
|
||||
|
||||
;
|
||||
set_tls_context(context);
|
||||
#else
|
||||
// NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
|
||||
ASDisplayNodeCAssertNil(tls_context, @"Nested ASLayoutElementContexts aren't supported.");
|
||||
|
||||
tls_context = (__bridge ASLayoutElementContext *)(__bridge_retained CFTypeRef)context;
|
||||
#endif
|
||||
}
|
||||
|
||||
ASLayoutElementContext *ASLayoutElementGetCurrentContext()
|
||||
{
|
||||
// Don't retain here. Caller will retain if it wants to!
|
||||
return get_tls_context();
|
||||
}
|
||||
|
||||
void ASLayoutElementPopContext()
|
||||
{
|
||||
#ifdef MINIMAL_ASDK
|
||||
ASDisplayNodeCAssertNotNil(get_tls_context(), @"Attempt to pop context when there wasn't a context!");
|
||||
set_tls_context(nil);
|
||||
#else
|
||||
ASDisplayNodeCAssertNotNil(tls_context, @"Attempt to pop context when there wasn't a context!");
|
||||
CFRelease((__bridge CFTypeRef)tls_context);
|
||||
tls_context = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static pthread_key_t ASLayoutElementContextKey() {
|
||||
static pthread_key_t k;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
pthread_key_create(&k, NULL);
|
||||
});
|
||||
return k;
|
||||
}
|
||||
void ASLayoutElementPushContext(ASLayoutElementContext *context)
|
||||
{
|
||||
// NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
|
||||
ASDisplayNodeCAssertNil(pthread_getspecific(ASLayoutElementContextKey()), @"Nested ASLayoutElementContexts aren't supported.");
|
||||
|
||||
let cfCtx = (__bridge_retained CFTypeRef)context;
|
||||
pthread_setspecific(ASLayoutElementContextKey(), cfCtx);
|
||||
}
|
||||
|
||||
ASLayoutElementContext *ASLayoutElementGetCurrentContext()
|
||||
{
|
||||
// Don't retain here. Caller will retain if it wants to!
|
||||
let ctxPtr = pthread_getspecific(ASLayoutElementContextKey());
|
||||
return (__bridge ASLayoutElementContext *)ctxPtr;
|
||||
}
|
||||
|
||||
void ASLayoutElementPopContext()
|
||||
{
|
||||
let ctx = (CFTypeRef)pthread_getspecific(ASLayoutElementContextKey());
|
||||
ASDisplayNodeCAssertNotNil(ctx, @"Attempt to pop context when there wasn't a context!");
|
||||
CFRelease(ctx);
|
||||
pthread_setspecific(ASLayoutElementContextKey(), NULL);
|
||||
}
|
||||
|
||||
#endif // AS_TLS_AVAILABLE
|
||||
|
||||
#pragma mark - ASLayoutElementStyle
|
||||
|
||||
NSString * const ASLayoutElementStyleWidthProperty = @"ASLayoutElementStyleWidthProperty";
|
||||
NSString * const ASLayoutElementStyleMinWidthProperty = @"ASLayoutElementStyleMinWidthProperty";
|
||||
NSString * const ASLayoutElementStyleMaxWidthProperty = @"ASLayoutElementStyleMaxWidthProperty";
|
||||
|
||||
NSString * const ASLayoutElementStyleHeightProperty = @"ASLayoutElementStyleHeightProperty";
|
||||
NSString * const ASLayoutElementStyleMinHeightProperty = @"ASLayoutElementStyleMinHeightProperty";
|
||||
NSString * const ASLayoutElementStyleMaxHeightProperty = @"ASLayoutElementStyleMaxHeightProperty";
|
||||
|
||||
NSString * const ASLayoutElementStyleSpacingBeforeProperty = @"ASLayoutElementStyleSpacingBeforeProperty";
|
||||
NSString * const ASLayoutElementStyleSpacingAfterProperty = @"ASLayoutElementStyleSpacingAfterProperty";
|
||||
NSString * const ASLayoutElementStyleFlexGrowProperty = @"ASLayoutElementStyleFlexGrowProperty";
|
||||
NSString * const ASLayoutElementStyleFlexShrinkProperty = @"ASLayoutElementStyleFlexShrinkProperty";
|
||||
NSString * const ASLayoutElementStyleFlexBasisProperty = @"ASLayoutElementStyleFlexBasisProperty";
|
||||
NSString * const ASLayoutElementStyleAlignSelfProperty = @"ASLayoutElementStyleAlignSelfProperty";
|
||||
NSString * const ASLayoutElementStyleAscenderProperty = @"ASLayoutElementStyleAscenderProperty";
|
||||
NSString * const ASLayoutElementStyleDescenderProperty = @"ASLayoutElementStyleDescenderProperty";
|
||||
|
||||
NSString * const ASLayoutElementStyleLayoutPositionProperty = @"ASLayoutElementStyleLayoutPositionProperty";
|
||||
|
||||
#if YOGA
|
||||
NSString * const ASYogaFlexWrapProperty = @"ASLayoutElementStyleLayoutFlexWrapProperty";
|
||||
NSString * const ASYogaFlexDirectionProperty = @"ASYogaFlexDirectionProperty";
|
||||
NSString * const ASYogaDirectionProperty = @"ASYogaDirectionProperty";
|
||||
NSString * const ASYogaSpacingProperty = @"ASYogaSpacingProperty";
|
||||
NSString * const ASYogaJustifyContentProperty = @"ASYogaJustifyContentProperty";
|
||||
NSString * const ASYogaAlignItemsProperty = @"ASYogaAlignItemsProperty";
|
||||
NSString * const ASYogaPositionTypeProperty = @"ASYogaPositionTypeProperty";
|
||||
NSString * const ASYogaPositionProperty = @"ASYogaPositionProperty";
|
||||
NSString * const ASYogaMarginProperty = @"ASYogaMarginProperty";
|
||||
NSString * const ASYogaPaddingProperty = @"ASYogaPaddingProperty";
|
||||
NSString * const ASYogaBorderProperty = @"ASYogaBorderProperty";
|
||||
NSString * const ASYogaAspectRatioProperty = @"ASYogaAspectRatioProperty";
|
||||
#endif
|
||||
|
||||
#define ASLayoutElementStyleSetSizeWithScope(x) \
|
||||
__instanceLock__.lock(); \
|
||||
ASLayoutElementSize newSize = _size.load(); \
|
||||
{ x } \
|
||||
_size.store(newSize); \
|
||||
__instanceLock__.unlock();
|
||||
|
||||
#define ASLayoutElementStyleCallDelegate(propertyName)\
|
||||
do {\
|
||||
[self propertyDidChange:propertyName];\
|
||||
[_delegate style:self propertyDidChange:propertyName];\
|
||||
} while(0)
|
||||
|
||||
@implementation ASLayoutElementStyle {
|
||||
ASDN::RecursiveMutex __instanceLock__;
|
||||
ASLayoutElementStyleExtensions _extensions;
|
||||
|
||||
std::atomic<ASLayoutElementSize> _size;
|
||||
std::atomic<CGFloat> _spacingBefore;
|
||||
std::atomic<CGFloat> _spacingAfter;
|
||||
std::atomic<CGFloat> _flexGrow;
|
||||
std::atomic<CGFloat> _flexShrink;
|
||||
std::atomic<ASDimension> _flexBasis;
|
||||
std::atomic<ASStackLayoutAlignSelf> _alignSelf;
|
||||
std::atomic<CGFloat> _ascender;
|
||||
std::atomic<CGFloat> _descender;
|
||||
std::atomic<CGPoint> _layoutPosition;
|
||||
|
||||
#if YOGA
|
||||
YGNodeRef _yogaNode;
|
||||
std::atomic<YGWrap> _flexWrap;
|
||||
std::atomic<ASStackLayoutDirection> _flexDirection;
|
||||
std::atomic<YGDirection> _direction;
|
||||
std::atomic<ASStackLayoutJustifyContent> _justifyContent;
|
||||
std::atomic<ASStackLayoutAlignItems> _alignItems;
|
||||
std::atomic<YGPositionType> _positionType;
|
||||
std::atomic<ASEdgeInsets> _position;
|
||||
std::atomic<ASEdgeInsets> _margin;
|
||||
std::atomic<ASEdgeInsets> _padding;
|
||||
std::atomic<ASEdgeInsets> _border;
|
||||
std::atomic<CGFloat> _aspectRatio;
|
||||
#endif
|
||||
}
|
||||
|
||||
@dynamic width, height, minWidth, maxWidth, minHeight, maxHeight;
|
||||
@dynamic preferredSize, minSize, maxSize, preferredLayoutSize, minLayoutSize, maxLayoutSize;
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithDelegate:(id<ASLayoutElementStyleDelegate>)delegate
|
||||
{
|
||||
self = [self init];
|
||||
if (self) {
|
||||
_delegate = delegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_size = ASLayoutElementSizeMake();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
ASSynthesizeLockingMethodsWithMutex(__instanceLock__)
|
||||
|
||||
#pragma mark - ASLayoutElementStyleSize
|
||||
|
||||
- (ASLayoutElementSize)size
|
||||
{
|
||||
return _size.load();
|
||||
}
|
||||
|
||||
- (void)setSize:(ASLayoutElementSize)size
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize = size;
|
||||
});
|
||||
// No CallDelegate method as ASLayoutElementSize is currently internal.
|
||||
}
|
||||
|
||||
#pragma mark - ASLayoutElementStyleSizeForwarding
|
||||
|
||||
- (ASDimension)width
|
||||
{
|
||||
return _size.load().width;
|
||||
}
|
||||
|
||||
- (void)setWidth:(ASDimension)width
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.width = width;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty);
|
||||
}
|
||||
|
||||
- (ASDimension)height
|
||||
{
|
||||
return _size.load().height;
|
||||
}
|
||||
|
||||
- (void)setHeight:(ASDimension)height
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.height = height;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty);
|
||||
}
|
||||
|
||||
- (ASDimension)minWidth
|
||||
{
|
||||
return _size.load().minWidth;
|
||||
}
|
||||
|
||||
- (void)setMinWidth:(ASDimension)minWidth
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.minWidth = minWidth;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty);
|
||||
}
|
||||
|
||||
- (ASDimension)maxWidth
|
||||
{
|
||||
return _size.load().maxWidth;
|
||||
}
|
||||
|
||||
- (void)setMaxWidth:(ASDimension)maxWidth
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.maxWidth = maxWidth;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty);
|
||||
}
|
||||
|
||||
- (ASDimension)minHeight
|
||||
{
|
||||
return _size.load().minHeight;
|
||||
}
|
||||
|
||||
- (void)setMinHeight:(ASDimension)minHeight
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.minHeight = minHeight;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty);
|
||||
}
|
||||
|
||||
- (ASDimension)maxHeight
|
||||
{
|
||||
return _size.load().maxHeight;
|
||||
}
|
||||
|
||||
- (void)setMaxHeight:(ASDimension)maxHeight
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.maxHeight = maxHeight;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - ASLayoutElementStyleSizeHelpers
|
||||
|
||||
- (void)setPreferredSize:(CGSize)preferredSize
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.width = ASDimensionMakeWithPoints(preferredSize.width);
|
||||
newSize.height = ASDimensionMakeWithPoints(preferredSize.height);
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty);
|
||||
}
|
||||
|
||||
- (CGSize)preferredSize
|
||||
{
|
||||
ASLayoutElementSize size = _size.load();
|
||||
if (size.width.unit == ASDimensionUnitFraction) {
|
||||
NSCAssert(NO, @"Cannot get preferredSize of element with fractional width. Width: %@.", NSStringFromASDimension(size.width));
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
if (size.height.unit == ASDimensionUnitFraction) {
|
||||
NSCAssert(NO, @"Cannot get preferredSize of element with fractional height. Height: %@.", NSStringFromASDimension(size.height));
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
return CGSizeMake(size.width.value, size.height.value);
|
||||
}
|
||||
|
||||
- (void)setMinSize:(CGSize)minSize
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.minWidth = ASDimensionMakeWithPoints(minSize.width);
|
||||
newSize.minHeight = ASDimensionMakeWithPoints(minSize.height);
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty);
|
||||
}
|
||||
|
||||
- (void)setMaxSize:(CGSize)maxSize
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.maxWidth = ASDimensionMakeWithPoints(maxSize.width);
|
||||
newSize.maxHeight = ASDimensionMakeWithPoints(maxSize.height);
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty);
|
||||
}
|
||||
|
||||
- (ASLayoutSize)preferredLayoutSize
|
||||
{
|
||||
ASLayoutElementSize size = _size.load();
|
||||
return ASLayoutSizeMake(size.width, size.height);
|
||||
}
|
||||
|
||||
- (void)setPreferredLayoutSize:(ASLayoutSize)preferredLayoutSize
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.width = preferredLayoutSize.width;
|
||||
newSize.height = preferredLayoutSize.height;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty);
|
||||
}
|
||||
|
||||
- (ASLayoutSize)minLayoutSize
|
||||
{
|
||||
ASLayoutElementSize size = _size.load();
|
||||
return ASLayoutSizeMake(size.minWidth, size.minHeight);
|
||||
}
|
||||
|
||||
- (void)setMinLayoutSize:(ASLayoutSize)minLayoutSize
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.minWidth = minLayoutSize.width;
|
||||
newSize.minHeight = minLayoutSize.height;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty);
|
||||
}
|
||||
|
||||
- (ASLayoutSize)maxLayoutSize
|
||||
{
|
||||
ASLayoutElementSize size = _size.load();
|
||||
return ASLayoutSizeMake(size.maxWidth, size.maxHeight);
|
||||
}
|
||||
|
||||
- (void)setMaxLayoutSize:(ASLayoutSize)maxLayoutSize
|
||||
{
|
||||
ASLayoutElementStyleSetSizeWithScope({
|
||||
newSize.maxWidth = maxLayoutSize.width;
|
||||
newSize.maxHeight = maxLayoutSize.height;
|
||||
});
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty);
|
||||
}
|
||||
|
||||
#pragma mark - ASStackLayoutElement
|
||||
|
||||
- (void)setSpacingBefore:(CGFloat)spacingBefore
|
||||
{
|
||||
_spacingBefore.store(spacingBefore);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingBeforeProperty);
|
||||
}
|
||||
|
||||
- (CGFloat)spacingBefore
|
||||
{
|
||||
return _spacingBefore.load();
|
||||
}
|
||||
|
||||
- (void)setSpacingAfter:(CGFloat)spacingAfter
|
||||
{
|
||||
_spacingAfter.store(spacingAfter);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingAfterProperty);
|
||||
}
|
||||
|
||||
- (CGFloat)spacingAfter
|
||||
{
|
||||
return _spacingAfter.load();
|
||||
}
|
||||
|
||||
- (void)setFlexGrow:(CGFloat)flexGrow
|
||||
{
|
||||
_flexGrow.store(flexGrow);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexGrowProperty);
|
||||
}
|
||||
|
||||
- (CGFloat)flexGrow
|
||||
{
|
||||
return _flexGrow.load();
|
||||
}
|
||||
|
||||
- (void)setFlexShrink:(CGFloat)flexShrink
|
||||
{
|
||||
_flexShrink.store(flexShrink);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexShrinkProperty);
|
||||
}
|
||||
|
||||
- (CGFloat)flexShrink
|
||||
{
|
||||
return _flexShrink.load();
|
||||
}
|
||||
|
||||
- (void)setFlexBasis:(ASDimension)flexBasis
|
||||
{
|
||||
_flexBasis.store(flexBasis);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexBasisProperty);
|
||||
}
|
||||
|
||||
- (ASDimension)flexBasis
|
||||
{
|
||||
return _flexBasis.load();
|
||||
}
|
||||
|
||||
- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf
|
||||
{
|
||||
_alignSelf.store(alignSelf);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAlignSelfProperty);
|
||||
}
|
||||
|
||||
- (ASStackLayoutAlignSelf)alignSelf
|
||||
{
|
||||
return _alignSelf.load();
|
||||
}
|
||||
|
||||
- (void)setAscender:(CGFloat)ascender
|
||||
{
|
||||
_ascender.store(ascender);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAscenderProperty);
|
||||
}
|
||||
|
||||
- (CGFloat)ascender
|
||||
{
|
||||
return _ascender.load();
|
||||
}
|
||||
|
||||
- (void)setDescender:(CGFloat)descender
|
||||
{
|
||||
_descender.store(descender);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleDescenderProperty);
|
||||
}
|
||||
|
||||
- (CGFloat)descender
|
||||
{
|
||||
return _descender.load();
|
||||
}
|
||||
|
||||
#pragma mark - ASAbsoluteLayoutElement
|
||||
|
||||
- (void)setLayoutPosition:(CGPoint)layoutPosition
|
||||
{
|
||||
_layoutPosition.store(layoutPosition);
|
||||
ASLayoutElementStyleCallDelegate(ASLayoutElementStyleLayoutPositionProperty);
|
||||
}
|
||||
|
||||
- (CGPoint)layoutPosition
|
||||
{
|
||||
return _layoutPosition.load();
|
||||
}
|
||||
|
||||
#pragma mark - Extensions
|
||||
|
||||
- (void)setLayoutOptionExtensionBool:(BOOL)value atIndex:(int)idx
|
||||
{
|
||||
NSCAssert(idx < kMaxLayoutElementBoolExtensions, @"Setting index outside of max bool extensions space");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_extensions.boolExtensions[idx] = value;
|
||||
}
|
||||
|
||||
- (BOOL)layoutOptionExtensionBoolAtIndex:(int)idx\
|
||||
{
|
||||
NSCAssert(idx < kMaxLayoutElementBoolExtensions, @"Accessing index outside of max bool extensions space");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _extensions.boolExtensions[idx];
|
||||
}
|
||||
|
||||
- (void)setLayoutOptionExtensionInteger:(NSInteger)value atIndex:(int)idx
|
||||
{
|
||||
NSCAssert(idx < kMaxLayoutElementStateIntegerExtensions, @"Setting index outside of max integer extensions space");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_extensions.integerExtensions[idx] = value;
|
||||
}
|
||||
|
||||
- (NSInteger)layoutOptionExtensionIntegerAtIndex:(int)idx
|
||||
{
|
||||
NSCAssert(idx < kMaxLayoutElementStateIntegerExtensions, @"Accessing index outside of max integer extensions space");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _extensions.integerExtensions[idx];
|
||||
}
|
||||
|
||||
- (void)setLayoutOptionExtensionEdgeInsets:(UIEdgeInsets)value atIndex:(int)idx
|
||||
{
|
||||
NSCAssert(idx < kMaxLayoutElementStateEdgeInsetExtensions, @"Setting index outside of max edge insets extensions space");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_extensions.edgeInsetsExtensions[idx] = value;
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)layoutOptionExtensionEdgeInsetsAtIndex:(int)idx
|
||||
{
|
||||
NSCAssert(idx < kMaxLayoutElementStateEdgeInsetExtensions, @"Accessing index outside of max edge insets extensions space");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _extensions.edgeInsetsExtensions[idx];
|
||||
}
|
||||
|
||||
#pragma mark - Debugging
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return ASObjectDescriptionMake(self, [self propertiesForDescription]);
|
||||
}
|
||||
|
||||
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
|
||||
{
|
||||
NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
|
||||
|
||||
if ((self.minLayoutSize.width.unit != ASDimensionUnitAuto ||
|
||||
self.minLayoutSize.height.unit != ASDimensionUnitAuto)) {
|
||||
[result addObject:@{ @"minLayoutSize" : NSStringFromASLayoutSize(self.minLayoutSize) }];
|
||||
}
|
||||
|
||||
if ((self.preferredLayoutSize.width.unit != ASDimensionUnitAuto ||
|
||||
self.preferredLayoutSize.height.unit != ASDimensionUnitAuto)) {
|
||||
[result addObject:@{ @"preferredSize" : NSStringFromASLayoutSize(self.preferredLayoutSize) }];
|
||||
}
|
||||
|
||||
if ((self.maxLayoutSize.width.unit != ASDimensionUnitAuto ||
|
||||
self.maxLayoutSize.height.unit != ASDimensionUnitAuto)) {
|
||||
[result addObject:@{ @"maxLayoutSize" : NSStringFromASLayoutSize(self.maxLayoutSize) }];
|
||||
}
|
||||
|
||||
if (self.alignSelf != ASStackLayoutAlignSelfAuto) {
|
||||
[result addObject:@{ @"alignSelf" : [@[@"ASStackLayoutAlignSelfAuto",
|
||||
@"ASStackLayoutAlignSelfStart",
|
||||
@"ASStackLayoutAlignSelfEnd",
|
||||
@"ASStackLayoutAlignSelfCenter",
|
||||
@"ASStackLayoutAlignSelfStretch"] objectAtIndex:self.alignSelf] }];
|
||||
}
|
||||
|
||||
if (self.ascender != 0) {
|
||||
[result addObject:@{ @"ascender" : @(self.ascender) }];
|
||||
}
|
||||
|
||||
if (self.descender != 0) {
|
||||
[result addObject:@{ @"descender" : @(self.descender) }];
|
||||
}
|
||||
|
||||
if (ASDimensionEqualToDimension(self.flexBasis, ASDimensionAuto) == NO) {
|
||||
[result addObject:@{ @"flexBasis" : NSStringFromASDimension(self.flexBasis) }];
|
||||
}
|
||||
|
||||
if (self.flexGrow != 0) {
|
||||
[result addObject:@{ @"flexGrow" : @(self.flexGrow) }];
|
||||
}
|
||||
|
||||
if (self.flexShrink != 0) {
|
||||
[result addObject:@{ @"flexShrink" : @(self.flexShrink) }];
|
||||
}
|
||||
|
||||
if (self.spacingAfter != 0) {
|
||||
[result addObject:@{ @"spacingAfter" : @(self.spacingAfter) }];
|
||||
}
|
||||
|
||||
if (self.spacingBefore != 0) {
|
||||
[result addObject:@{ @"spacingBefore" : @(self.spacingBefore) }];
|
||||
}
|
||||
|
||||
if (CGPointEqualToPoint(self.layoutPosition, CGPointZero) == NO) {
|
||||
[result addObject:@{ @"layoutPosition" : [NSValue valueWithCGPoint:self.layoutPosition] }];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)propertyDidChange:(NSString *)propertyName
|
||||
{
|
||||
#if YOGA
|
||||
/* TODO(appleguy): STYLE SETTER METHODS LEFT TO IMPLEMENT
|
||||
void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow);
|
||||
void YGNodeStyleSetFlex(YGNodeRef node, float flex);
|
||||
*/
|
||||
|
||||
if (_yogaNode == NULL) {
|
||||
return;
|
||||
}
|
||||
// Because the NSStrings used to identify each property are const, use efficient pointer comparison.
|
||||
if (propertyName == ASLayoutElementStyleWidthProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, Width, self.width);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleMinWidthProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, MinWidth, self.minWidth);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleMaxWidthProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, MaxWidth, self.maxWidth);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleHeightProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, Height, self.height);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleMinHeightProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, MinHeight, self.minHeight);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleMaxHeightProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, MaxHeight, self.maxHeight);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleFlexGrowProperty) {
|
||||
YGNodeStyleSetFlexGrow(_yogaNode, self.flexGrow);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleFlexShrinkProperty) {
|
||||
YGNodeStyleSetFlexShrink(_yogaNode, self.flexShrink);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleFlexBasisProperty) {
|
||||
YGNODE_STYLE_SET_DIMENSION(_yogaNode, FlexBasis, self.flexBasis);
|
||||
}
|
||||
else if (propertyName == ASLayoutElementStyleAlignSelfProperty) {
|
||||
YGNodeStyleSetAlignSelf(_yogaNode, yogaAlignSelf(self.alignSelf));
|
||||
}
|
||||
else if (propertyName == ASYogaFlexWrapProperty) {
|
||||
YGNodeStyleSetFlexWrap(_yogaNode, self.flexWrap);
|
||||
}
|
||||
else if (propertyName == ASYogaFlexDirectionProperty) {
|
||||
YGNodeStyleSetFlexDirection(_yogaNode, yogaFlexDirection(self.flexDirection));
|
||||
}
|
||||
else if (propertyName == ASYogaDirectionProperty) {
|
||||
YGNodeStyleSetDirection(_yogaNode, self.direction);
|
||||
}
|
||||
else if (propertyName == ASYogaJustifyContentProperty) {
|
||||
YGNodeStyleSetJustifyContent(_yogaNode, yogaJustifyContent(self.justifyContent));
|
||||
}
|
||||
else if (propertyName == ASYogaAlignItemsProperty) {
|
||||
ASStackLayoutAlignItems alignItems = self.alignItems;
|
||||
if (alignItems != ASStackLayoutAlignItemsNotSet) {
|
||||
YGNodeStyleSetAlignItems(_yogaNode, yogaAlignItems(alignItems));
|
||||
}
|
||||
}
|
||||
else if (propertyName == ASYogaPositionTypeProperty) {
|
||||
YGNodeStyleSetPositionType(_yogaNode, self.positionType);
|
||||
}
|
||||
else if (propertyName == ASYogaPositionProperty) {
|
||||
ASEdgeInsets position = self.position;
|
||||
YGEdge edge = YGEdgeLeft;
|
||||
for (int i = 0; i < YGEdgeAll + 1; ++i) {
|
||||
YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge);
|
||||
edge = (YGEdge)(edge + 1);
|
||||
}
|
||||
}
|
||||
else if (propertyName == ASYogaMarginProperty) {
|
||||
ASEdgeInsets margin = self.margin;
|
||||
YGEdge edge = YGEdgeLeft;
|
||||
for (int i = 0; i < YGEdgeAll + 1; ++i) {
|
||||
YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge);
|
||||
edge = (YGEdge)(edge + 1);
|
||||
}
|
||||
}
|
||||
else if (propertyName == ASYogaPaddingProperty) {
|
||||
ASEdgeInsets padding = self.padding;
|
||||
YGEdge edge = YGEdgeLeft;
|
||||
for (int i = 0; i < YGEdgeAll + 1; ++i) {
|
||||
YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge);
|
||||
edge = (YGEdge)(edge + 1);
|
||||
}
|
||||
}
|
||||
else if (propertyName == ASYogaBorderProperty) {
|
||||
ASEdgeInsets border = self.border;
|
||||
YGEdge edge = YGEdgeLeft;
|
||||
for (int i = 0; i < YGEdgeAll + 1; ++i) {
|
||||
YGNODE_STYLE_SET_FLOAT_WITH_EDGE(_yogaNode, Border, dimensionForEdgeWithEdgeInsets(edge, border), edge);
|
||||
edge = (YGEdge)(edge + 1);
|
||||
}
|
||||
}
|
||||
else if (propertyName == ASYogaAspectRatioProperty) {
|
||||
CGFloat aspectRatio = self.aspectRatio;
|
||||
if (aspectRatio > FLT_EPSILON && aspectRatio < CGFLOAT_MAX / 2.0) {
|
||||
YGNodeStyleSetAspectRatio(_yogaNode, aspectRatio);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Yoga Flexbox Properties
|
||||
|
||||
#if YOGA
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
[super initialize];
|
||||
YGConfigSetPointScaleFactor(YGConfigGetDefault(), ASScreenScale());
|
||||
// Yoga recommends using Web Defaults for all new projects. This will be enabled for Texture very soon.
|
||||
//YGConfigSetUseWebDefaults(YGConfigGetDefault(), true);
|
||||
}
|
||||
|
||||
- (YGNodeRef)yogaNode
|
||||
{
|
||||
return _yogaNode;
|
||||
}
|
||||
|
||||
- (YGNodeRef)yogaNodeCreateIfNeeded
|
||||
{
|
||||
if (_yogaNode == NULL) {
|
||||
_yogaNode = YGNodeNew();
|
||||
}
|
||||
return _yogaNode;
|
||||
}
|
||||
|
||||
- (void)destroyYogaNode
|
||||
{
|
||||
if (_yogaNode != NULL) {
|
||||
// Release the __bridge_retained Context object.
|
||||
ASLayoutElementYogaUpdateMeasureFunc(_yogaNode, nil);
|
||||
YGNodeFree(_yogaNode);
|
||||
_yogaNode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self destroyYogaNode];
|
||||
}
|
||||
|
||||
- (YGWrap)flexWrap { return _flexWrap.load(); }
|
||||
- (ASStackLayoutDirection)flexDirection { return _flexDirection.load(); }
|
||||
- (YGDirection)direction { return _direction.load(); }
|
||||
- (ASStackLayoutJustifyContent)justifyContent { return _justifyContent.load(); }
|
||||
- (ASStackLayoutAlignItems)alignItems { return _alignItems.load(); }
|
||||
- (YGPositionType)positionType { return _positionType.load(); }
|
||||
- (ASEdgeInsets)position { return _position.load(); }
|
||||
- (ASEdgeInsets)margin { return _margin.load(); }
|
||||
- (ASEdgeInsets)padding { return _padding.load(); }
|
||||
- (ASEdgeInsets)border { return _border.load(); }
|
||||
- (CGFloat)aspectRatio { return _aspectRatio.load(); }
|
||||
|
||||
- (void)setFlexWrap:(YGWrap)flexWrap {
|
||||
_flexWrap.store(flexWrap);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaFlexWrapProperty);
|
||||
}
|
||||
- (void)setFlexDirection:(ASStackLayoutDirection)flexDirection {
|
||||
_flexDirection.store(flexDirection);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaFlexDirectionProperty);
|
||||
}
|
||||
- (void)setDirection:(YGDirection)direction {
|
||||
_direction.store(direction);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaDirectionProperty);
|
||||
}
|
||||
- (void)setJustifyContent:(ASStackLayoutJustifyContent)justify {
|
||||
_justifyContent.store(justify);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaJustifyContentProperty);
|
||||
}
|
||||
- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems {
|
||||
_alignItems.store(alignItems);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaAlignItemsProperty);
|
||||
}
|
||||
- (void)setPositionType:(YGPositionType)positionType {
|
||||
_positionType.store(positionType);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaPositionTypeProperty);
|
||||
}
|
||||
- (void)setPosition:(ASEdgeInsets)position {
|
||||
_position.store(position);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaPositionProperty);
|
||||
}
|
||||
- (void)setMargin:(ASEdgeInsets)margin {
|
||||
_margin.store(margin);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaMarginProperty);
|
||||
}
|
||||
- (void)setPadding:(ASEdgeInsets)padding {
|
||||
_padding.store(padding);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaPaddingProperty);
|
||||
}
|
||||
- (void)setBorder:(ASEdgeInsets)border {
|
||||
_border.store(border);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaBorderProperty);
|
||||
}
|
||||
- (void)setAspectRatio:(CGFloat)aspectRatio {
|
||||
_aspectRatio.store(aspectRatio);
|
||||
ASLayoutElementStyleCallDelegate(ASYogaAspectRatioProperty);
|
||||
}
|
||||
|
||||
#endif /* YOGA */
|
||||
|
||||
@end
|
||||
@ -839,24 +839,6 @@ static UIColor *defaultTintColor = nil;
|
||||
if (flags.setSublayerTransform)
|
||||
layer.sublayerTransform = sublayerTransform;
|
||||
|
||||
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)
|
||||
layer.masksToBounds = clipsToBounds;
|
||||
|
||||
@ -916,6 +898,24 @@ static UIColor *defaultTintColor = nil;
|
||||
|
||||
ASPendingStateApplyMetricsToLayer(self, layer);
|
||||
|
||||
if (flags.setContents)
|
||||
layer.contents = contents;
|
||||
|
||||
if (flags.setContentsScale)
|
||||
layer.contentsScale = contentsScale;
|
||||
|
||||
if (flags.setRasterizationScale)
|
||||
layer.rasterizationScale = rasterizationScale;
|
||||
|
||||
if (flags.setContentsGravity)
|
||||
layer.contentsGravity = contentsGravity;
|
||||
|
||||
if (flags.setContentsRect)
|
||||
layer.contentsRect = contentsRect;
|
||||
|
||||
if (flags.setContentsCenter)
|
||||
layer.contentsCenter = contentsCenter;
|
||||
|
||||
if (flags.needsLayout)
|
||||
[layer setNeedsLayout];
|
||||
|
||||
@ -958,24 +958,6 @@ static UIColor *defaultTintColor = nil;
|
||||
if (flags.setSublayerTransform)
|
||||
layer.sublayerTransform = sublayerTransform;
|
||||
|
||||
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;
|
||||
|
||||
@ -1143,6 +1125,24 @@ static UIColor *defaultTintColor = nil;
|
||||
ASPendingStateApplyMetricsToLayer(self, layer);
|
||||
}
|
||||
|
||||
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.needsLayout)
|
||||
[view setNeedsLayout];
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user