refactor(ios): image component refactor (#1591)

* feat(ios): add hippy image data loader

* feat(ios): add hippy image data loader protocol

* feat(ios): add data request for path

* refactor(ios): refactor image component data loader
This commit is contained in:
luomy
2022-03-04 19:50:06 +08:00
committed by GitHub
parent b95e1168f4
commit ea6902efe8
15 changed files with 535 additions and 501 deletions
@@ -123,7 +123,6 @@
0691853226281E05005D599D /* CALayer+HippyAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0691853126281E05005D599D /* CALayer+HippyAnimation.m */; };
06F2A76F2519E319000AF839 /* HippyJSEnginesMapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 06F2A76E2519E319000AF839 /* HippyJSEnginesMapper.mm */; };
06F3E3E025A5D867007934F6 /* HippyListTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 06F3E3DF25A5D867007934F6 /* HippyListTableView.m */; };
06FF8BD62511BC0400C03900 /* HippyImageProviderProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 06FF8BD52511BC0400C03900 /* HippyImageProviderProtocol.m */; };
06FF8EEC2511C10B00C03900 /* HippyDefaultImageProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 06FF8EEB2511C10B00C03900 /* HippyDefaultImageProvider.m */; };
06FF8EEF2511C20900C03900 /* NSData+DataType.m in Sources */ = {isa = PBXBuildFile; fileRef = 06FF8EEE2511C20900C03900 /* NSData+DataType.m */; };
781580042795449A006E92D4 /* HippyDevInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 7815800327954499006E92D4 /* HippyDevInfo.m */; };
@@ -206,6 +205,7 @@
F48291AA274E5BB600D91128 /* HippyOCTurboModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = F48291A7274E5BB500D91128 /* HippyOCTurboModule.mm */; };
F48291AB274E5BB600D91128 /* HippyTurboModuleManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = F48291A8274E5BB500D91128 /* HippyTurboModuleManager.mm */; };
F48291AC274E5BB600D91128 /* NSObject+HippyTurbo.mm in Sources */ = {isa = PBXBuildFile; fileRef = F48291A9274E5BB500D91128 /* NSObject+HippyTurbo.mm */; };
F4833E6727CF24320054A33D /* HippyImageDataLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F4833E6627CF24320054A33D /* HippyImageDataLoader.m */; };
F4AFCB1627BE29D50058AAB1 /* UIView+HippyEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4AFCB1527BE29D50058AAB1 /* UIView+HippyEvent.mm */; };
F4CB9D29279270D3008D6F2C /* UIView+RootViewRegister.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4CB9D28279270D3008D6F2C /* UIView+RootViewRegister.mm */; };
F4D0028F2675C7D5004A43B6 /* HippyGradientObject.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D0028E2675C7D5004A43B6 /* HippyGradientObject.m */; };
@@ -475,7 +475,6 @@
06F3E3DE25A5D867007934F6 /* HippyListTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyListTableView.h; sourceTree = "<group>"; };
06F3E3DF25A5D867007934F6 /* HippyListTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyListTableView.m; sourceTree = "<group>"; };
06FF8BD42511BC0400C03900 /* HippyImageProviderProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyImageProviderProtocol.h; sourceTree = "<group>"; };
06FF8BD52511BC0400C03900 /* HippyImageProviderProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyImageProviderProtocol.m; sourceTree = "<group>"; };
06FF8EEA2511C10B00C03900 /* HippyDefaultImageProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyDefaultImageProvider.h; sourceTree = "<group>"; };
06FF8EEB2511C10B00C03900 /* HippyDefaultImageProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyDefaultImageProvider.m; sourceTree = "<group>"; };
06FF8EED2511C20900C03900 /* NSData+DataType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSData+DataType.h"; sourceTree = "<group>"; };
@@ -654,6 +653,9 @@
F48291A7274E5BB500D91128 /* HippyOCTurboModule.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HippyOCTurboModule.mm; sourceTree = "<group>"; };
F48291A8274E5BB500D91128 /* HippyTurboModuleManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HippyTurboModuleManager.mm; sourceTree = "<group>"; };
F48291A9274E5BB500D91128 /* NSObject+HippyTurbo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSObject+HippyTurbo.mm"; sourceTree = "<group>"; };
F4833E6527CF24320054A33D /* HippyImageDataLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyImageDataLoader.h; sourceTree = "<group>"; };
F4833E6627CF24320054A33D /* HippyImageDataLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyImageDataLoader.m; sourceTree = "<group>"; };
F4833E6827CF462C0054A33D /* HippyImageDataLoaderProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyImageDataLoaderProtocol.h; sourceTree = "<group>"; };
F4AFCB1427BE29D50058AAB1 /* UIView+HippyEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+HippyEvent.h"; sourceTree = "<group>"; };
F4AFCB1527BE29D50058AAB1 /* UIView+HippyEvent.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIView+HippyEvent.mm"; sourceTree = "<group>"; };
F4CB9D27279270D3008D6F2C /* UIView+RootViewRegister.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+RootViewRegister.h"; sourceTree = "<group>"; };
@@ -1009,9 +1011,11 @@
064C594323AB1A51001E80DD /* HippyAnimatedImageView.h */,
064C594723AB1A51001E80DD /* HippyAnimatedImageView.m */,
06FF8BD42511BC0400C03900 /* HippyImageProviderProtocol.h */,
06FF8BD52511BC0400C03900 /* HippyImageProviderProtocol.m */,
06FF8EEA2511C10B00C03900 /* HippyDefaultImageProvider.h */,
06FF8EEB2511C10B00C03900 /* HippyDefaultImageProvider.m */,
F4833E6527CF24320054A33D /* HippyImageDataLoader.h */,
F4833E6627CF24320054A33D /* HippyImageDataLoader.m */,
F4833E6827CF462C0054A33D /* HippyImageDataLoaderProtocol.h */,
);
path = image;
sourceTree = "<group>";
@@ -1909,7 +1913,6 @@
781580042795449A006E92D4 /* HippyDevInfo.m in Sources */,
064C5A4A23AB1A51001E80DD /* HippyModuleMethod.mm in Sources */,
D84AB9C6273A618B00987D03 /* thread.cc in Sources */,
06FF8BD62511BC0400C03900 /* HippyImageProviderProtocol.m in Sources */,
F4E7B43E276C61EB00F17139 /* dom_event.cc in Sources */,
D84AB9BF273A618B00987D03 /* timer_module.cc in Sources */,
D84AB9C5273A618B00987D03 /* task.cc in Sources */,
@@ -1924,6 +1927,7 @@
064C59F223AB1A51001E80DD /* HippyNetWork.m in Sources */,
064C5A0523AB1A51001E80DD /* HippyClipboardModule.m in Sources */,
F45D1BDD26B1555E004010EE /* HippyI18nUtils.m in Sources */,
F4833E6727CF24320054A33D /* HippyImageDataLoader.m in Sources */,
787C199E2726A1020062EBFF /* HippyDomModel.mm in Sources */,
7882DFDC272929D300C12149 /* HippyCSSPropsDefine.m in Sources */,
064C5A2A23AB1A51001E80DD /* HippyBackgroundImageCacheManager.m in Sources */,
+3 -1
View File
@@ -783,7 +783,9 @@ HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithBundleURL
if (!_valid) {
return;
}
_domManager->TerminateTaskRunner();
if (_domManager) {
_domManager->TerminateTaskRunner();
}
HippyAssertMainQueue();
HippyAssert(_javaScriptExecutor != nil, @"Can't complete invalidation without a JS executor");
@@ -35,6 +35,8 @@
@implementation HippyDefaultImageProvider
@synthesize imageDataPath;
HIPPY_EXPORT_MODULE(defaultImageProvider)
+ (BOOL)canHandleData:(NSData *)data {
@@ -46,26 +48,22 @@ HIPPY_EXPORT_MODULE(defaultImageProvider)
return ret;
}
+ (NSUInteger)priorityForData:(NSData *)data {
return 0;
}
+ (instancetype)imageProviderInstanceForData:(NSData *)data {
return [[[self class] alloc] initWithData:data];
}
- (instancetype)initWithData:(NSData *)data {
self = [super init];
if (self) {
if ([[self class] isAnimatedImage:data]) {
_imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
} else {
_data = data;
}
[self setImageData:data];
}
return self;
}
- (void)setImageData:(NSData *)imageData {
if ([[self class] isAnimatedImage:imageData]) {
_imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
} else {
_data = imageData;
}
}
- (UIImage *)image {
if (nil == _image) {
if (_data) {
@@ -0,0 +1,37 @@
/*!
* iOS SDK
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "HippyImageDataLoaderProtocol.h"
NS_ASSUME_NONNULL_BEGIN
extern NSString *const HippyImageDataLoaderErrorDomain;
extern const NSUInteger HippyImageDataLoaderErrorFileNotExists;
extern const NSUInteger HippyImageDataLoaderErrorNoPathIncoming;
extern const NSUInteger HippyImageDataLoaderErrorPathToUrl;
@interface HippyImageDataLoader : NSObject<HippyImageDataLoaderProtocol>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,231 @@
/*!
* iOS SDK
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "HippyImageDataLoader.h"
#import "HippyUtils.h"
#import "HippyImageCacheManager.h"
NSString *const HippyImageDataLoaderErrorDomain = @"HippyImageDataLoaderErrorDomain";
const NSUInteger HippyImageDataLoaderErrorFileNotExists = 1000;
const NSUInteger HippyImageDataLoaderErrorNoPathIncoming = 1001;
const NSUInteger HippyImageDataLoaderErrorPathToUrl = 1002;
typedef NS_ENUM(NSUInteger, ImagePathType) {
ImagePathTypeUnknown,
ImagePathTypeHTTPPath,
ImagePathTypeFilePath,
ImagePathTypeBase64Path,
};
typedef void(^DownloadProgress)(NSUInteger, NSUInteger);
typedef void(^CompletionBlock)(id, NSString *, NSError *);
static ImagePathType checkPathTypeFromPath(NSString *path) {
ImagePathType type = ImagePathTypeUnknown;
if ([path hasPrefix:@"http"]) {
type = ImagePathTypeHTTPPath;
}
else if ([path hasPrefix:@"file://"]) {
type = ImagePathTypeFilePath;
}
else if ([path containsString:@"data:image/"] && [path containsString:@";base64,"]) {
type = ImagePathTypeBase64Path;
}
return type;
}
static NSOperationQueue *ImageDataLoaderQueue(void) {
static dispatch_once_t onceToken;
static NSOperationQueue *_hippy_image_queue = nil;
dispatch_once(&onceToken, ^{
_hippy_image_queue = [[NSOperationQueue alloc] init];
_hippy_image_queue.maxConcurrentOperationCount = 1;
});
return _hippy_image_queue;
}
@interface HippyImageDataLoader ()<NSURLSessionDataDelegate> {
ImagePathType _imagePathType;
NSURLSessionDataTask *_task;
NSMutableData *_imageData;
NSUInteger _imageDataTotalLength;
DownloadProgress _progress;
CompletionBlock _completion;
}
@end
@implementation HippyImageDataLoader
#pragma mark HippyImageDataLoaderProtocol Implementation
- (BOOL)canHandleImageAtPath:(NSString *)path {
ImagePathType type = checkPathTypeFromPath(path);
return ImagePathTypeUnknown != type;
}
- (void)loadImageAtPath:(NSString *)path progress:(DownloadProgress)progress
completion:(CompletionBlock)dataCompletion {
ImagePathType type = checkPathTypeFromPath(path);
switch (type) {
case ImagePathTypeHTTPPath:
[self loadImageAtHTTPPath:path progress:progress dataCompletion:dataCompletion];
break;
case ImagePathTypeFilePath:
[self loadImageAtFilePath:path progress:progress dataCompletion:dataCompletion];
break;
case ImagePathTypeBase64Path:
[self loadImageAtBase64Path:path progress:progress dataCompletion:dataCompletion];
break;
default:
break;
}
}
- (void)cancelImageDownloadAtPath:(NSString *)path {
[_task cancel];
}
#pragma mark Image Data Loader
- (void)loadImageAtHTTPPath:(NSString *)path progress:(DownloadProgress)progress dataCompletion:(CompletionBlock)dataCompletion {
if (_task) {
[_task cancel];
}
NSURL *sourceUrl = HippyURLWithString(path, nil);
if (sourceUrl) {
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self
delegateQueue:ImageDataLoaderQueue()];
_task = [session dataTaskWithURL:sourceUrl];
[_task resume];
_progress = progress;
_completion = dataCompletion;
}
}
- (void)loadImageAtFilePath:(NSString *)path progress:(DownloadProgress)progress dataCompletion:(CompletionBlock)dataCompletion {
if (path) {
BOOL isDirectory = NO;
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
if (fileExists) {
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:&error];
if (error) {
dataCompletion(nil, path, error);
}
else {
NSUInteger length = [data length];
if (progress) {
progress(length, length);
}
if (dataCompletion) {
dataCompletion(data, path, nil);
}
}
}else {
if (dataCompletion) {
NSError *error = [NSError errorWithDomain:HippyImageDataLoaderErrorDomain code:HippyImageDataLoaderErrorFileNotExists userInfo:@{@"path": path}];
dataCompletion(nil, path, error);
}
}
}else {
if (dataCompletion) {
NSError *error = [NSError errorWithDomain:HippyImageDataLoaderErrorDomain code:HippyImageDataLoaderErrorNoPathIncoming userInfo:nil];
dataCompletion(nil, path, error);
}
}
}
- (void)loadImageAtBase64Path:(NSString *)path progress:(DownloadProgress)progress dataCompletion:(CompletionBlock)dataCompletion {
if (path) {
NSURL *url = [NSURL URLWithString:path];
if (url) {
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:&error];
if (error) {
dataCompletion(nil, path, error);
}else {
NSUInteger length = [data length];
if (progress) {
progress(length, length);
}
if (dataCompletion) {
dataCompletion(data, path, nil);
}
}
}else {
if (dataCompletion) {
NSError *error = [NSError errorWithDomain:HippyImageDataLoaderErrorDomain code:HippyImageDataLoaderErrorNoPathIncoming userInfo:nil];
dataCompletion(nil, path, error);
}
}
}else {
if (dataCompletion) {
NSError *error = [NSError errorWithDomain:HippyImageDataLoaderErrorDomain code:HippyImageDataLoaderErrorNoPathIncoming userInfo:nil];
dataCompletion(nil, path, error);
}
}
}
#pragma mark URL request delegate implementation
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
if (_task == dataTask) {
_imageDataTotalLength = response.expectedContentLength;
completionHandler(NSURLSessionResponseAllow);
NSUInteger capacity =
NSURLResponseUnknownLength != _imageDataTotalLength ? (NSUInteger)_imageDataTotalLength : 256;
_imageData = [[NSMutableData alloc] initWithCapacity:capacity];
}
}
- (void)URLSession:(__unused NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
if (_task == dataTask) {
[_imageData appendData:data];
if (_progress) {
_progress([_imageData length], _imageDataTotalLength);
}
}
}
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
if (_task == task) {
if (_progress) {
_progress([_imageData length], _imageDataTotalLength);
}
if (_completion) {
_completion(_imageData, [[[task originalRequest] URL] absoluteString], error);
}
}
[session finishTasksAndInvalidate];
}
- (void)dealloc {
[_task cancel];
}
@end
@@ -0,0 +1,43 @@
/*!
* iOS SDK
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol HippyImageDataLoaderProtocol <NSObject>
@optional
- (BOOL)canHandleImageAtPath:(NSString *)path;
@required
- (void)loadImageAtPath:(NSString *)path progress:(void (^)(NSUInteger, NSUInteger))progress
completion:(void (^)(id, NSString *, NSError *))completion;
- (void)cancelImageDownloadAtPath:(NSString *)path;
@end
NS_ASSUME_NONNULL_END
@@ -37,14 +37,14 @@
*/
+ (BOOL)isAnimatedImage:(NSData *)data;
/** return priority for data
* instance with higher priority will process data
*/
+ (NSUInteger)priorityForData:(NSData *)data;
@property(nonatomic, copy)NSString *imageDataPath;
/** return image provider instance for data
/**
* Set image data for provider
*
* @param data Image data
*/
+ (instancetype)imageProviderInstanceForData:(NSData *)data;
- (void)setImageData:(NSData *)data;
/** return image.If it is an animated image,return first frame
*/
@@ -1,51 +0,0 @@
/*!
* iOS SDK
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "HippyImageProviderProtocol.h"
#import "objc/runtime.h"
#import "HippyBridge.h"
Class<HippyImageProviderProtocol> imageProviderClassFromBridge(NSData *data, HippyBridge *bridge) {
NSSet<Class<HippyImageProviderProtocol>> *classes = [bridge imageProviders];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> *_Nullable bindings) {
if ([evaluatedObject conformsToProtocol:@protocol(HippyImageProviderProtocol)]) {
Class<HippyImageProviderProtocol> class = (Class<HippyImageProviderProtocol>)evaluatedObject;
return [class canHandleData:data];
} else {
return NO;
}
}];
NSSet<Class<HippyImageProviderProtocol>> *sub = [classes filteredSetUsingPredicate:predicate];
Class<HippyImageProviderProtocol> candidate = nil;
for (Class<HippyImageProviderProtocol> class in sub) {
if (nil == candidate) {
candidate = class;
} else {
NSUInteger candidatePriority = [candidate priorityForData:data];
NSUInteger classPriority = [class priorityForData:data];
if (classPriority > candidatePriority) {
candidate = class;
}
}
}
return candidate;
}
+1 -1
View File
@@ -78,7 +78,7 @@ typedef NS_ENUM(NSInteger, HippyResizeMode) {
- (void)updateImage:(UIImage *)image;
- (UIImage *)imageFromData:(NSData *)data;
- (void)setImageProvider:(id<HippyImageProviderProtocol>)imageProvider;
- (void)clearImageIfDetached;
+28 -326
View File
@@ -25,7 +25,6 @@
#import "HippyUtils.h"
#import "HippyImageViewCustomLoader.h"
#import "HippyBridge+LocalFileSource.h"
#import "HippyImageCacheManager.h"
#import "HippyAnimatedImage.h"
#import "HippyDefaultImageProvider.h"
#import <Accelerate/Accelerate.h>
@@ -46,16 +45,6 @@ typedef struct _BorderRadiusStruct {
CGFloat bottomRightRadius;
} BorderRadiusStruct;
static NSOperationQueue *hippy_image_queue() {
static dispatch_once_t onceToken;
static NSOperationQueue *_hippy_image_queue = nil;
dispatch_once(&onceToken, ^{
_hippy_image_queue = [[NSOperationQueue alloc] init];
_hippy_image_queue.maxConcurrentOperationCount = 1;
});
return _hippy_image_queue;
}
static NSOperationQueue *animated_image_queue() {
static dispatch_once_t onceToken;
static NSOperationQueue *_animatedImageOQ = nil;
@@ -197,15 +186,12 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
@end
@interface HippyImageView () {
NSURLSessionDataTask *_task;
NSURL *_imageLoadURL;
long long _totalLength;
NSMutableData *_data;
__weak CALayer *_borderWidthLayer;
BOOL _needsUpdateBorderRadius;
CGSize _size;
BOOL _needsReloadImage;
BOOL _needsUpdateImage;
id<HippyImageProviderProtocol> _imageProvider;
}
@property (nonatomic) HippyAnimatedImageOperation *animatedImageOperation;
@@ -236,7 +222,6 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
- (void)didMoveToWindow {
[super didMoveToWindow];
if (!self.window) {
[self cancelImageLoad];
} else if ([self shouldChangeImageSource]) {
[self reloadImage];
}
@@ -309,7 +294,6 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
- (void)setResizeMode:(HippyResizeMode)resizeMode {
if (_resizeMode != resizeMode) {
_resizeMode = resizeMode;
if (_resizeMode == HippyResizeModeRepeat) {
self.contentMode = UIViewContentModeScaleToFill;
} else {
@@ -328,6 +312,12 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
}
}
- (void)setImageProvider:(id<HippyImageProviderProtocol>)imageProvider {
if (_imageProvider != imageProvider) {
_imageProvider = imageProvider;
}
}
- (void)didSetProps:(NSArray<NSString *> *)changedProps {
if (_needsReloadImage) {
self.animatedImage = nil;
@@ -354,311 +344,40 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
}
- (void)reloadImage {
NSDictionary *source = [self.source firstObject];
if (source && CGRectGetWidth(self.frame) > 0 && CGRectGetHeight(self.frame) > 0) {
if (_onLoadStart) {
_onLoadStart(@{});
}
NSString *uri = source[@"uri"];
self.pendingImageSourceUri = uri;
BOOL isBlurredImage = NO;
NSData *data = [[HippyImageCacheManager sharedInstance] imageCacheDataForURLString:uri];
if (data) {
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(data, self.bridge);
id<HippyImageProviderProtocol> instance = [ipClass imageProviderInstanceForData:data];
if (instance) {
BOOL isAnimatedImage = [instance imageCount] > 1;
if (isAnimatedImage) {
if (_animatedImageOperation) {
[_animatedImageOperation cancel];
}
_animatedImageOperation = [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:instance imageView:self
imageURL:uri];
[animated_image_queue() addOperation:_animatedImageOperation];
} else {
UIImage *image = [instance image];
if (image) {
[self loadImage:image url:uri error:nil needBlur:!isBlurredImage needCache:YES];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable for uri %@", uri];
NSError *theError = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[self loadImage:nil url:uri error:theError needBlur:YES needCache:NO];
}
}
return;
if (_imageProvider) {
NSString *imagePath = _imageProvider.imageDataPath;
BOOL isAnimatedImage = [_imageProvider imageCount] > 1;
if (isAnimatedImage) {
if (_animatedImageOperation) {
[_animatedImageOperation cancel];
}
}
if ([uri hasPrefix:@"file://"]) {
NSURL *fileURL = [NSURL URLWithString:uri];
NSError *error = nil;
NSData *imageData = [NSData dataWithContentsOfURL:fileURL options:(NSDataReadingMappedIfSafe) error:&error];
if (error) {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable for uri %@", uri];
_animatedImageOperation =
[[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:_imageProvider
imageView:self
imageURL:imagePath];
[animated_image_queue() addOperation:_animatedImageOperation];
}else {
UIImage *image = [_imageProvider image];
if (image) {
[self loadImage:image url:imagePath error:nil needBlur:YES];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable for uri %@", _imageProvider.imageDataPath];
NSError *theError = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[self loadImage:nil url:uri error:theError needBlur:YES needCache:NO];
}
else {
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(imageData, self.bridge);
id<HippyImageProviderProtocol> instance = [self instanceImageProviderFromClass:ipClass imageData:imageData];
BOOL isAnimatedImage = [ipClass isAnimatedImage:imageData];
if (isAnimatedImage) {
if (_animatedImageOperation) {
[_animatedImageOperation cancel];
}
_animatedImageOperation = [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:instance imageView:self
imageURL:source[@"uri"]];
[animated_image_queue() addOperation:_animatedImageOperation];
} else {
UIImage *image = [instance image];
if (image) {
[self loadImage:image url:uri error:nil needBlur:YES needCache:YES];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable for uri %@", uri];
NSError *theError = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[self loadImage:nil url:uri error:theError needBlur:YES needCache:NO];
}
}
}
return;
}
NSData *uriData = [uri dataUsingEncoding:NSUTF8StringEncoding];
if (nil == uriData) {
return;
}
CFURLRef urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, NULL);
NSURL *source_url = CFBridgingRelease(urlRef);
if ([HippyBridge isHippyLocalFileURLString:uri]) {
NSString *localPath = [_bridge absoluteStringFromHippyLocalFileURLString:uri];
BOOL isDirectory = NO;
BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:localPath isDirectory:&isDirectory];
if (fileExist && !isDirectory) {
NSData *imageData = [NSData dataWithContentsOfFile:localPath];
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(imageData, self.bridge);
id<HippyImageProviderProtocol> instance = [self instanceImageProviderFromClass:ipClass imageData:imageData];
BOOL isAnimatedImage = [ipClass isAnimatedImage:imageData];
if (isAnimatedImage) {
if (_animatedImageOperation) {
[_animatedImageOperation cancel];
}
_animatedImageOperation = [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:instance imageView:self
imageURL:source[@"uri"]];
[animated_image_queue() addOperation:_animatedImageOperation];
} else {
UIImage *image = [instance image];
if (image) {
[self loadImage:image url:source_url.absoluteString error:nil needBlur:YES needCache:YES];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable for uri %@", source_url.absoluteString];
NSError *theError = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[self loadImage:nil url:source_url.absoluteString error:theError needBlur:YES needCache:NO];
}
}
} else {
NSString *errorMessage = [NSString stringWithFormat:@"local image data not exist %@", source_url.absoluteString];
NSError *error = imageErrorFromParams(ImageDataNotExist, errorMessage);
[self loadImage:nil url:source_url.absoluteString error:error needBlur:YES needCache:NO];
}
return;
}
__weak __typeof(self) weakSelf = self;
typedef void (^HandleBase64CompletedBlock)(NSString *);
HandleBase64CompletedBlock handleBase64CompletedBlock = ^void(NSString *base64Data) {
NSRange range = [base64Data rangeOfString:@";base64,"];
if (NSNotFound != range.location) {
base64Data = [base64Data substringFromIndex:range.location + range.length];
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64Data options:NSDataBase64DecodingIgnoreUnknownCharacters];
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(imageData, self.bridge);
id<HippyImageProviderProtocol> instance = [self instanceImageProviderFromClass:ipClass imageData:imageData];
BOOL isAnimatedImage = [ipClass isAnimatedImage:imageData];
if (isAnimatedImage) {
if (weakSelf.animatedImageOperation) {
[weakSelf.animatedImageOperation cancel];
}
weakSelf.animatedImageOperation = [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:instance imageView:self
imageURL:source[@"uri"]];
[animated_image_queue() addOperation:weakSelf.animatedImageOperation];
} else {
UIImage *image = [instance image];
if (image) {
[weakSelf loadImage:image url:source[@"uri"] error:nil needBlur:YES needCache:YES];
} else {
NSError *error = imageErrorFromParams(ImageDataUnavailable, @"base64 data not available");
[self loadImage:nil url:source[@"uri"] error:error needBlur:YES needCache:NO];
}
}
}
};
typedef void (^HandleImageCompletedBlock)(NSURL *);
HandleImageCompletedBlock handleImageCompletedBlock = ^void(NSURL *source_url) {
[weakSelf.bridge.imageLoader imageView:weakSelf loadAtUrl:source_url placeholderImage:weakSelf.defaultImage context:NULL
progress:^(long long currentLength, long long totalLength) {
if (weakSelf.onProgress) {
weakSelf.onProgress(@{@"loaded": @((double)currentLength), @"total": @((double)totalLength)});
}
}
completed:^(NSData *data, NSURL *url, NSError *error) {
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(data, self.bridge);
id<HippyImageProviderProtocol> instance = [self instanceImageProviderFromClass:ipClass imageData:data];
BOOL isAnimatedImage = [ipClass isAnimatedImage:data];
if (isAnimatedImage) {
if (weakSelf.animatedImageOperation) {
[weakSelf.animatedImageOperation cancel];
}
weakSelf.animatedImageOperation = [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:instance imageView:self
imageURL:source[@"uri"]];
[animated_image_queue() addOperation:weakSelf.animatedImageOperation];
} else {
UIImage *image = [instance image];
if (image) {
[weakSelf loadImage:image url:source[@"uri"] error:nil needBlur:YES needCache:YES];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable %@", source[@"uri"]];
NSError *error = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[weakSelf loadImage:nil url:source[@"uri"] error:error needBlur:YES needCache:NO];
}
}
}];
};
if (_bridge.imageLoader && source_url) {
if (_defaultImage) {
weakSelf.image = _defaultImage;
}
if ([[source_url absoluteString] hasPrefix:@"data:image/"]) {
handleBase64CompletedBlock([source_url absoluteString]);
} else {
if (_imageLoadURL) {
[_bridge.imageLoader cancelImageDownload:self withUrl:_imageLoadURL];
}
_imageLoadURL = source_url;
handleImageCompletedBlock(source_url);
}
} else {
if ([uri hasPrefix:@"data:image/"]) {
handleBase64CompletedBlock(uri);
} else {
if (_task) {
[self cancelImageLoad];
}
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:hippy_image_queue()];
_task = [session dataTaskWithURL:source_url];
[_task resume];
[self loadImage:nil url:imagePath error:theError needBlur:YES];
}
}
}
}
- (void)cancelImageLoad {
self.pendingImageSourceUri = nil;
NSDictionary *source = [self.source firstObject];
if (_bridge.imageLoader) {
[_animatedImageOperation cancel];
_animatedImageOperation = nil;
[_bridge.imageLoader cancelImageDownload:self withUrl:source[@"uri"]];
} else {
[_task cancel];
_task = nil;
[hippy_image_queue() addOperationWithBlock:^{
self->_data = nil;
self->_totalLength = 0;
}];
}
}
- (void)clearImage {
[self cancelImageLoad];
[self.layer removeAnimationForKey:@"contents"];
self.image = nil;
self.imageSourceUri = nil;
}
- (UIImage *)imageFromData:(NSData *)data {
if (nil == data) {
return nil;
}
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(data, self.bridge);
id<HippyImageProviderProtocol> instance = [self instanceImageProviderFromClass:ipClass imageData:data];
return [instance image];
}
#pragma mark -
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
if (_task == dataTask) {
_totalLength = response.expectedContentLength;
completionHandler(NSURLSessionResponseAllow);
NSUInteger capacity = NSURLResponseUnknownLength != _totalLength ? (NSUInteger)_totalLength : 256;
_data = [[NSMutableData alloc] initWithCapacity:capacity];
}
}
- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
if (_task == dataTask) {
if (_onProgress && NSURLResponseUnknownLength != _totalLength) {
_onProgress(@{ @"loaded": @((double)data.length), @"total": @((double)_totalLength) });
}
[_data appendData:data];
}
}
- (void)URLSession:(__unused NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
if (_task == task) {
NSString *urlString = [[[task originalRequest] URL] absoluteString];
if (!error) {
if ([_data length] > 0) {
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(_data, self.bridge);
id<HippyImageProviderProtocol> instance = [self instanceImageProviderFromClass:ipClass imageData:_data];
BOOL isAnimatedImage = [ipClass isAnimatedImage:_data];
if (isAnimatedImage) {
if (_animatedImageOperation) {
[_animatedImageOperation cancel];
}
_animatedImageOperation = [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:instance imageView:self
imageURL:urlString];
[animated_image_queue() addOperation:_animatedImageOperation];
} else {
UIImage *image = [self imageFromData:_data];
;
if (image) {
[[HippyImageCacheManager sharedInstance] setImageCacheData:_data forURLString:urlString];
[self loadImage:image url:urlString error:nil needBlur:YES needCache:YES];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"image data unavailable %@", urlString];
NSError *theError = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[self loadImage:nil url:urlString error:theError needBlur:YES needCache:NO];
}
}
} else {
NSURLResponse *response = [task response];
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSUInteger statusCode = [httpResponse statusCode];
NSString *errorMessage = [NSString stringWithFormat:@"no data received, HTTPStatusCode is %zd, url is %@", statusCode, urlString];
NSError *imgError = imageErrorFromParams(ImageDataUnavailable, errorMessage);
[self loadImage:nil url:urlString error:imgError needBlur:NO needCache:NO];
}
}
} else {
NSError *imgError = imageErrorFromParams(ImageDataReceivedError, error.localizedDescription);
[self loadImage:nil url:urlString error:imgError needBlur:YES needCache:NO];
}
}
[session finishTasksAndInvalidate];
}
#pragma mark -
- (void)loadImage:(UIImage *)image url:(NSString *)url error:(NSError *)error needBlur:(BOOL)needBlur needCache:(BOOL)needCache {
- (void)loadImage:(UIImage *)image url:(NSString *)url error:(NSError *)error needBlur:(BOOL)needBlur {
if (error) {
if (_onError && error.code != NSURLErrorCancelled) {
_onError(@{ @"error": error.localizedDescription, @"errorCode": @(error.code), @"errorURL": url });
@@ -668,8 +387,7 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
}
return;
}
NSDictionary *source = [self.source firstObject];
NSString *currentImageURLString = [source objectForKey:@"uri"];
NSString *currentImageURLString = _imageProvider.imageDataPath;
BOOL shouldContinue = url && currentImageURLString && [url isEqualToString:currentImageURLString];
if (!shouldContinue) {
return;
@@ -705,18 +423,12 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
NSError *finalError = HippyErrorFromErrorAndModuleName(error, self.bridge.moduleName);
HippyFatal(finalError);
}
if (needCache) {
[[HippyImageCacheManager sharedInstance] setImage:blurredImage forURLString:url blurRadius:br];
}
HippyExecuteOnMainQueue(^{
setImageBlock(blurredImage);
});
});
} else {
HippyExecuteOnMainQueue(^{
if (needCache) {
[[HippyImageCacheManager sharedInstance] setImage:image forURLString:url blurRadius:br];
}
setImageBlock(image);
});
}
@@ -885,16 +597,6 @@ NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) {
return radius;
}
- (id<HippyImageProviderProtocol>)instanceImageProviderFromClass:(Class<HippyImageProviderProtocol>)cls imageData:(NSData *)data {
id<HippyImageProviderProtocol> instance = [cls imageProviderInstanceForData:data];
if ([instance isKindOfClass:[HippyDefaultImageProvider class]]) {
HippyDefaultImageProvider *provider = (HippyDefaultImageProvider *)instance;
provider.imageViewSize = _size;
provider.downSample = _downSample;
}
return instance;
}
@end
@implementation UIImage (Hippy)
@@ -958,7 +660,7 @@ HIPPY_ENUM_CONVERTER(HippyResizeMode, (@{
dispatch_async(dispatch_get_main_queue(), ^{
HippyImageView *sIV = wIV;
NSString *sURL = wURL;
[sIV loadImage:animatedImage.posterImage url:sURL error:nil needBlur:YES needCache:NO];
[sIV loadImage:animatedImage.posterImage url:sURL error:nil needBlur:YES];
sIV.animatedImage = animatedImage;
});
}
@@ -21,7 +21,13 @@
*/
#import "HippyViewManager.h"
#import "HippyImageDataLoaderProtocol.h"
#import "HippyImageProviderProtocol.h"
@interface HippyImageViewManager : HippyViewManager
@property(nonatomic, strong)Class<HippyImageDataLoaderProtocol> imageDataLoaderClass;
@property(nonatomic, strong)Class<HippyImageProviderProtocol> imageProviderClass;
@end
+103 -20
View File
@@ -23,9 +23,17 @@
#import "HippyImageViewManager.h"
#import "HippyImageView.h"
#import "HippyConvert.h"
#import "HippyBridge+LocalFileSource.h"
#import "HippyImageDataLoader.h"
#import "HippyDefaultImageProvider.h"
#import "objc/runtime.h"
#import <UIKit/UIKit.h>
@interface HippyImageViewManager () {
NSMapTable<UIView *, id<HippyImageDataLoaderProtocol>> *_imageViewLoaderMapTable;
}
@end
@implementation HippyImageViewManager
HIPPY_EXPORT_MODULE(Image)
@@ -33,7 +41,6 @@ HIPPY_EXPORT_MODULE(Image)
HIPPY_EXPORT_VIEW_PROPERTY(blurRadius, CGFloat)
HIPPY_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets)
HIPPY_EXPORT_VIEW_PROPERTY(resizeMode, HippyResizeMode)
HIPPY_EXPORT_VIEW_PROPERTY(source, NSArray)
HIPPY_EXPORT_VIEW_PROPERTY(onLoadStart, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(onProgress, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(onError, HippyDirectEventBlock)
@@ -42,6 +49,37 @@ HIPPY_EXPORT_VIEW_PROPERTY(onLoad, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(onLoadEnd, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(downSample, BOOL)
HIPPY_CUSTOM_VIEW_PROPERTY(source, NSArray, HippyImageView) {
NSArray *pathSources = [HippyConvert NSArray:json];
if ([pathSources isKindOfClass:[NSArray class]]) {
NSDictionary *dicSource = [pathSources firstObject];
NSString *path = dicSource[@"uri"];
id<HippyImageDataLoaderProtocol> imageDataLoader = [self imageDataLoaderForView:view];
__weak HippyImageView *weakView = view;
[imageDataLoader loadImageAtPath:path progress:^(NSUInteger current, NSUInteger total) {
} completion:^(id result, NSString *path, NSError *error) {
if (!error && weakView) {
dispatch_async(dispatch_get_main_queue(), ^{
if (weakView) {
HippyImageView *strongView = weakView;
if ([result isKindOfClass:[UIImage class]]) {
[strongView updateImage:(UIImage *)result];
}
else if ([result isKindOfClass:[NSData class]]) {
Class cls = [self imageProviderClass];
id<HippyImageProviderProtocol> imageProvider = [[cls alloc] init];
imageProvider.imageDataPath = path;
[imageProvider setImageData:(NSData *)result];
[strongView setImageProvider:imageProvider];
[strongView reloadImage];
}
}
});
}
}];
}
}
HIPPY_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, HippyImageView) {
view.tintColor = [HippyConvert UIColor:json] ?: defaultView.tintColor;
view.renderingMode = json ? UIImageRenderingModeAlwaysTemplate : defaultView.renderingMode;
@@ -49,25 +87,27 @@ HIPPY_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, HippyImageView) {
HIPPY_CUSTOM_VIEW_PROPERTY(defaultSource, NSString, HippyImageView) {
NSString *source = [HippyConvert NSString:json];
if ([HippyBridge isHippyLocalFileURLString:source]) {
NSString *localPath = [self.bridge absoluteStringFromHippyLocalFileURLString:source];
BOOL isDirectory = NO;
BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:localPath isDirectory:&isDirectory];
if (fileExist && !isDirectory) {
NSData *imageData = [NSData dataWithContentsOfFile:localPath];
UIImage *image = [view imageFromData:imageData];
view.defaultImage = image;
id<HippyImageDataLoaderProtocol> imageDataLoader = [self imageDataLoaderForView:view];
__weak HippyImageView *weakView = view;
[imageDataLoader loadImageAtPath:source progress:^(NSUInteger current, NSUInteger total) {
} completion:^(id result, NSString *path, NSError *error) {
if (!error && weakView) {
dispatch_async(dispatch_get_main_queue(), ^{
if (weakView) {
HippyImageView *strongView = weakView;
if ([result isKindOfClass:[NSData class]]) {
Class cls = [self imageProviderClass];
id<HippyImageProviderProtocol> imageProvider = [[cls alloc] init];
[imageProvider setImageData:(NSData *)result];
strongView.defaultImage = [imageProvider image];
}
else if ([result isKindOfClass:[UIImage class]]) {
strongView.defaultImage = (UIImage *)result;
}
}
});
}
}
else if ([source hasPrefix:@"data:image/"]) {
NSRange range = [source rangeOfString:@";base64,"];
if (NSNotFound != range.location) {
source = [source substringFromIndex:range.location + range.length];
}
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:source options:NSDataBase64DecodingIgnoreUnknownCharacters];
UIImage *image = [view imageFromData:imageData];
view.defaultImage = image;
}
}];
}
#define HIPPY_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \
@@ -86,4 +126,47 @@ HIPPY_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
return [[HippyImageView alloc] initWithBridge:self.bridge];
}
- (Class<HippyImageDataLoaderProtocol>)imageDataLoaderClass {
if (!_imageDataLoaderClass) {
_imageDataLoaderClass = [HippyImageDataLoader class];
}
return _imageDataLoaderClass;
}
- (Class<HippyImageProviderProtocol>)imageProviderClass {
if (!_imageProviderClass) {
_imageProviderClass = [HippyDefaultImageProvider class];
}
return _imageProviderClass;
}
- (id<HippyImageDataLoaderProtocol>)getNewImageDataLoaderInstance {
Class cls = [self imageDataLoaderClass];
return [[cls alloc] init];
}
- (id<HippyImageProviderProtocol>)getNewImageProviderInstance {
Class cls = [self imageProviderClass];
return [[cls alloc] init];
}
- (NSMapTable *)imageViewLoaderMapTable {
if (_imageViewLoaderMapTable) {
_imageViewLoaderMapTable = [NSMapTable weakToStrongObjectsMapTable];
}
return _imageViewLoaderMapTable;
}
- (id<HippyImageDataLoaderProtocol>)imageDataLoaderForView:(UIView *)view {
id<HippyImageDataLoaderProtocol> loader = nil;
if (view) {
loader = objc_getAssociatedObject(view, @selector(imageDataLoaderForView:));
if (!loader) {
loader = [self getNewImageDataLoaderInstance];
objc_setAssociatedObject(view, @selector(imageDataLoaderForView:), loader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
return loader;
}
@end
@@ -24,7 +24,6 @@
#import "HippyBridge.h"
#import "HippyImageLoaderModule.h"
#import "HippyImageCacheManager.h"
#import "HippyImageProviderProtocol.h"
@implementation HippyImageLoaderModule
@@ -34,52 +33,53 @@ HIPPY_EXPORT_MODULE(ImageLoaderModule)
// clang-format off
HIPPY_EXPORT_METHOD(getSize:(NSString *)urlString resolver:(HippyPromiseResolveBlock)resolve rejecter:(HippyPromiseRejectBlock)reject) {
UIImage *image = [[HippyImageCacheManager sharedInstance] loadImageFromCacheForURLString:urlString radius:0 isBlurredImage:nil];
if (image) {
NSDictionary *dic = @{@"width": @(image.size.width), @"height": @(image.size.height)};
resolve(dic);
return;
}
NSData *uriData = [urlString dataUsingEncoding:NSUTF8StringEncoding];
if (nil == uriData) {
NSError *error = [NSError errorWithDomain:@"ImageLoaderModuleDomain" code:1 userInfo:@{@"reason": @"url parse error"}];
reject(@"1", @"url parse error", error);
return;
}
CFURLRef urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, NULL);
NSURL *source_url = CFBridgingRelease(urlRef);
typedef void (^HandleCompletedBlock)(BOOL, NSData *, NSURL *, NSError *);
HandleCompletedBlock completedBlock = ^void(BOOL cached, NSData *data, NSURL *url, NSError *error) {
if (error) {
NSError *error = [NSError errorWithDomain:@"ImageLoaderModuleDomain" code:1 userInfo:@{@"reason": @"url parse error"}];
reject(@"2", @"url request error", error);
} else {
if (!cached) {
[[HippyImageCacheManager sharedInstance] setImageCacheData:data forURLString:urlString];
}
Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(data,self.bridge);
id<HippyImageProviderProtocol> instance = [ipClass imageProviderInstanceForData:data];
UIImage *image = [instance image];
if (image) {
NSDictionary *dic = @{@"width": @(image.size.width), @"height": @(image.size.height)};
resolve(dic);
} else {
NSError *error = [NSError errorWithDomain:@"ImageLoaderModuleDomain" code:2 userInfo:@{@"reason": @"image parse error"}];
reject(@"2", @"image request error", error);
}
}
};
if (_bridge.imageLoader && [_bridge.imageLoader respondsToSelector: @selector(loadImage:completed:)]) {
[_bridge.imageLoader loadImage: source_url completed:^(NSData *data, NSURL *url, NSError *error, BOOL cached) {
completedBlock(cached, data, url, error);
}];
} else {
[[[NSURLSession sharedSession] dataTaskWithURL:source_url completionHandler:^(NSData * _Nullable data, __unused NSURLResponse * _Nullable response, NSError * _Nullable error) {
completedBlock(NO, data, source_url, error);
}] resume];
}
//TODO complete this method
// UIImage *image = [[HippyImageCacheManager sharedInstance] loadImageFromCacheForURLString:urlString radius:0 isBlurredImage:nil];
// if (image) {
// NSDictionary *dic = @{@"width": @(image.size.width), @"height": @(image.size.height)};
// resolve(dic);
// return;
// }
// NSData *uriData = [urlString dataUsingEncoding:NSUTF8StringEncoding];
// if (nil == uriData) {
// NSError *error = [NSError errorWithDomain:@"ImageLoaderModuleDomain" code:1 userInfo:@{@"reason": @"url parse error"}];
// reject(@"1", @"url parse error", error);
// return;
// }
// CFURLRef urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, NULL);
// NSURL *source_url = CFBridgingRelease(urlRef);
//
// typedef void (^HandleCompletedBlock)(BOOL, NSData *, NSURL *, NSError *);
// HandleCompletedBlock completedBlock = ^void(BOOL cached, NSData *data, NSURL *url, NSError *error) {
// if (error) {
// NSError *error = [NSError errorWithDomain:@"ImageLoaderModuleDomain" code:1 userInfo:@{@"reason": @"url parse error"}];
// reject(@"2", @"url request error", error);
// } else {
// if (!cached) {
// [[HippyImageCacheManager sharedInstance] setImageCacheData:data forURLString:urlString];
// }
// Class<HippyImageProviderProtocol> ipClass = imageProviderClassFromBridge(data,self.bridge);
// id<HippyImageProviderProtocol> instance = [ipClass imageProviderInstanceForData:data];
// UIImage *image = [instance image];
// if (image) {
// NSDictionary *dic = @{@"width": @(image.size.width), @"height": @(image.size.height)};
// resolve(dic);
// } else {
// NSError *error = [NSError errorWithDomain:@"ImageLoaderModuleDomain" code:2 userInfo:@{@"reason": @"image parse error"}];
// reject(@"2", @"image request error", error);
// }
// }
// };
//
// if (_bridge.imageLoader && [_bridge.imageLoader respondsToSelector: @selector(loadImage:completed:)]) {
// [_bridge.imageLoader loadImage: source_url completed:^(NSData *data, NSURL *url, NSError *error, BOOL cached) {
// completedBlock(cached, data, url, error);
// }];
// } else {
// [[[NSURLSession sharedSession] dataTaskWithURL:source_url completionHandler:^(NSData * _Nullable data, __unused NSURLResponse * _Nullable response, NSError * _Nullable error) {
// completedBlock(NO, data, source_url, error);
// }] resume];
// }
}
// clang-format on
+9 -6
View File
@@ -515,7 +515,7 @@ BOOL HippyIsLocalAssetURL(NSURL *__nullable imageURL) {
return [extension isEqualToString:@"png"] || [extension isEqualToString:@"jpg"];
}
HIPPY_EXTERN NSString *__nullable HippyTempFilePath(NSString *extension, NSError **error) {
NSString *__nullable HippyTempFilePath(NSString *extension, NSError **error) {
static NSError *setupError = nil;
static NSString *directory;
static dispatch_once_t onceToken;
@@ -695,7 +695,7 @@ UIColor *HippyConvertStringToColor(NSString *colorString) {
return color;
}
HIPPY_EXTERN UIColor *HippyConvertNumberToColor(NSInteger colorNumber) {
UIColor *HippyConvertNumberToColor(NSInteger colorNumber) {
NSInteger a = (colorNumber >> 24) & 0xFF;
NSInteger r = (colorNumber >> 16) & 0xFF;
NSInteger g = (colorNumber >> 8) & 0xFF;
@@ -765,15 +765,18 @@ NSURL *__nullable HippyURLByReplacingQueryParam(NSURL *__nullable URL, NSString
NSURL *__nullable HippyURLWithString(NSString *URLString, NSString *baseURLString) {
if (URLString) {
NSURL *baseURL = HippyURLWithString(baseURLString, NULL);
CFURLRef URLRef = CFURLCreateWithString(NULL, (CFStringRef)URLString, (CFURLRef)baseURL);
if (URLRef) {
return CFBridgingRelease(URLRef);
NSData *uriData = [URLString dataUsingEncoding:NSUTF8StringEncoding];
if (nil == uriData) {
return nil;
}
CFURLRef urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, (__bridge CFURLRef)baseURL);
NSURL *source_url = CFBridgingRelease(urlRef);
return source_url;
}
return nil;
}
HIPPY_EXTERN NSStringEncoding HippyGetStringEncodingFromURLResponse(NSURLResponse *response) {
NSStringEncoding HippyGetStringEncodingFromURLResponse(NSURLResponse *response) {
NSString *textEncoding = [response textEncodingName];
if (!textEncoding) {
return NSUTF8StringEncoding;
+2 -26
View File
@@ -127,7 +127,6 @@ using HPViewBinding = std::unordered_map<int32_t, std::tuple<std::vector<int32_t
static void HippyTraverseViewNodes(id<HippyComponent> view, void (^block)(id<HippyComponent>)) {
if (view.hippyTag) {
block(view);
for (id<HippyComponent> subview in view.hippySubviews) {
HippyTraverseViewNodes(subview, block);
}
@@ -462,7 +461,6 @@ dispatch_queue_t HippyGetUIManagerQueue(void) {
[(id<HippyInvalidating>)subview invalidate];
}
[registry removeObjectForKey:subview.hippyTag];
if (registry == (NSMutableDictionary<NSNumber *, id<HippyComponent>> *)self->_viewRegistry) {
[self->_bridgeTransactionListeners removeObject:subview];
}
@@ -470,28 +468,6 @@ dispatch_queue_t HippyGetUIManagerQueue(void) {
}
}
/**
* Disassociates children from container. Doesn't remove from registries.
* @returns Array of removed items.
*/
- (NSArray<id<HippyComponent>> *)childrenToRemoveFromContainer:(id<HippyComponent>)container atIndices:(NSArray<NSNumber *> *)atIndices {
// If there are no indices to move or the container has no subviews don't bother
// We support parents with nil subviews so long as they're all nil so this allows for this behavior
if (atIndices.count == 0 || [container hippySubviews].count == 0) {
return nil;
}
// Construction of removed children must be done "up front", before indices are disturbed by removals.
NSMutableArray<id<HippyComponent>> *removedChildren = [NSMutableArray arrayWithCapacity:atIndices.count];
HippyAssert(container != nil, @"container view (for ID %@) not found", container);
for (NSNumber *indexNumber in atIndices) {
NSUInteger index = indexNumber.unsignedIntegerValue;
if (index < [container hippySubviews].count) {
[removedChildren addObject:[container hippySubviews][index]];
}
}
return removedChildren;
}
- (void)removeChildren:(NSArray<id<HippyComponent>> *)children fromContainer:(id<HippyComponent>)container {
for (id<HippyComponent> removedChild in children) {
[container removeHippySubview:removedChild];
@@ -884,14 +860,14 @@ dispatch_queue_t HippyGetUIManagerQueue(void) {
int32_t tag = dom_node->GetId();
HippyShadowView *shadowView = _shadowViewRegistry[@(tag)];
[shadowView removeFromHippySuperview];
[_shadowViewRegistry removeObjectForKey:@(tag)];
[self purgeChildren:@[shadowView] fromRegistry:(NSMutableDictionary<NSNumber *, id<HippyComponent>> *)_shadowViewRegistry];
__weak auto weakSelf = self;
[self addUIBlock:^(HippyUIManager *uiManager, NSDictionary<NSNumber *,__kindof UIView *> *viewRegistry) {
UIView *view = viewRegistry[@(tag)];
[view removeFromHippySuperview];
if (weakSelf) {
auto strongSelf = weakSelf;
[strongSelf->_viewRegistry removeObjectForKey:@(tag)];
[strongSelf purgeChildren:@[view] fromRegistry:(NSMutableDictionary<NSNumber *, id<HippyComponent>> *)(strongSelf->_viewRegistry)];
}
}];
}