seperate iOS project from main project
This commit is contained in:
22
Example/Pods/YYWebImage/LICENSE
generated
Normal file
22
Example/Pods/YYWebImage/LICENSE
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 ibireme <ibireme@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
308
Example/Pods/YYWebImage/README.md
generated
Executable file
308
Example/Pods/YYWebImage/README.md
generated
Executable file
@@ -0,0 +1,308 @@
|
||||
YYWebImage
|
||||
==============
|
||||
[](https://raw.githubusercontent.com/ibireme/YYWebImage/master/LICENSE)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoapods.org/?q= YYWebImage)
|
||||
[](http://cocoapods.org/?q= YYWebImage)
|
||||
[](https://www.apple.com/nl/ios/)
|
||||
[](https://travis-ci.org/ibireme/YYWebImage)
|
||||
|
||||

|
||||
|
||||
YYWebImage is an asynchronous image loading framework (a component of [YYKit](https://github.com/ibireme/YYKit)).
|
||||
|
||||
It was created as an improved replacement for SDWebImage, PINRemoteImage and FLAnimatedImage.
|
||||
|
||||
It use [YYCache](https://github.com/ibireme/YYCache) to support memory and disk cache, and [YYImage](https://github.com/ibireme/YYImage) to support WebP/APNG/GIF image decode.<br/>
|
||||
See these project for more information.
|
||||
|
||||
|
||||
Features
|
||||
==============
|
||||
- Asynchronous image load from remote or local URL.
|
||||
- Animated WebP, APNG, GIF support (dynamic buffer, lower memory usage).
|
||||
- Baseline/progressive/interlaced image decode support.
|
||||
- Image loading category for UIImageView, UIButton, MKAnnotationView and CALayer.
|
||||
- Image effect: blur, round corner, resize, color tint, crop, rotate and more.
|
||||
- High performance memory and disk image cache.
|
||||
- High performance image loader to avoid main thread blocked.
|
||||
- Fully documented.
|
||||
|
||||
Usage
|
||||
==============
|
||||
|
||||
###Load image from URL
|
||||
|
||||
// load from remote url
|
||||
imageView.yy_imageURL = [NSURL URLWithString:@"http://github.com/logo.png"];
|
||||
|
||||
// load from local url
|
||||
imageView.yy_imageURL = [NSURL fileURLWithPath:@"/tmp/logo.png"];
|
||||
|
||||
|
||||
###Load animated image
|
||||
|
||||
// just replace `UIImageView` with `YYAnimatedImageView`
|
||||
UIImageView *imageView = [YYAnimatedImageView new];
|
||||
imageView.yy_imageURL = [NSURL URLWithString:@"http://github.com/ani.webp"];
|
||||
|
||||
|
||||
###Load image progressively
|
||||
|
||||
// progressive
|
||||
[imageView yy_setImageWithURL:url options:YYWebImageOptionProgressive];
|
||||
|
||||
// progressive with blur and fade animation (see the demo at the top of this page)
|
||||
[imageView yy_setImageWithURL:url options:YYWebImageOptionProgressiveBlur | YYWebImageOptionSetImageWithFadeAnimation];
|
||||
|
||||
|
||||
###Load and process image
|
||||
|
||||
// 1. download image from remote
|
||||
// 2. get download progress
|
||||
// 3. resize image and add round corner
|
||||
// 4. set image with a fade animation
|
||||
|
||||
[imageView yy_setImageWithURL:url
|
||||
placeholder:nil
|
||||
options:YYWebImageOptionSetImageWithFadeAnimation
|
||||
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
progress = (float)receivedSize / expectedSize;
|
||||
}
|
||||
transform:^UIImage *(UIImage *image, NSURL *url) {
|
||||
image = [image yy_imageByResizeToSize:CGSizeMake(100, 100) contentMode:UIViewContentModeCenter];
|
||||
return [image yy_imageByRoundCornerRadius:10];
|
||||
}
|
||||
completion:^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
if (from == YYWebImageFromDiskCache) {
|
||||
NSLog(@"load from disk cache");
|
||||
}
|
||||
}];
|
||||
|
||||
###Image Cache
|
||||
YYImageCache *cache = [YYWebImageManager sharedManager].cache;
|
||||
|
||||
// get cache capacity
|
||||
cache.memoryCache.totalCost;
|
||||
cache.memoryCache.totalCount;
|
||||
cache.diskCache.totalCost;
|
||||
cache.diskCache.totalCount;
|
||||
|
||||
// clear cache
|
||||
[cache.memoryCache removeAllObjects];
|
||||
[cache.diskCache removeAllObjects];
|
||||
|
||||
// clear disk cache with progress
|
||||
[cache.diskCache removeAllObjectsWithProgressBlock:^(int removedCount, int totalCount) {
|
||||
// progress
|
||||
} endBlock:^(BOOL error) {
|
||||
// end
|
||||
}];
|
||||
|
||||
Installation
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. Update cocoapods to the latest version.
|
||||
2. Add `pod 'YYWebImage'` to your Podfile.
|
||||
3. Run `pod install` or `pod update`.
|
||||
4. Import \<YYWebImage/YYWebImage.h\>.
|
||||
5. Notice: it doesn't include WebP subspec by default, if you want to support WebP format, you may add `pod 'YYImage/WebP'` to your Podfile. You may call `YYImageWebPAvailable()` to check whether the WebP subspec is installed correctly.
|
||||
|
||||
### Carthage
|
||||
|
||||
1. Add `github "ibireme/YYWebImage"` to your Cartfile.
|
||||
2. Run `carthage update --platform ios` and add the framework to your project.
|
||||
3. Import \<YYWebImage/YYWebImage.h\>.
|
||||
4. Notice: carthage framework doesn't include webp component, if you want to support WebP format, use CocoaPods or install manually. You may call `YYImageWebPAvailable()` to check whether the WebP library is installed correctly.
|
||||
|
||||
### Manually
|
||||
|
||||
1. Download all the files in the YYWebImage subdirectory.
|
||||
2. Add the source files to your Xcode project.
|
||||
3. Link with required frameworks:
|
||||
* UIKit
|
||||
* CoreFoundation
|
||||
* QuartzCore
|
||||
* AssetsLibrary
|
||||
* ImageIO
|
||||
* Accelerate
|
||||
* MobileCoreServices
|
||||
* sqlite3
|
||||
* libz
|
||||
4. Import `YYWebImage.h`.
|
||||
5. Notice: if you want to support WebP format, you may add `Vendor/WebP.framework`(static library) to your Xcode project.
|
||||
|
||||
|
||||
Documentation
|
||||
==============
|
||||
Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYWebImage/).<br/>
|
||||
You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc).
|
||||
|
||||
|
||||
Requirements
|
||||
==============
|
||||
This library requires `iOS 6.0+` and `Xcode 7.0+`.
|
||||
|
||||
|
||||
License
|
||||
==============
|
||||
YYWebImage is provided under the MIT license. See LICENSE file for details.
|
||||
|
||||
|
||||
<br/><br/>
|
||||
---
|
||||
中文介绍
|
||||
==============
|
||||

|
||||
|
||||
YYWebImage 是一个异步图片加载框架 ([YYKit](https://github.com/ibireme/YYKit) 组件之一).
|
||||
|
||||
其设计目的是试图替代 SDWebImage、PINRemoteImage、FLAnimatedImage 等开源框架,它支持这些开源框架的大部分功能,同时增加了大量新特性、并且有不小的性能提升。
|
||||
|
||||
它底层用 [YYCache](https://github.com/ibireme/YYCache) 实现了内存和磁盘缓存, 用 [YYImage](https://github.com/ibireme/YYImage) 实现了 WebP/APNG/GIF 动图的解码和播放。<br/>
|
||||
你可以查看这些项目以获得更多信息。
|
||||
|
||||
|
||||
特性
|
||||
==============
|
||||
- 异步的图片加载,支持 HTTP 和本地文件。
|
||||
- 支持 GIF、APNG、WebP 动画(动态缓存,低内存占用)。
|
||||
- 支持逐行扫描、隔行扫描、渐进式图像加载。
|
||||
- UIImageView、UIButton、MKAnnotationView、CALayer 的 Category 方法支持。
|
||||
- 常见图片处理:模糊、圆角、大小调整、裁切、旋转、色调等。
|
||||
- 高性能的内存和磁盘缓存。
|
||||
- 高性能的图片设置方式,以避免主线程阻塞。
|
||||
- 每个类和方法都有完善的文档注释。
|
||||
|
||||
用法
|
||||
==============
|
||||
|
||||
###从 URL 加载图片
|
||||
|
||||
// 加载网络图片
|
||||
imageView.yy_imageURL = [NSURL URLWithString:@"http://github.com/logo.png"];
|
||||
|
||||
// 加载本地图片
|
||||
imageView.yy_imageURL = [NSURL fileURLWithPath:@"/tmp/logo.png"];
|
||||
|
||||
|
||||
###加载动图
|
||||
|
||||
// 只需要把 `UIImageView` 替换为 `YYAnimatedImageView` 即可。
|
||||
UIImageView *imageView = [YYAnimatedImageView new];
|
||||
imageView.yy_imageURL = [NSURL URLWithString:@"http://github.com/ani.webp"];
|
||||
|
||||
|
||||
###渐进式图片加载
|
||||
|
||||
// 渐进式:边下载边显示
|
||||
[imageView yy_setImageWithURL:url options:YYWebImageOptionProgressive];
|
||||
|
||||
// 渐进式加载,增加模糊效果和渐变动画 (见本页最上方的GIF演示)
|
||||
[imageView yy_setImageWithURL:url options:YYWebImageOptionProgressiveBlur | YYWebImageOptionSetImageWithFadeAnimation];
|
||||
|
||||
|
||||
###加载、处理图片
|
||||
|
||||
// 1. 下载图片
|
||||
// 2. 获得图片下载进度
|
||||
// 3. 调整图片大小、加圆角
|
||||
// 4. 显示图片时增加一个淡入动画,以获得更好的用户体验
|
||||
|
||||
[imageView yy_setImageWithURL:url
|
||||
placeholder:nil
|
||||
options:YYWebImageOptionSetImageWithFadeAnimation
|
||||
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
progress = (float)receivedSize / expectedSize;
|
||||
}
|
||||
transform:^UIImage *(UIImage *image, NSURL *url) {
|
||||
image = [image yy_imageByResizeToSize:CGSizeMake(100, 100) contentMode:UIViewContentModeCenter];
|
||||
return [image yy_imageByRoundCornerRadius:10];
|
||||
}
|
||||
completion:^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
if (from == YYWebImageFromDiskCache) {
|
||||
NSLog(@"load from disk cache");
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
###图片缓存
|
||||
YYImageCache *cache = [YYWebImageManager sharedManager].cache;
|
||||
|
||||
// 获取缓存大小
|
||||
cache.memoryCache.totalCost;
|
||||
cache.memoryCache.totalCount;
|
||||
cache.diskCache.totalCost;
|
||||
cache.diskCache.totalCount;
|
||||
|
||||
// 清空缓存
|
||||
[cache.memoryCache removeAllObjects];
|
||||
[cache.diskCache removeAllObjects];
|
||||
|
||||
// 清空磁盘缓存,带进度回调
|
||||
[cache.diskCache removeAllObjectsWithProgressBlock:^(int removedCount, int totalCount) {
|
||||
// progress
|
||||
} endBlock:^(BOOL error) {
|
||||
// end
|
||||
}];
|
||||
|
||||
安装
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. 将 cocoapods 更新至最新版本.
|
||||
2. 在 Podfile 中添加 `pod 'YYWebImage'`。
|
||||
3. 执行 `pod install` 或 `pod update`。
|
||||
4. 导入 \<YYWebImage/YYWebImage.h\>。
|
||||
5. 注意:pod 配置并没有包含 WebP 组件, 如果你需要支持 WebP,可以在 Podfile 中添加 `pod 'YYImage/WebP'`。你可以调用 `YYImageWebPAvailable()` 来检查一下 WebP 组件是否被正确安装。
|
||||
|
||||
### Carthage
|
||||
|
||||
1. 在 Cartfile 中添加 `github "ibireme/YYWebImage"`。
|
||||
2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。
|
||||
3. 导入 \<YYWebImage/YYWebImage.h\>。
|
||||
4. 注意: carthage framework 并没有包含 webp 组件。如果你需要支持 WebP,可以用 CocoaPods 安装,或者手动安装。
|
||||
|
||||
### 手动安装
|
||||
|
||||
1. 下载 YYWebImage 文件夹内的所有内容。
|
||||
2. 将 YYWebImage 内的源文件添加(拖放)到你的工程。
|
||||
3. 链接以下 frameworks:
|
||||
* UIKit
|
||||
* CoreFoundation
|
||||
* QuartzCore
|
||||
* AssetsLibrary
|
||||
* ImageIO
|
||||
* Accelerate
|
||||
* MobileCoreServices
|
||||
* sqlite3
|
||||
* libz
|
||||
4. 导入 `YYWebImage.h`。
|
||||
5. 注意:如果你需要支持 WebP,可以将 `Vendor/WebP.framework`(静态库) 加入你的工程。你可以调用 `YYImageWebPAvailable()` 来检查一下 WebP 组件是否被正确安装。
|
||||
|
||||
|
||||
文档
|
||||
==============
|
||||
你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYWebImage/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。
|
||||
|
||||
|
||||
系统要求
|
||||
==============
|
||||
该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。
|
||||
|
||||
|
||||
许可证
|
||||
==============
|
||||
YYWebImage 使用 MIT 许可证,详情见 LICENSE 文件。
|
||||
|
||||
相关链接
|
||||
==============
|
||||
[移动端图片格式调研](http://blog.ibireme.com/2015/11/02/mobile_image_benchmark/)<br/>
|
||||
|
||||
[iOS 处理图片的一些小 Tip](http://blog.ibireme.com/2015/11/02/ios_image_tips/)
|
||||
|
112
Example/Pods/YYWebImage/YYWebImage/Categories/CALayer+YYWebImage.h
generated
Normal file
112
Example/Pods/YYWebImage/YYWebImage/Categories/CALayer+YYWebImage.h
generated
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// CALayer+YYWebImage.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#else
|
||||
#import "YYWebImageManager.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Web image methods for CALayer.
|
||||
It will set image to layer.contents.
|
||||
*/
|
||||
@interface CALayer (YYWebImage)
|
||||
|
||||
#pragma mark - image
|
||||
|
||||
/**
|
||||
Current image URL.
|
||||
|
||||
@discussion Set a new value to this property will cancel the previous request
|
||||
operation and create a new request operation to fetch image. Set nil to clear
|
||||
the image and image URL.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSURL *yy_imageURL;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param options The options to use when request the image.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL options:(YYWebImageOptions)options;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder he image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param manager The manager to create image request operation.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(nullable YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Cancel the current image request.
|
||||
*/
|
||||
- (void)yy_cancelCurrentImageRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
189
Example/Pods/YYWebImage/YYWebImage/Categories/CALayer+YYWebImage.m
generated
Normal file
189
Example/Pods/YYWebImage/YYWebImage/Categories/CALayer+YYWebImage.m
generated
Normal file
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// CALayer+YYWebImage.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "CALayer+YYWebImage.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "_YYWebImageSetter.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Dummy class for category
|
||||
@interface CALayer_YYWebImage : NSObject @end
|
||||
@implementation CALayer_YYWebImage @end
|
||||
|
||||
|
||||
static int _YYWebImageSetterKey;
|
||||
|
||||
@implementation CALayer (YYWebImage)
|
||||
|
||||
- (NSURL *)yy_imageURL {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
return setter.imageURL;
|
||||
}
|
||||
|
||||
- (void)setYy_imageURL:(NSURL *)imageURL {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL placeholder:(UIImage *)placeholder {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL options:(YYWebImageOptions)options {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
|
||||
manager = manager ? manager : [YYWebImageManager sharedManager];
|
||||
|
||||
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (!setter) {
|
||||
setter = [_YYWebImageSetter new];
|
||||
objc_setAssociatedObject(self, &_YYWebImageSetterKey, setter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
int32_t sentinel = [setter cancelWithNewURL:imageURL];
|
||||
|
||||
_yy_dispatch_sync_on_main_queue(^{
|
||||
if ((options & YYWebImageOptionSetImageWithFadeAnimation) &&
|
||||
!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
[self removeAnimationForKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
|
||||
if (!imageURL) {
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.contents = (id)placeholder.CGImage;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the image from memory as quickly as possible
|
||||
UIImage *imageFromMemory = nil;
|
||||
if (manager.cache &&
|
||||
!(options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(options & YYWebImageOptionRefreshImageCache)) {
|
||||
imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
|
||||
}
|
||||
if (imageFromMemory) {
|
||||
if (!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
self.contents = (id)imageFromMemory.CGImage;
|
||||
}
|
||||
if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.contents = (id)placeholder.CGImage;
|
||||
}
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([_YYWebImageSetter setterQueue], ^{
|
||||
YYWebImageProgressBlock _progress = nil;
|
||||
if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progress(receivedSize, expectedSize);
|
||||
});
|
||||
};
|
||||
|
||||
__block int32_t newSentinel = 0;
|
||||
__block __weak typeof(setter) weakSetter = nil;
|
||||
YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
|
||||
BOOL showFade = (options & YYWebImageOptionSetImageWithFadeAnimation);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;
|
||||
if (setImage && self && !sentinelChanged) {
|
||||
if (showFade) {
|
||||
CATransition *transition = [CATransition animation];
|
||||
transition.duration = stage == YYWebImageStageFinished ? _YYWebImageFadeTime : _YYWebImageProgressiveFadeTime;
|
||||
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
transition.type = kCATransitionFade;
|
||||
[self addAnimation:transition forKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
self.contents = (id)image.CGImage;
|
||||
}
|
||||
if (completion) {
|
||||
if (sentinelChanged) {
|
||||
completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
} else {
|
||||
completion(image, url, from, stage, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
|
||||
weakSetter = setter;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
- (void)yy_cancelCurrentImageRequest {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (setter) [setter cancel];
|
||||
}
|
||||
|
||||
@end
|
109
Example/Pods/YYWebImage/YYWebImage/Categories/MKAnnotationView+YYWebImage.h
generated
Normal file
109
Example/Pods/YYWebImage/YYWebImage/Categories/MKAnnotationView+YYWebImage.h
generated
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// MKAnnotationView+YYWebImage.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#else
|
||||
#import "YYWebImageManager.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Web image methods for MKAnnotationView.
|
||||
*/
|
||||
@interface MKAnnotationView (YYWebImage)
|
||||
|
||||
/**
|
||||
Current image URL.
|
||||
|
||||
@discussion Set a new value to this property will cancel the previous request
|
||||
operation and create a new request operation to fetch image. Set nil to clear
|
||||
the image and image URL.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSURL *yy_imageURL;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param options The options to use when request the image.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL options:(YYWebImageOptions)options;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder he image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param manager The manager to create image request operation.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(nullable YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Cancel the current image request.
|
||||
*/
|
||||
- (void)yy_cancelCurrentImageRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
187
Example/Pods/YYWebImage/YYWebImage/Categories/MKAnnotationView+YYWebImage.m
generated
Normal file
187
Example/Pods/YYWebImage/YYWebImage/Categories/MKAnnotationView+YYWebImage.m
generated
Normal file
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// MKAnnotationView+YYWebImage.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "MKAnnotationView+YYWebImage.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "_YYWebImageSetter.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Dummy class for category
|
||||
@interface MKAnnotationView_YYWebImage : NSObject @end
|
||||
@implementation MKAnnotationView_YYWebImage @end
|
||||
|
||||
|
||||
static int _YYWebImageSetterKey;
|
||||
|
||||
@implementation MKAnnotationView (YYWebImage)
|
||||
|
||||
- (NSURL *)yy_imageURL {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
return setter.imageURL;
|
||||
}
|
||||
|
||||
- (void)setYy_imageURL:(NSURL *)imageURL {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL placeholder:(UIImage *)placeholder {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL options:(YYWebImageOptions)options {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
|
||||
manager = manager ? manager : [YYWebImageManager sharedManager];
|
||||
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (!setter) {
|
||||
setter = [_YYWebImageSetter new];
|
||||
objc_setAssociatedObject(self, &_YYWebImageSetterKey, setter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
int32_t sentinel = [setter cancelWithNewURL:imageURL];
|
||||
|
||||
_yy_dispatch_sync_on_main_queue(^{
|
||||
if ((options & YYWebImageOptionSetImageWithFadeAnimation) &&
|
||||
!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
if (!self.highlighted) {
|
||||
[self.layer removeAnimationForKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
}
|
||||
if (!imageURL) {
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.image = placeholder;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the image from memory as quickly as possible
|
||||
UIImage *imageFromMemory = nil;
|
||||
if (manager.cache &&
|
||||
!(options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(options & YYWebImageOptionRefreshImageCache)) {
|
||||
imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
|
||||
}
|
||||
if (imageFromMemory) {
|
||||
if (!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
self.image = imageFromMemory;
|
||||
}
|
||||
if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.image = placeholder;
|
||||
}
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([_YYWebImageSetter setterQueue], ^{
|
||||
YYWebImageProgressBlock _progress = nil;
|
||||
if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progress(receivedSize, expectedSize);
|
||||
});
|
||||
};
|
||||
|
||||
__block int32_t newSentinel = 0;
|
||||
__block __weak typeof(setter) weakSetter = nil;
|
||||
YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
|
||||
BOOL showFade = ((options & YYWebImageOptionSetImageWithFadeAnimation) && !self.highlighted);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;
|
||||
if (setImage && self && !sentinelChanged) {
|
||||
if (showFade) {
|
||||
CATransition *transition = [CATransition animation];
|
||||
transition.duration = stage == YYWebImageStageFinished ? _YYWebImageFadeTime : _YYWebImageProgressiveFadeTime;
|
||||
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
transition.type = kCATransitionFade;
|
||||
[self.layer addAnimation:transition forKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
self.image = image;
|
||||
}
|
||||
if (completion) {
|
||||
if (sentinelChanged) {
|
||||
completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
} else {
|
||||
completion(image, url, from, stage, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
|
||||
weakSetter = setter;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)yy_cancelCurrentImageRequest {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (setter) [setter cancel];
|
||||
}
|
||||
|
||||
@end
|
213
Example/Pods/YYWebImage/YYWebImage/Categories/UIButton+YYWebImage.h
generated
Normal file
213
Example/Pods/YYWebImage/YYWebImage/Categories/UIButton+YYWebImage.h
generated
Normal file
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// UIButton+YYWebImage.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#else
|
||||
#import "YYWebImageManager.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Web image methods for UIButton.
|
||||
*/
|
||||
@interface UIButton (YYWebImage)
|
||||
|
||||
#pragma mark - image
|
||||
|
||||
/**
|
||||
Current image URL for the specified state.
|
||||
@return The image URL, or nil.
|
||||
*/
|
||||
- (nullable NSURL *)yy_imageURLForState:(UIControlState)state;
|
||||
|
||||
/**
|
||||
Set the button's image with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder;
|
||||
|
||||
/**
|
||||
Set the button's image with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param options The options to use when request the image.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
options:(YYWebImageOptions)options;
|
||||
|
||||
/**
|
||||
Set the button's image with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the button's image with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the button's image with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param manager The manager to create image request operation.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(nullable YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Cancel the current image request for a specified state.
|
||||
@param state The state that uses the specified image.
|
||||
*/
|
||||
- (void)yy_cancelImageRequestForState:(UIControlState)state;
|
||||
|
||||
|
||||
|
||||
#pragma mark - background image
|
||||
|
||||
/**
|
||||
Current backgroundImage URL for the specified state.
|
||||
@return The image URL, or nil.
|
||||
*/
|
||||
- (nullable NSURL *)yy_backgroundImageURLForState:(UIControlState)state;
|
||||
|
||||
/**
|
||||
Set the button's backgroundImage with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)yy_setBackgroundImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder;
|
||||
|
||||
/**
|
||||
Set the button's backgroundImage with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param options The options to use when request the image.
|
||||
*/
|
||||
- (void)yy_setBackgroundImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
options:(YYWebImageOptions)options;
|
||||
|
||||
/**
|
||||
Set the button's backgroundImage with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setBackgroundImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the button's backgroundImage with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setBackgroundImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the button's backgroundImage with a specified URL for the specified state.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param state The state that uses the specified image.
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param manager The manager to create image request operation.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setBackgroundImageWithURL:(nullable NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(nullable YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Cancel the current backgroundImage request for a specified state.
|
||||
@param state The state that uses the specified image.
|
||||
*/
|
||||
- (void)yy_cancelBackgroundImageRequestForState:(UIControlState)state;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
439
Example/Pods/YYWebImage/YYWebImage/Categories/UIButton+YYWebImage.m
generated
Normal file
439
Example/Pods/YYWebImage/YYWebImage/Categories/UIButton+YYWebImage.m
generated
Normal file
@@ -0,0 +1,439 @@
|
||||
//
|
||||
// UIButton+YYWebImage.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "UIButton+YYWebImage.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "_YYWebImageSetter.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Dummy class for category
|
||||
@interface UIButton_YYWebImage : NSObject @end
|
||||
@implementation UIButton_YYWebImage @end
|
||||
|
||||
static inline NSNumber *UIControlStateSingle(UIControlState state) {
|
||||
if (state & UIControlStateHighlighted) return @(UIControlStateHighlighted);
|
||||
if (state & UIControlStateDisabled) return @(UIControlStateDisabled);
|
||||
if (state & UIControlStateSelected) return @(UIControlStateSelected);
|
||||
return @(UIControlStateNormal);
|
||||
}
|
||||
|
||||
static inline NSArray *UIControlStateMulti(UIControlState state) {
|
||||
NSMutableArray *array = [NSMutableArray new];
|
||||
if (state & UIControlStateHighlighted) [array addObject:@(UIControlStateHighlighted)];
|
||||
if (state & UIControlStateDisabled) [array addObject:@(UIControlStateDisabled)];
|
||||
if (state & UIControlStateSelected) [array addObject:@(UIControlStateSelected)];
|
||||
if ((state & 0xFF) == 0) [array addObject:@(UIControlStateNormal)];
|
||||
return array;
|
||||
}
|
||||
|
||||
static int _YYWebImageSetterKey;
|
||||
static int _YYWebImageBackgroundSetterKey;
|
||||
|
||||
|
||||
@interface _YYWebImageSetterDicForButton : NSObject
|
||||
- (_YYWebImageSetter *)setterForState:(NSNumber *)state;
|
||||
- (_YYWebImageSetter *)lazySetterForState:(NSNumber *)state;
|
||||
@end
|
||||
|
||||
@implementation _YYWebImageSetterDicForButton {
|
||||
NSMutableDictionary *_dic;
|
||||
dispatch_semaphore_t _lock;
|
||||
}
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
_dic = [NSMutableDictionary new];
|
||||
return self;
|
||||
}
|
||||
- (_YYWebImageSetter *)setterForState:(NSNumber *)state {
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
_YYWebImageSetter *setter = _dic[state];
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return setter;
|
||||
|
||||
}
|
||||
- (_YYWebImageSetter *)lazySetterForState:(NSNumber *)state {
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
_YYWebImageSetter *setter = _dic[state];
|
||||
if (!setter) {
|
||||
setter = [_YYWebImageSetter new];
|
||||
_dic[state] = setter;
|
||||
}
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return setter;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation UIButton (YYWebImage)
|
||||
|
||||
#pragma mark - image
|
||||
|
||||
- (void)_yy_setImageWithURL:(NSURL *)imageURL
|
||||
forSingleState:(NSNumber *)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
|
||||
manager = manager ? manager : [YYWebImageManager sharedManager];
|
||||
|
||||
_YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (!dic) {
|
||||
dic = [_YYWebImageSetterDicForButton new];
|
||||
objc_setAssociatedObject(self, &_YYWebImageSetterKey, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
_YYWebImageSetter *setter = [dic lazySetterForState:state];
|
||||
int32_t sentinel = [setter cancelWithNewURL:imageURL];
|
||||
|
||||
_yy_dispatch_sync_on_main_queue(^{
|
||||
if (!imageURL) {
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
[self setImage:placeholder forState:state.integerValue];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the image from memory as quickly as possible
|
||||
UIImage *imageFromMemory = nil;
|
||||
if (manager.cache &&
|
||||
!(options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(options & YYWebImageOptionRefreshImageCache)) {
|
||||
imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
|
||||
}
|
||||
if (imageFromMemory) {
|
||||
if (!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
[self setImage:imageFromMemory forState:state.integerValue];
|
||||
}
|
||||
if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
[self setImage:placeholder forState:state.integerValue];
|
||||
}
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([_YYWebImageSetter setterQueue], ^{
|
||||
YYWebImageProgressBlock _progress = nil;
|
||||
if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progress(receivedSize, expectedSize);
|
||||
});
|
||||
};
|
||||
|
||||
__block int32_t newSentinel = 0;
|
||||
__block __weak typeof(setter) weakSetter = nil;
|
||||
YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;
|
||||
if (setImage && self && !sentinelChanged) {
|
||||
[self setImage:image forState:state.integerValue];
|
||||
}
|
||||
if (completion) {
|
||||
if (sentinelChanged) {
|
||||
completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
} else {
|
||||
completion(image, url, from, stage, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
|
||||
weakSetter = setter;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_yy_cancelImageRequestForSingleState:(NSNumber *)state {
|
||||
_YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
_YYWebImageSetter *setter = [dic setterForState:state];
|
||||
if (setter) [setter cancel];
|
||||
}
|
||||
|
||||
- (NSURL *)yy_imageURLForState:(UIControlState)state {
|
||||
_YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
_YYWebImageSetter *setter = [dic setterForState:UIControlStateSingle(state)];
|
||||
return setter.imageURL;
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:placeholder
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
options:(YYWebImageOptions)options {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:nil
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
for (NSNumber *num in UIControlStateMulti(state)) {
|
||||
[self _yy_setImageWithURL:imageURL
|
||||
forSingleState:num
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:manager
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)yy_cancelImageRequestForState:(UIControlState)state {
|
||||
for (NSNumber *num in UIControlStateMulti(state)) {
|
||||
[self _yy_cancelImageRequestForSingleState:num];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - background image
|
||||
|
||||
- (void)_yy_setBackgroundImageWithURL:(NSURL *)imageURL
|
||||
forSingleState:(NSNumber *)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
|
||||
manager = manager ? manager : [YYWebImageManager sharedManager];
|
||||
|
||||
_YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageBackgroundSetterKey);
|
||||
if (!dic) {
|
||||
dic = [_YYWebImageSetterDicForButton new];
|
||||
objc_setAssociatedObject(self, &_YYWebImageBackgroundSetterKey, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
_YYWebImageSetter *setter = [dic lazySetterForState:state];
|
||||
int32_t sentinel = [setter cancelWithNewURL:imageURL];
|
||||
|
||||
_yy_dispatch_sync_on_main_queue(^{
|
||||
if (!imageURL) {
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
[self setBackgroundImage:placeholder forState:state.integerValue];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the image from memory as quickly as possible
|
||||
UIImage *imageFromMemory = nil;
|
||||
if (manager.cache &&
|
||||
!(options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(options & YYWebImageOptionRefreshImageCache)) {
|
||||
imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
|
||||
}
|
||||
if (imageFromMemory) {
|
||||
if (!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
[self setBackgroundImage:imageFromMemory forState:state.integerValue];
|
||||
}
|
||||
if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
[self setBackgroundImage:placeholder forState:state.integerValue];
|
||||
}
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([_YYWebImageSetter setterQueue], ^{
|
||||
YYWebImageProgressBlock _progress = nil;
|
||||
if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progress(receivedSize, expectedSize);
|
||||
});
|
||||
};
|
||||
|
||||
__block int32_t newSentinel = 0;
|
||||
__block __weak typeof(setter) weakSetter = nil;
|
||||
YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;
|
||||
if (setImage && self && !sentinelChanged) {
|
||||
[self setBackgroundImage:image forState:state.integerValue];
|
||||
}
|
||||
if (completion) {
|
||||
if (sentinelChanged) {
|
||||
completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
} else {
|
||||
completion(image, url, from, stage, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
|
||||
weakSetter = setter;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_yy_cancelBackgroundImageRequestForSingleState:(NSNumber *)state {
|
||||
_YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageBackgroundSetterKey);
|
||||
_YYWebImageSetter *setter = [dic setterForState:state];
|
||||
if (setter) [setter cancel];
|
||||
}
|
||||
|
||||
- (NSURL *)yy_backgroundImageURLForState:(UIControlState)state {
|
||||
_YYWebImageSetterDicForButton *dic = objc_getAssociatedObject(self, &_YYWebImageBackgroundSetterKey);
|
||||
_YYWebImageSetter *setter = [dic setterForState:UIControlStateSingle(state)];
|
||||
return setter.imageURL;
|
||||
}
|
||||
|
||||
- (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder {
|
||||
[self yy_setBackgroundImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:placeholder
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
options:(YYWebImageOptions)options {
|
||||
[self yy_setBackgroundImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:nil
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setBackgroundImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setBackgroundImageWithURL:imageURL
|
||||
forState:state
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setBackgroundImageWithURL:(NSURL *)imageURL
|
||||
forState:(UIControlState)state
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
for (NSNumber *num in UIControlStateMulti(state)) {
|
||||
[self _yy_setBackgroundImageWithURL:imageURL
|
||||
forSingleState:num
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:manager
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)yy_cancelBackgroundImageRequestForState:(UIControlState)state {
|
||||
for (NSNumber *num in UIControlStateMulti(state)) {
|
||||
[self _yy_cancelBackgroundImageRequestForSingleState:num];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
316
Example/Pods/YYWebImage/YYWebImage/Categories/UIImage+YYWebImage.h
generated
Normal file
316
Example/Pods/YYWebImage/YYWebImage/Categories/UIImage+YYWebImage.h
generated
Normal file
@@ -0,0 +1,316 @@
|
||||
//
|
||||
// UIImage+YYWebImage.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 13/4/4.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Provide some commen method for `UIImage`.
|
||||
Image process is based on CoreGraphic and vImage.
|
||||
*/
|
||||
@interface UIImage (YYWebImage)
|
||||
|
||||
#pragma mark - Create image
|
||||
///=============================================================================
|
||||
/// @name Create image
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Create an animated image with GIF data. After created, you can access
|
||||
the images via property '.images'. If the data is not animated gif, this
|
||||
function is same as [UIImage imageWithData:data scale:scale];
|
||||
|
||||
@discussion It has a better display performance, but costs more memory
|
||||
(width * height * frames Bytes). It only suited to display small
|
||||
gif such as animated emoticon. If you want to display large gif,
|
||||
see `YYImage`.
|
||||
|
||||
@param data GIF data.
|
||||
|
||||
@param scale The scale factor
|
||||
|
||||
@return A new image created from GIF, or nil when an error occurs.
|
||||
*/
|
||||
+ (nullable UIImage *)yy_imageWithSmallGIFData:(NSData *)data scale:(CGFloat)scale;
|
||||
|
||||
/**
|
||||
Create and return a 1x1 point size image with the given color.
|
||||
|
||||
@param color The color.
|
||||
*/
|
||||
+ (nullable UIImage *)yy_imageWithColor:(UIColor *)color;
|
||||
|
||||
/**
|
||||
Create and return a pure color image with the given color and size.
|
||||
|
||||
@param color The color.
|
||||
@param size New image's type.
|
||||
*/
|
||||
+ (nullable UIImage *)yy_imageWithColor:(UIColor *)color size:(CGSize)size;
|
||||
|
||||
/**
|
||||
Create and return an image with custom draw code.
|
||||
|
||||
@param size The image size.
|
||||
@param drawBlock The draw block.
|
||||
|
||||
@return The new image.
|
||||
*/
|
||||
+ (nullable UIImage *)yy_imageWithSize:(CGSize)size drawBlock:(void (^)(CGContextRef context))drawBlock;
|
||||
|
||||
#pragma mark - Image Info
|
||||
///=============================================================================
|
||||
/// @name Image Info
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Whether this image has alpha channel.
|
||||
*/
|
||||
- (BOOL)yy_hasAlphaChannel;
|
||||
|
||||
|
||||
#pragma mark - Modify Image
|
||||
///=============================================================================
|
||||
/// @name Modify Image
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Draws the entire image in the specified rectangle, content changed with
|
||||
the contentMode.
|
||||
|
||||
@discussion This method draws the entire image in the current graphics context,
|
||||
respecting the image's orientation setting. In the default coordinate system,
|
||||
images are situated down and to the right of the origin of the specified
|
||||
rectangle. This method respects any transforms applied to the current graphics
|
||||
context, however.
|
||||
|
||||
@param rect The rectangle in which to draw the image.
|
||||
|
||||
@param contentMode Draw content mode
|
||||
|
||||
@param clips A Boolean value that determines whether content are confined to the rect.
|
||||
*/
|
||||
- (void)yy_drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode clipsToBounds:(BOOL)clips;
|
||||
|
||||
/**
|
||||
Returns a new image which is scaled from this image.
|
||||
The image will be stretched as needed.
|
||||
|
||||
@param size The new size to be scaled, values should be positive.
|
||||
|
||||
@return The new image with the given size.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByResizeToSize:(CGSize)size;
|
||||
|
||||
/**
|
||||
Returns a new image which is scaled from this image.
|
||||
The image content will be changed with thencontentMode.
|
||||
|
||||
@param size The new size to be scaled, values should be positive.
|
||||
|
||||
@param contentMode The content mode for image content.
|
||||
|
||||
@return The new image with the given size.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByResizeToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode;
|
||||
|
||||
/**
|
||||
Returns a new image which is cropped from this image.
|
||||
|
||||
@param rect Image's inner rect.
|
||||
|
||||
@return The new image, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByCropToRect:(CGRect)rect;
|
||||
|
||||
/**
|
||||
Returns a new image which is edge inset from this image.
|
||||
|
||||
@param insets Inset (positive) for each of the edges, values can be negative to 'outset'.
|
||||
|
||||
@param color Extend edge's fill color, nil means clear color.
|
||||
|
||||
@return The new image, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByInsetEdge:(UIEdgeInsets)insets withColor:(nullable UIColor *)color;
|
||||
|
||||
/**
|
||||
Rounds a new image with a given corner size.
|
||||
|
||||
@param radius The radius of each corner oval. Values larger than half the
|
||||
rectangle's width or height are clamped appropriately to half
|
||||
the width or height.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius;
|
||||
|
||||
/**
|
||||
Rounds a new image with a given corner size.
|
||||
|
||||
@param radius The radius of each corner oval. Values larger than half the
|
||||
rectangle's width or height are clamped appropriately to
|
||||
half the width or height.
|
||||
|
||||
@param borderWidth The inset border line width. Values larger than half the rectangle's
|
||||
width or height are clamped appropriately to half the width
|
||||
or height.
|
||||
|
||||
@param borderColor The border stroke color. nil means clear color.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius
|
||||
borderWidth:(CGFloat)borderWidth
|
||||
borderColor:(nullable UIColor *)borderColor;
|
||||
|
||||
/**
|
||||
Rounds a new image with a given corner size.
|
||||
|
||||
@param radius The radius of each corner oval. Values larger than half the
|
||||
rectangle's width or height are clamped appropriately to
|
||||
half the width or height.
|
||||
|
||||
@param corners A bitmask value that identifies the corners that you want
|
||||
rounded. You can use this parameter to round only a subset
|
||||
of the corners of the rectangle.
|
||||
|
||||
@param borderWidth The inset border line width. Values larger than half the rectangle's
|
||||
width or height are clamped appropriately to half the width
|
||||
or height.
|
||||
|
||||
@param borderColor The border stroke color. nil means clear color.
|
||||
|
||||
@param borderLineJoin The border line join.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius
|
||||
corners:(UIRectCorner)corners
|
||||
borderWidth:(CGFloat)borderWidth
|
||||
borderColor:(nullable UIColor *)borderColor
|
||||
borderLineJoin:(CGLineJoin)borderLineJoin;
|
||||
|
||||
/**
|
||||
Returns a new rotated image (relative to the center).
|
||||
|
||||
@param radians Rotated radians in counterclockwise.⟲
|
||||
|
||||
@param fitSize YES: new image's size is extend to fit all content.
|
||||
NO: image's size will not change, content may be clipped.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRotate:(CGFloat)radians fitSize:(BOOL)fitSize;
|
||||
|
||||
/**
|
||||
Returns a new image rotated counterclockwise by a quarter‑turn (90°). ⤺
|
||||
The width and height will be exchanged.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRotateLeft90;
|
||||
|
||||
/**
|
||||
Returns a new image rotated clockwise by a quarter‑turn (90°). ⤼
|
||||
The width and height will be exchanged.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRotateRight90;
|
||||
|
||||
/**
|
||||
Returns a new image rotated 180° . ↻
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByRotate180;
|
||||
|
||||
/**
|
||||
Returns a vertically flipped image. ⥯
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByFlipVertical;
|
||||
|
||||
/**
|
||||
Returns a horizontally flipped image. ⇋
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByFlipHorizontal;
|
||||
|
||||
|
||||
#pragma mark - Image Effect
|
||||
///=============================================================================
|
||||
/// @name Image Effect
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Tint the image in alpha channel with the given color.
|
||||
|
||||
@param color The color.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByTintColor:(UIColor *)color;
|
||||
|
||||
/**
|
||||
Returns a grayscaled image.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByGrayscale;
|
||||
|
||||
/**
|
||||
Applies a blur effect to this image. Suitable for blur any content.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByBlurSoft;
|
||||
|
||||
/**
|
||||
Applies a blur effect to this image. Suitable for blur any content except pure white.
|
||||
(same as iOS Control Panel)
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByBlurLight;
|
||||
|
||||
/**
|
||||
Applies a blur effect to this image. Suitable for displaying black text.
|
||||
(same as iOS Navigation Bar White)
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByBlurExtraLight;
|
||||
|
||||
/**
|
||||
Applies a blur effect to this image. Suitable for displaying white text.
|
||||
(same as iOS Notification Center)
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByBlurDark;
|
||||
|
||||
/**
|
||||
Applies a blur and tint color to this image.
|
||||
|
||||
@param tintColor The tint color.
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByBlurWithTint:(UIColor *)tintColor;
|
||||
|
||||
/**
|
||||
Applies a blur, tint color, and saturation adjustment to this image,
|
||||
optionally within the area specified by @a maskImage.
|
||||
|
||||
@param blurRadius The radius of the blur in points, 0 means no blur effect.
|
||||
|
||||
@param tintColor An optional UIColor object that is uniformly blended with
|
||||
the result of the blur and saturation operations. The
|
||||
alpha channel of this color determines how strong the
|
||||
tint is. nil means no tint.
|
||||
|
||||
@param tintBlendMode The @a tintColor blend mode. Default is kCGBlendModeNormal (0).
|
||||
|
||||
@param saturation A value of 1.0 produces no change in the resulting image.
|
||||
Values less than 1.0 will desaturation the resulting image
|
||||
while values greater than 1.0 will have the opposite effect.
|
||||
0 means gray scale.
|
||||
|
||||
@param maskImage If specified, @a inputImage is only modified in the area(s)
|
||||
defined by this mask. This must be an image mask or it
|
||||
must meet the requirements of the mask parameter of
|
||||
CGContextClipToMask.
|
||||
|
||||
@return image with effect, or nil if an error occurs (e.g. no
|
||||
enough memory).
|
||||
*/
|
||||
- (nullable UIImage *)yy_imageByBlurRadius:(CGFloat)blurRadius
|
||||
tintColor:(nullable UIColor *)tintColor
|
||||
tintMode:(CGBlendMode)tintBlendMode
|
||||
saturation:(CGFloat)saturation
|
||||
maskImage:(nullable UIImage *)maskImage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
745
Example/Pods/YYWebImage/YYWebImage/Categories/UIImage+YYWebImage.m
generated
Normal file
745
Example/Pods/YYWebImage/YYWebImage/Categories/UIImage+YYWebImage.m
generated
Normal file
@@ -0,0 +1,745 @@
|
||||
//
|
||||
// UIImage+YYWebImage.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 13/4/4.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "UIImage+YYWebImage.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <Accelerate/Accelerate.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Dummy class for category
|
||||
@interface UIImage_YYWebImage : NSObject @end
|
||||
@implementation UIImage_YYWebImage @end
|
||||
|
||||
|
||||
|
||||
/// Convert degrees to radians.
|
||||
static inline CGFloat _DegreesToRadians(CGFloat degrees) {
|
||||
return degrees * M_PI / 180;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Resize rect to fit the size using a given contentMode.
|
||||
|
||||
@param rect The draw rect
|
||||
@param size The content size
|
||||
@param mode The content mode
|
||||
@return A resized rect for the given content mode.
|
||||
@discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill.
|
||||
*/
|
||||
static CGRect _YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) {
|
||||
rect = CGRectStandardize(rect);
|
||||
size.width = size.width < 0 ? -size.width : size.width;
|
||||
size.height = size.height < 0 ? -size.height : size.height;
|
||||
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
|
||||
switch (mode) {
|
||||
case UIViewContentModeScaleAspectFit:
|
||||
case UIViewContentModeScaleAspectFill: {
|
||||
if (rect.size.width < 0.01 || rect.size.height < 0.01 ||
|
||||
size.width < 0.01 || size.height < 0.01) {
|
||||
rect.origin = center;
|
||||
rect.size = CGSizeZero;
|
||||
} else {
|
||||
CGFloat scale;
|
||||
if (mode == UIViewContentModeScaleAspectFit) {
|
||||
if (size.width / size.height < rect.size.width / rect.size.height) {
|
||||
scale = rect.size.height / size.height;
|
||||
} else {
|
||||
scale = rect.size.width / size.width;
|
||||
}
|
||||
} else {
|
||||
if (size.width / size.height < rect.size.width / rect.size.height) {
|
||||
scale = rect.size.width / size.width;
|
||||
} else {
|
||||
scale = rect.size.height / size.height;
|
||||
}
|
||||
}
|
||||
size.width *= scale;
|
||||
size.height *= scale;
|
||||
rect.size = size;
|
||||
rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
|
||||
}
|
||||
} break;
|
||||
case UIViewContentModeCenter: {
|
||||
rect.size = size;
|
||||
rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
|
||||
} break;
|
||||
case UIViewContentModeTop: {
|
||||
rect.origin.x = center.x - size.width * 0.5;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeBottom: {
|
||||
rect.origin.x = center.x - size.width * 0.5;
|
||||
rect.origin.y += rect.size.height - size.height;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeLeft: {
|
||||
rect.origin.y = center.y - size.height * 0.5;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeRight: {
|
||||
rect.origin.y = center.y - size.height * 0.5;
|
||||
rect.origin.x += rect.size.width - size.width;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeTopLeft: {
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeTopRight: {
|
||||
rect.origin.x += rect.size.width - size.width;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeBottomLeft: {
|
||||
rect.origin.y += rect.size.height - size.height;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeBottomRight: {
|
||||
rect.origin.x += rect.size.width - size.width;
|
||||
rect.origin.y += rect.size.height - size.height;
|
||||
rect.size = size;
|
||||
} break;
|
||||
case UIViewContentModeScaleToFill:
|
||||
case UIViewContentModeRedraw:
|
||||
default: {
|
||||
rect = rect;
|
||||
}
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static NSTimeInterval _yy_CGImageSourceGetGIFFrameDelayAtIndex(CGImageSourceRef source, size_t index) {
|
||||
NSTimeInterval delay = 0;
|
||||
CFDictionaryRef dic = CGImageSourceCopyPropertiesAtIndex(source, index, NULL);
|
||||
if (dic) {
|
||||
CFDictionaryRef dicGIF = CFDictionaryGetValue(dic, kCGImagePropertyGIFDictionary);
|
||||
if (dicGIF) {
|
||||
NSNumber *num = CFDictionaryGetValue(dicGIF, kCGImagePropertyGIFUnclampedDelayTime);
|
||||
if (num.doubleValue <= __FLT_EPSILON__) {
|
||||
num = CFDictionaryGetValue(dicGIF, kCGImagePropertyGIFDelayTime);
|
||||
}
|
||||
delay = num.doubleValue;
|
||||
}
|
||||
CFRelease(dic);
|
||||
}
|
||||
|
||||
// http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser-compatibility
|
||||
if (delay < 0.02) delay = 0.1;
|
||||
return delay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@implementation UIImage (YYWebImage)
|
||||
|
||||
+ (UIImage *)yy_imageWithSmallGIFData:(NSData *)data scale:(CGFloat)scale {
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)(data), NULL);
|
||||
if (!source) return nil;
|
||||
|
||||
size_t count = CGImageSourceGetCount(source);
|
||||
if (count <= 1) {
|
||||
CFRelease(source);
|
||||
return [self.class imageWithData:data scale:scale];
|
||||
}
|
||||
|
||||
NSUInteger frames[count];
|
||||
double oneFrameTime = 1 / 50.0; // 50 fps
|
||||
NSTimeInterval totalTime = 0;
|
||||
NSUInteger totalFrame = 0;
|
||||
NSUInteger gcdFrame = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
NSTimeInterval delay = _yy_CGImageSourceGetGIFFrameDelayAtIndex(source, i);
|
||||
totalTime += delay;
|
||||
NSInteger frame = lrint(delay / oneFrameTime);
|
||||
if (frame < 1) frame = 1;
|
||||
frames[i] = frame;
|
||||
totalFrame += frames[i];
|
||||
if (i == 0) gcdFrame = frames[i];
|
||||
else {
|
||||
NSUInteger frame = frames[i], tmp;
|
||||
if (frame < gcdFrame) {
|
||||
tmp = frame; frame = gcdFrame; gcdFrame = tmp;
|
||||
}
|
||||
while (true) {
|
||||
tmp = frame % gcdFrame;
|
||||
if (tmp == 0) break;
|
||||
frame = gcdFrame;
|
||||
gcdFrame = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
NSMutableArray *array = [NSMutableArray new];
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL);
|
||||
if (!imageRef) {
|
||||
CFRelease(source);
|
||||
return nil;
|
||||
}
|
||||
size_t width = CGImageGetWidth(imageRef);
|
||||
size_t height = CGImageGetHeight(imageRef);
|
||||
if (width == 0 || height == 0) {
|
||||
CFRelease(source);
|
||||
CFRelease(imageRef);
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
|
||||
BOOL hasAlpha = NO;
|
||||
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
|
||||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
|
||||
alphaInfo == kCGImageAlphaLast ||
|
||||
alphaInfo == kCGImageAlphaFirst) {
|
||||
hasAlpha = YES;
|
||||
}
|
||||
// BGRA8888 (premultiplied) or BGRX8888
|
||||
// same as UIGraphicsBeginImageContext() and -[UIView drawRect:]
|
||||
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
|
||||
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
|
||||
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, bitmapInfo);
|
||||
CGColorSpaceRelease(space);
|
||||
if (!context) {
|
||||
CFRelease(source);
|
||||
CFRelease(imageRef);
|
||||
return nil;
|
||||
}
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode
|
||||
CGImageRef decoded = CGBitmapContextCreateImage(context);
|
||||
CFRelease(context);
|
||||
if (!decoded) {
|
||||
CFRelease(source);
|
||||
CFRelease(imageRef);
|
||||
return nil;
|
||||
}
|
||||
UIImage *image = [UIImage imageWithCGImage:decoded scale:scale orientation:UIImageOrientationUp];
|
||||
CGImageRelease(imageRef);
|
||||
CGImageRelease(decoded);
|
||||
if (!image) {
|
||||
CFRelease(source);
|
||||
return nil;
|
||||
}
|
||||
for (size_t j = 0, max = frames[i] / gcdFrame; j < max; j++) {
|
||||
[array addObject:image];
|
||||
}
|
||||
}
|
||||
CFRelease(source);
|
||||
UIImage *image = [self.class animatedImageWithImages:array duration:totalTime];
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (UIImage *)yy_imageWithColor:(UIColor *)color {
|
||||
return [self yy_imageWithColor:color size:CGSizeMake(1, 1)];
|
||||
}
|
||||
|
||||
+ (UIImage *)yy_imageWithColor:(UIColor *)color size:(CGSize)size {
|
||||
if (!color || size.width <= 0 || size.height <= 0) return nil;
|
||||
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, color.CGColor);
|
||||
CGContextFillRect(context, rect);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (UIImage *)yy_imageWithSize:(CGSize)size drawBlock:(void (^)(CGContextRef context))drawBlock {
|
||||
if (!drawBlock) return nil;
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (!context) return nil;
|
||||
drawBlock(context);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (BOOL)yy_hasAlphaChannel {
|
||||
if (self.CGImage == NULL) return NO;
|
||||
CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage) & kCGBitmapAlphaInfoMask;
|
||||
return (alpha == kCGImageAlphaFirst ||
|
||||
alpha == kCGImageAlphaLast ||
|
||||
alpha == kCGImageAlphaPremultipliedFirst ||
|
||||
alpha == kCGImageAlphaPremultipliedLast);
|
||||
}
|
||||
|
||||
- (void)yy_drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode clipsToBounds:(BOOL)clips{
|
||||
CGRect drawRect = _YYCGRectFitWithContentMode(rect, self.size, contentMode);
|
||||
if (drawRect.size.width == 0 || drawRect.size.height == 0) return;
|
||||
if (clips) {
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (context) {
|
||||
CGContextSaveGState(context);
|
||||
CGContextAddRect(context, rect);
|
||||
CGContextClip(context);
|
||||
[self drawInRect:drawRect];
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
} else {
|
||||
[self drawInRect:drawRect];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByResizeToSize:(CGSize)size {
|
||||
if (size.width <= 0 || size.height <= 0) return nil;
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
|
||||
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByResizeToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode {
|
||||
if (size.width <= 0 || size.height <= 0) return nil;
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
|
||||
[self yy_drawInRect:CGRectMake(0, 0, size.width, size.height) withContentMode:contentMode clipsToBounds:NO];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByCropToRect:(CGRect)rect {
|
||||
rect.origin.x *= self.scale;
|
||||
rect.origin.y *= self.scale;
|
||||
rect.size.width *= self.scale;
|
||||
rect.size.height *= self.scale;
|
||||
if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
|
||||
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
|
||||
UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
|
||||
CGImageRelease(imageRef);
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByInsetEdge:(UIEdgeInsets)insets withColor:(UIColor *)color {
|
||||
CGSize size = self.size;
|
||||
size.width -= insets.left + insets.right;
|
||||
size.height -= insets.top + insets.bottom;
|
||||
if (size.width <= 0 || size.height <= 0) return nil;
|
||||
CGRect rect = CGRectMake(-insets.left, -insets.top, self.size.width, self.size.height);
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (color) {
|
||||
CGContextSetFillColorWithColor(context, color.CGColor);
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
|
||||
CGPathAddRect(path, NULL, rect);
|
||||
CGContextAddPath(context, path);
|
||||
CGContextEOFillPath(context);
|
||||
CGPathRelease(path);
|
||||
}
|
||||
[self drawInRect:rect];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius {
|
||||
return [self yy_imageByRoundCornerRadius:radius borderWidth:0 borderColor:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius
|
||||
borderWidth:(CGFloat)borderWidth
|
||||
borderColor:(UIColor *)borderColor {
|
||||
return [self yy_imageByRoundCornerRadius:radius corners:UIRectCornerAllCorners borderWidth:borderWidth borderColor:borderColor borderLineJoin:kCGLineJoinMiter];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius
|
||||
corners:(UIRectCorner)corners
|
||||
borderWidth:(CGFloat)borderWidth
|
||||
borderColor:(UIColor *)borderColor
|
||||
borderLineJoin:(CGLineJoin)borderLineJoin {
|
||||
|
||||
if (corners != UIRectCornerAllCorners) {
|
||||
UIRectCorner tmp = 0;
|
||||
if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;
|
||||
if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight;
|
||||
if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;
|
||||
if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight;
|
||||
corners = tmp;
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
CGContextTranslateCTM(context, 0, -rect.size.height);
|
||||
|
||||
CGFloat minSize = MIN(self.size.width, self.size.height);
|
||||
if (borderWidth < minSize / 2) {
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(radius, borderWidth)];
|
||||
[path closePath];
|
||||
|
||||
CGContextSaveGState(context);
|
||||
[path addClip];
|
||||
CGContextDrawImage(context, rect, self.CGImage);
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {
|
||||
CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;
|
||||
CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);
|
||||
CGFloat strokeRadius = radius > self.scale / 2 ? radius - self.scale / 2 : 0;
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, borderWidth)];
|
||||
[path closePath];
|
||||
|
||||
path.lineWidth = borderWidth;
|
||||
path.lineJoinStyle = borderLineJoin;
|
||||
[borderColor setStroke];
|
||||
[path stroke];
|
||||
}
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRotate:(CGFloat)radians fitSize:(BOOL)fitSize {
|
||||
size_t width = (size_t)CGImageGetWidth(self.CGImage);
|
||||
size_t height = (size_t)CGImageGetHeight(self.CGImage);
|
||||
CGRect newRect = CGRectApplyAffineTransform(CGRectMake(0., 0., width, height),
|
||||
fitSize ? CGAffineTransformMakeRotation(radians) : CGAffineTransformIdentity);
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(NULL,
|
||||
(size_t)newRect.size.width,
|
||||
(size_t)newRect.size.height,
|
||||
8,
|
||||
(size_t)newRect.size.width * 4,
|
||||
colorSpace,
|
||||
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
if (!context) return nil;
|
||||
|
||||
CGContextSetShouldAntialias(context, true);
|
||||
CGContextSetAllowsAntialiasing(context, true);
|
||||
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
|
||||
|
||||
CGContextTranslateCTM(context, +(newRect.size.width * 0.5), +(newRect.size.height * 0.5));
|
||||
CGContextRotateCTM(context, radians);
|
||||
|
||||
CGContextDrawImage(context, CGRectMake(-(width * 0.5), -(height * 0.5), width, height), self.CGImage);
|
||||
CGImageRef imgRef = CGBitmapContextCreateImage(context);
|
||||
UIImage *img = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
|
||||
CGImageRelease(imgRef);
|
||||
CGContextRelease(context);
|
||||
return img;
|
||||
}
|
||||
|
||||
- (UIImage *)_yy_flipHorizontal:(BOOL)horizontal vertical:(BOOL)vertical {
|
||||
if (!self.CGImage) return nil;
|
||||
size_t width = (size_t)CGImageGetWidth(self.CGImage);
|
||||
size_t height = (size_t)CGImageGetHeight(self.CGImage);
|
||||
size_t bytesPerRow = width * 4;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
if (!context) return nil;
|
||||
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
|
||||
UInt8 *data = (UInt8 *)CGBitmapContextGetData(context);
|
||||
if (!data) {
|
||||
CGContextRelease(context);
|
||||
return nil;
|
||||
}
|
||||
vImage_Buffer src = { data, height, width, bytesPerRow };
|
||||
vImage_Buffer dest = { data, height, width, bytesPerRow };
|
||||
if (vertical) {
|
||||
vImageVerticalReflect_ARGB8888(&src, &dest, kvImageBackgroundColorFill);
|
||||
}
|
||||
if (horizontal) {
|
||||
vImageHorizontalReflect_ARGB8888(&src, &dest, kvImageBackgroundColorFill);
|
||||
}
|
||||
CGImageRef imgRef = CGBitmapContextCreateImage(context);
|
||||
CGContextRelease(context);
|
||||
UIImage *img = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
|
||||
CGImageRelease(imgRef);
|
||||
return img;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRotateLeft90 {
|
||||
return [self yy_imageByRotate:_DegreesToRadians(90) fitSize:YES];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRotateRight90 {
|
||||
return [self yy_imageByRotate:_DegreesToRadians(-90) fitSize:YES];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByRotate180 {
|
||||
return [self _yy_flipHorizontal:YES vertical:YES];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByFlipVertical {
|
||||
return [self _yy_flipHorizontal:NO vertical:YES];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByFlipHorizontal {
|
||||
return [self _yy_flipHorizontal:YES vertical:NO];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByTintColor:(UIColor *)color {
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
|
||||
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
|
||||
[color set];
|
||||
UIRectFill(rect);
|
||||
[self drawAtPoint:CGPointMake(0, 0) blendMode:kCGBlendModeDestinationIn alpha:1];
|
||||
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return newImage;
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByGrayscale {
|
||||
return [self yy_imageByBlurRadius:0 tintColor:nil tintMode:0 saturation:0 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByBlurSoft {
|
||||
return [self yy_imageByBlurRadius:60 tintColor:[UIColor colorWithWhite:0.84 alpha:0.36] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByBlurLight {
|
||||
return [self yy_imageByBlurRadius:60 tintColor:[UIColor colorWithWhite:1.0 alpha:0.3] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByBlurExtraLight {
|
||||
return [self yy_imageByBlurRadius:40 tintColor:[UIColor colorWithWhite:0.97 alpha:0.82] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByBlurDark {
|
||||
return [self yy_imageByBlurRadius:40 tintColor:[UIColor colorWithWhite:0.11 alpha:0.73] tintMode:kCGBlendModeNormal saturation:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByBlurWithTint:(UIColor *)tintColor {
|
||||
const CGFloat EffectColorAlpha = 0.6;
|
||||
UIColor *effectColor = tintColor;
|
||||
size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
|
||||
if (componentCount == 2) {
|
||||
CGFloat b;
|
||||
if ([tintColor getWhite:&b alpha:NULL]) {
|
||||
effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
|
||||
}
|
||||
} else {
|
||||
CGFloat r, g, b;
|
||||
if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
|
||||
effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
|
||||
}
|
||||
}
|
||||
return [self yy_imageByBlurRadius:20 tintColor:effectColor tintMode:kCGBlendModeNormal saturation:-1.0 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)yy_imageByBlurRadius:(CGFloat)blurRadius
|
||||
tintColor:(UIColor *)tintColor
|
||||
tintMode:(CGBlendMode)tintBlendMode
|
||||
saturation:(CGFloat)saturation
|
||||
maskImage:(UIImage *)maskImage {
|
||||
if (self.size.width < 1 || self.size.height < 1) {
|
||||
NSLog(@"UIImage+YYAdd error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
|
||||
return nil;
|
||||
}
|
||||
if (!self.CGImage) {
|
||||
NSLog(@"UIImage+YYAdd error: inputImage must be backed by a CGImage: %@", self);
|
||||
return nil;
|
||||
}
|
||||
if (maskImage && !maskImage.CGImage) {
|
||||
NSLog(@"UIImage+YYAdd error: effectMaskImage must be backed by a CGImage: %@", maskImage);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// iOS7 and above can use new func.
|
||||
BOOL hasNewFunc = (long)vImageBuffer_InitWithCGImage != 0 && (long)vImageCreateCGImageFromBuffer != 0;
|
||||
BOOL hasBlur = blurRadius > __FLT_EPSILON__;
|
||||
BOOL hasSaturation = fabs(saturation - 1.0) > __FLT_EPSILON__;
|
||||
|
||||
CGSize size = self.size;
|
||||
CGRect rect = { CGPointZero, size };
|
||||
CGFloat scale = self.scale;
|
||||
CGImageRef imageRef = self.CGImage;
|
||||
BOOL opaque = NO;
|
||||
|
||||
if (!hasBlur && !hasSaturation) {
|
||||
return [self _yy_mergeImageRef:imageRef tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
|
||||
}
|
||||
|
||||
vImage_Buffer effect = { 0 }, scratch = { 0 };
|
||||
vImage_Buffer *input = NULL, *output = NULL;
|
||||
|
||||
vImage_CGImageFormat format = {
|
||||
.bitsPerComponent = 8,
|
||||
.bitsPerPixel = 32,
|
||||
.colorSpace = NULL,
|
||||
.bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, //requests a BGRA buffer.
|
||||
.version = 0,
|
||||
.decode = NULL,
|
||||
.renderingIntent = kCGRenderingIntentDefault
|
||||
};
|
||||
|
||||
if (hasNewFunc) {
|
||||
vImage_Error err;
|
||||
err = vImageBuffer_InitWithCGImage(&effect, &format, NULL, imageRef, kvImagePrintDiagnosticsToConsole);
|
||||
if (err != kvImageNoError) {
|
||||
NSLog(@"UIImage+YYAdd error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", err, self);
|
||||
return nil;
|
||||
}
|
||||
err = vImageBuffer_Init(&scratch, effect.height, effect.width, format.bitsPerPixel, kvImageNoFlags);
|
||||
if (err != kvImageNoError) {
|
||||
NSLog(@"UIImage+YYAdd error: vImageBuffer_Init returned error code %zi for inputImage: %@", err, self);
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
|
||||
CGContextRef effectCtx = UIGraphicsGetCurrentContext();
|
||||
CGContextScaleCTM(effectCtx, 1.0, -1.0);
|
||||
CGContextTranslateCTM(effectCtx, 0, -size.height);
|
||||
CGContextDrawImage(effectCtx, rect, imageRef);
|
||||
effect.data = CGBitmapContextGetData(effectCtx);
|
||||
effect.width = CGBitmapContextGetWidth(effectCtx);
|
||||
effect.height = CGBitmapContextGetHeight(effectCtx);
|
||||
effect.rowBytes = CGBitmapContextGetBytesPerRow(effectCtx);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
|
||||
CGContextRef scratchCtx = UIGraphicsGetCurrentContext();
|
||||
scratch.data = CGBitmapContextGetData(scratchCtx);
|
||||
scratch.width = CGBitmapContextGetWidth(scratchCtx);
|
||||
scratch.height = CGBitmapContextGetHeight(scratchCtx);
|
||||
scratch.rowBytes = CGBitmapContextGetBytesPerRow(scratchCtx);
|
||||
}
|
||||
|
||||
input = &effect;
|
||||
output = &scratch;
|
||||
|
||||
if (hasBlur) {
|
||||
// A description of how to compute the box kernel width from the Gaussian
|
||||
// radius (aka standard deviation) appears in the SVG spec:
|
||||
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
|
||||
//
|
||||
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
|
||||
// successive box-blurs build a piece-wise quadratic convolution kernel, which
|
||||
// approximates the Gaussian kernel to within roughly 3%.
|
||||
//
|
||||
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
|
||||
//
|
||||
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
|
||||
//
|
||||
CGFloat inputRadius = blurRadius * scale;
|
||||
if (inputRadius - 2.0 < __FLT_EPSILON__) inputRadius = 2.0;
|
||||
uint32_t radius = floor((inputRadius * 3.0 * sqrt(2 * M_PI) / 4 + 0.5) / 2);
|
||||
radius |= 1; // force radius to be odd so that the three box-blur methodology works.
|
||||
int iterations;
|
||||
if (blurRadius * scale < 0.5) iterations = 1;
|
||||
else if (blurRadius * scale < 1.5) iterations = 2;
|
||||
else iterations = 3;
|
||||
NSInteger tempSize = vImageBoxConvolve_ARGB8888(input, output, NULL, 0, 0, radius, radius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
|
||||
void *temp = malloc(tempSize);
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
vImageBoxConvolve_ARGB8888(input, output, temp, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
|
||||
// swap
|
||||
vImage_Buffer *swap_tmp = input;
|
||||
input = output;
|
||||
output = swap_tmp;
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
|
||||
|
||||
if (hasSaturation) {
|
||||
// These values appear in the W3C Filter Effects spec:
|
||||
// https://dvcs.w3.org/hg/FXTF/raw-file/default/filters/Publish.html#grayscaleEquivalent
|
||||
CGFloat s = saturation;
|
||||
CGFloat matrixFloat[] = {
|
||||
0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
|
||||
0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
|
||||
0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
const int32_t divisor = 256;
|
||||
NSUInteger matrixSize = sizeof(matrixFloat) / sizeof(matrixFloat[0]);
|
||||
int16_t matrix[matrixSize];
|
||||
for (NSUInteger i = 0; i < matrixSize; ++i) {
|
||||
matrix[i] = (int16_t)roundf(matrixFloat[i] * divisor);
|
||||
}
|
||||
vImageMatrixMultiply_ARGB8888(input, output, matrix, divisor, NULL, NULL, kvImageNoFlags);
|
||||
// swap
|
||||
vImage_Buffer *swap_tmp = input;
|
||||
input = output;
|
||||
output = swap_tmp;
|
||||
}
|
||||
|
||||
UIImage *outputImage = nil;
|
||||
if (hasNewFunc) {
|
||||
CGImageRef effectCGImage = NULL;
|
||||
effectCGImage = vImageCreateCGImageFromBuffer(input, &format, &_yy_cleanupBuffer, NULL, kvImageNoAllocate, NULL);
|
||||
if (effectCGImage == NULL) {
|
||||
effectCGImage = vImageCreateCGImageFromBuffer(input, &format, NULL, NULL, kvImageNoFlags, NULL);
|
||||
free(input->data);
|
||||
}
|
||||
free(output->data);
|
||||
outputImage = [self _yy_mergeImageRef:effectCGImage tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
|
||||
CGImageRelease(effectCGImage);
|
||||
} else {
|
||||
CGImageRef effectCGImage;
|
||||
UIImage *effectImage;
|
||||
if (input != &effect) effectImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
if (input == &effect) effectImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
effectCGImage = effectImage.CGImage;
|
||||
outputImage = [self _yy_mergeImageRef:effectCGImage tintColor:tintColor tintBlendMode:tintBlendMode maskImage:maskImage opaque:opaque];
|
||||
}
|
||||
return outputImage;
|
||||
}
|
||||
|
||||
// Helper function to handle deferred cleanup of a buffer.
|
||||
static void _yy_cleanupBuffer(void *userData, void *buf_data) {
|
||||
free(buf_data);
|
||||
}
|
||||
|
||||
// Helper function to add tint and mask.
|
||||
- (UIImage *)_yy_mergeImageRef:(CGImageRef)effectCGImage
|
||||
tintColor:(UIColor *)tintColor
|
||||
tintBlendMode:(CGBlendMode)tintBlendMode
|
||||
maskImage:(UIImage *)maskImage
|
||||
opaque:(BOOL)opaque {
|
||||
BOOL hasTint = tintColor != nil && CGColorGetAlpha(tintColor.CGColor) > __FLT_EPSILON__;
|
||||
BOOL hasMask = maskImage != nil;
|
||||
CGSize size = self.size;
|
||||
CGRect rect = { CGPointZero, size };
|
||||
CGFloat scale = self.scale;
|
||||
|
||||
if (!hasTint && !hasMask) {
|
||||
return [UIImage imageWithCGImage:effectCGImage];
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextScaleCTM(context, 1.0, -1.0);
|
||||
CGContextTranslateCTM(context, 0, -size.height);
|
||||
if (hasMask) {
|
||||
CGContextDrawImage(context, rect, self.CGImage);
|
||||
CGContextSaveGState(context);
|
||||
CGContextClipToMask(context, rect, maskImage.CGImage);
|
||||
}
|
||||
CGContextDrawImage(context, rect, effectCGImage);
|
||||
if (hasTint) {
|
||||
CGContextSaveGState(context);
|
||||
CGContextSetBlendMode(context, tintBlendMode);
|
||||
CGContextSetFillColorWithColor(context, tintColor.CGColor);
|
||||
CGContextFillRect(context, rect);
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
if (hasMask) {
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return outputImage;
|
||||
}
|
||||
|
||||
@end
|
193
Example/Pods/YYWebImage/YYWebImage/Categories/UIImageView+YYWebImage.h
generated
Normal file
193
Example/Pods/YYWebImage/YYWebImage/Categories/UIImageView+YYWebImage.h
generated
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// UIImageView+YYWebImage.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#else
|
||||
#import "YYWebImageManager.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Web image methods for UIImageView.
|
||||
*/
|
||||
@interface UIImageView (YYWebImage)
|
||||
|
||||
#pragma mark - image
|
||||
|
||||
/**
|
||||
Current image URL.
|
||||
|
||||
@discussion Set a new value to this property will cancel the previous request
|
||||
operation and create a new request operation to fetch image. Set nil to clear
|
||||
the image and image URL.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSURL *yy_imageURL;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param options The options to use when request the image.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL options:(YYWebImageOptions)options;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `image` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder he image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param manager The manager to create image request operation.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(nullable YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Cancel the current image request.
|
||||
*/
|
||||
- (void)yy_cancelCurrentImageRequest;
|
||||
|
||||
|
||||
|
||||
#pragma mark - highlight image
|
||||
|
||||
/**
|
||||
Current highlighted image URL.
|
||||
|
||||
@discussion Set a new value to this property will cancel the previous request
|
||||
operation and create a new request operation to fetch image. Set nil to clear
|
||||
the highlighted image and image URL.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSURL *yy_highlightedImageURL;
|
||||
|
||||
/**
|
||||
Set the view's `highlightedImage` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)yy_setHighlightedImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder;
|
||||
|
||||
/**
|
||||
Set the view's `highlightedImage` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param options The options to use when request the image.
|
||||
*/
|
||||
- (void)yy_setHighlightedImageWithURL:(nullable NSURL *)imageURL options:(YYWebImageOptions)options;
|
||||
|
||||
/**
|
||||
Set the view's `highlightedImage` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setHighlightedImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `highlightedImage` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setHighlightedImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Set the view's `highlightedImage` with a specified URL.
|
||||
|
||||
@param imageURL The image url (remote or local file path).
|
||||
@param placeholder The image to be set initially, until the image request finishes.
|
||||
@param options The options to use when request the image.
|
||||
@param manager The manager to create image request operation.
|
||||
@param progress The block invoked (on main thread) during image request.
|
||||
@param transform The block invoked (on background thread) to do additional image process.
|
||||
@param completion The block invoked (on main thread) when image request completed.
|
||||
*/
|
||||
- (void)yy_setHighlightedImageWithURL:(nullable NSURL *)imageURL
|
||||
placeholder:(nullable UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(nullable YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Cancel the current highlighed image request.
|
||||
*/
|
||||
- (void)yy_cancelCurrentHighlightedImageRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
355
Example/Pods/YYWebImage/YYWebImage/Categories/UIImageView+YYWebImage.m
generated
Normal file
355
Example/Pods/YYWebImage/YYWebImage/Categories/UIImageView+YYWebImage.m
generated
Normal file
@@ -0,0 +1,355 @@
|
||||
//
|
||||
// UIImageView+YYWebImage.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "UIImageView+YYWebImage.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "_YYWebImageSetter.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Dummy class for category
|
||||
@interface UIImageView_YYWebImage : NSObject @end
|
||||
@implementation UIImageView_YYWebImage @end
|
||||
|
||||
static int _YYWebImageSetterKey;
|
||||
static int _YYWebImageHighlightedSetterKey;
|
||||
|
||||
|
||||
@implementation UIImageView (YYWebImage)
|
||||
|
||||
#pragma mark - image
|
||||
|
||||
- (NSURL *)yy_imageURL {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
return setter.imageURL;
|
||||
}
|
||||
|
||||
- (void)setYy_imageURL:(NSURL *)imageURL {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL placeholder:(UIImage *)placeholder {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL options:(YYWebImageOptions)options {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:progress
|
||||
transform:transform
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
|
||||
manager = manager ? manager : [YYWebImageManager sharedManager];
|
||||
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (!setter) {
|
||||
setter = [_YYWebImageSetter new];
|
||||
objc_setAssociatedObject(self, &_YYWebImageSetterKey, setter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
int32_t sentinel = [setter cancelWithNewURL:imageURL];
|
||||
|
||||
_yy_dispatch_sync_on_main_queue(^{
|
||||
if ((options & YYWebImageOptionSetImageWithFadeAnimation) &&
|
||||
!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
if (!self.highlighted) {
|
||||
[self.layer removeAnimationForKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
}
|
||||
|
||||
if (!imageURL) {
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.image = placeholder;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the image from memory as quickly as possible
|
||||
UIImage *imageFromMemory = nil;
|
||||
if (manager.cache &&
|
||||
!(options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(options & YYWebImageOptionRefreshImageCache)) {
|
||||
imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
|
||||
}
|
||||
if (imageFromMemory) {
|
||||
if (!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
self.image = imageFromMemory;
|
||||
}
|
||||
if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.image = placeholder;
|
||||
}
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([_YYWebImageSetter setterQueue], ^{
|
||||
YYWebImageProgressBlock _progress = nil;
|
||||
if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progress(receivedSize, expectedSize);
|
||||
});
|
||||
};
|
||||
|
||||
__block int32_t newSentinel = 0;
|
||||
__block __weak typeof(setter) weakSetter = nil;
|
||||
YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;
|
||||
if (setImage && self && !sentinelChanged) {
|
||||
BOOL showFade = ((options & YYWebImageOptionSetImageWithFadeAnimation) && !self.highlighted);
|
||||
if (showFade) {
|
||||
CATransition *transition = [CATransition animation];
|
||||
transition.duration = stage == YYWebImageStageFinished ? _YYWebImageFadeTime : _YYWebImageProgressiveFadeTime;
|
||||
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
transition.type = kCATransitionFade;
|
||||
[self.layer addAnimation:transition forKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
self.image = image;
|
||||
}
|
||||
if (completion) {
|
||||
if (sentinelChanged) {
|
||||
completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
} else {
|
||||
completion(image, url, from, stage, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
|
||||
weakSetter = setter;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)yy_cancelCurrentImageRequest {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageSetterKey);
|
||||
if (setter) [setter cancel];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - highlighted image
|
||||
|
||||
- (NSURL *)yy_highlightedImageURL {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageHighlightedSetterKey);
|
||||
return setter.imageURL;
|
||||
}
|
||||
|
||||
- (void)setYy_highlightedImageURL:(NSURL *)imageURL {
|
||||
[self yy_setHighlightedImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setHighlightedImageWithURL:(NSURL *)imageURL placeholder:(UIImage *)placeholder {
|
||||
[self yy_setHighlightedImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:kNilOptions
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setHighlightedImageWithURL:(NSURL *)imageURL options:(YYWebImageOptions)options {
|
||||
[self yy_setHighlightedImageWithURL:imageURL
|
||||
placeholder:nil
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)yy_setHighlightedImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setHighlightedImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:nil
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setHighlightedImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
[self yy_setHighlightedImageWithURL:imageURL
|
||||
placeholder:placeholder
|
||||
options:options
|
||||
manager:nil
|
||||
progress:progress
|
||||
transform:nil
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)yy_setHighlightedImageWithURL:(NSURL *)imageURL
|
||||
placeholder:(UIImage *)placeholder
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if ([imageURL isKindOfClass:[NSString class]]) imageURL = [NSURL URLWithString:(id)imageURL];
|
||||
manager = manager ? manager : [YYWebImageManager sharedManager];
|
||||
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageHighlightedSetterKey);
|
||||
if (!setter) {
|
||||
setter = [_YYWebImageSetter new];
|
||||
objc_setAssociatedObject(self, &_YYWebImageHighlightedSetterKey, setter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
int32_t sentinel = [setter cancelWithNewURL:imageURL];
|
||||
|
||||
_yy_dispatch_sync_on_main_queue(^{
|
||||
if ((options & YYWebImageOptionSetImageWithFadeAnimation) &&
|
||||
!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
if (self.highlighted) {
|
||||
[self.layer removeAnimationForKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
}
|
||||
if (!imageURL) {
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.highlightedImage = placeholder;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the image from memory as quickly as possible
|
||||
UIImage *imageFromMemory = nil;
|
||||
if (manager.cache &&
|
||||
!(options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(options & YYWebImageOptionRefreshImageCache)) {
|
||||
imageFromMemory = [manager.cache getImageForKey:[manager cacheKeyForURL:imageURL] withType:YYImageCacheTypeMemory];
|
||||
}
|
||||
if (imageFromMemory) {
|
||||
if (!(options & YYWebImageOptionAvoidSetImage)) {
|
||||
self.highlightedImage = imageFromMemory;
|
||||
}
|
||||
if(completion) completion(imageFromMemory, imageURL, YYWebImageFromMemoryCacheFast, YYWebImageStageFinished, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(options & YYWebImageOptionIgnorePlaceHolder)) {
|
||||
self.highlightedImage = placeholder;
|
||||
}
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([_YYWebImageSetter setterQueue], ^{
|
||||
YYWebImageProgressBlock _progress = nil;
|
||||
if (progress) _progress = ^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progress(receivedSize, expectedSize);
|
||||
});
|
||||
};
|
||||
|
||||
__block int32_t newSentinel = 0;
|
||||
__block __weak typeof(setter) weakSetter = nil;
|
||||
YYWebImageCompletionBlock _completion = ^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL setImage = (stage == YYWebImageStageFinished || stage == YYWebImageStageProgress) && image && !(options & YYWebImageOptionAvoidSetImage);
|
||||
BOOL showFade = ((options & YYWebImageOptionSetImageWithFadeAnimation) && self.highlighted);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL sentinelChanged = weakSetter && weakSetter.sentinel != newSentinel;
|
||||
if (setImage && self && !sentinelChanged) {
|
||||
if (showFade) {
|
||||
CATransition *transition = [CATransition animation];
|
||||
transition.duration = stage == YYWebImageStageFinished ? _YYWebImageFadeTime : _YYWebImageProgressiveFadeTime;
|
||||
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
transition.type = kCATransitionFade;
|
||||
[self.layer addAnimation:transition forKey:_YYWebImageFadeAnimationKey];
|
||||
}
|
||||
self.highlightedImage = image;
|
||||
}
|
||||
if (completion) {
|
||||
if (sentinelChanged) {
|
||||
completion(nil, url, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
} else {
|
||||
completion(image, url, from, stage, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
newSentinel = [setter setOperationWithSentinel:sentinel url:imageURL options:options manager:manager progress:_progress transform:transform completion:_completion];
|
||||
weakSetter = setter;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)yy_cancelCurrentHighlightedImageRequest {
|
||||
_YYWebImageSetter *setter = objc_getAssociatedObject(self, &_YYWebImageHighlightedSetterKey);
|
||||
if (setter) [setter cancel];
|
||||
}
|
||||
|
||||
@end
|
67
Example/Pods/YYWebImage/YYWebImage/Categories/_YYWebImageSetter.h
generated
Normal file
67
Example/Pods/YYWebImage/YYWebImage/Categories/_YYWebImageSetter.h
generated
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// _YYWebImageSetter.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/7/15.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <pthread.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#else
|
||||
#import "YYWebImageManager.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Submits a block for execution on a main queue and waits until the block completes.
|
||||
*/
|
||||
static inline void _yy_dispatch_sync_on_main_queue(void (^block)()) {
|
||||
if (pthread_main_np()) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), block);
|
||||
}
|
||||
}
|
||||
|
||||
extern NSString *const _YYWebImageFadeAnimationKey;
|
||||
extern const NSTimeInterval _YYWebImageFadeTime;
|
||||
extern const NSTimeInterval _YYWebImageProgressiveFadeTime;
|
||||
|
||||
/**
|
||||
Private class used by web image categories.
|
||||
Typically, you should not use this class directly.
|
||||
*/
|
||||
@interface _YYWebImageSetter : NSObject
|
||||
/// Current image url.
|
||||
@property (nullable, nonatomic, readonly) NSURL *imageURL;
|
||||
/// Current sentinel.
|
||||
@property (nonatomic, readonly) int32_t sentinel;
|
||||
|
||||
/// Create new operation for web image and return a sentinel value.
|
||||
- (int32_t)setOperationWithSentinel:(int32_t)sentinel
|
||||
url:(nullable NSURL *)imageURL
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/// Cancel and return a sentinel value. The imageURL will be set to nil.
|
||||
- (int32_t)cancel;
|
||||
|
||||
/// Cancel and return a sentinel value. The imageURL will be set to new value.
|
||||
- (int32_t)cancelWithNewURL:(nullable NSURL *)imageURL;
|
||||
|
||||
/// A queue to set operation.
|
||||
+ (dispatch_queue_t)setterQueue;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
103
Example/Pods/YYWebImage/YYWebImage/Categories/_YYWebImageSetter.m
generated
Normal file
103
Example/Pods/YYWebImage/YYWebImage/Categories/_YYWebImageSetter.m
generated
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// _YYWebImageSetter.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/7/15.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "_YYWebImageSetter.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
NSString *const _YYWebImageFadeAnimationKey = @"YYWebImageFade";
|
||||
const NSTimeInterval _YYWebImageFadeTime = 0.2;
|
||||
const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4;
|
||||
|
||||
|
||||
@implementation _YYWebImageSetter {
|
||||
dispatch_semaphore_t _lock;
|
||||
NSURL *_imageURL;
|
||||
NSOperation *_operation;
|
||||
int32_t _sentinel;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSURL *)imageURL {
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
NSURL *imageURL = _imageURL;
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return imageURL;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
OSAtomicIncrement32(&_sentinel);
|
||||
[_operation cancel];
|
||||
}
|
||||
|
||||
- (int32_t)setOperationWithSentinel:(int32_t)sentinel
|
||||
url:(NSURL *)imageURL
|
||||
options:(YYWebImageOptions)options
|
||||
manager:(YYWebImageManager *)manager
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
if (sentinel != _sentinel) {
|
||||
if (completion) completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
return _sentinel;
|
||||
}
|
||||
|
||||
NSOperation *operation = [manager requestImageWithURL:imageURL options:options progress:progress transform:transform completion:completion];
|
||||
if (!operation && completion) {
|
||||
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"YYWebImageOperation create failed." };
|
||||
completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageFinished, [NSError errorWithDomain:@"com.ibireme.webimage" code:-1 userInfo:userInfo]);
|
||||
}
|
||||
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
if (sentinel == _sentinel) {
|
||||
if (_operation) [_operation cancel];
|
||||
_operation = operation;
|
||||
sentinel = OSAtomicIncrement32(&_sentinel);
|
||||
} else {
|
||||
[operation cancel];
|
||||
}
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
- (int32_t)cancel {
|
||||
return [self cancelWithNewURL:nil];
|
||||
}
|
||||
|
||||
- (int32_t)cancelWithNewURL:(NSURL *)imageURL {
|
||||
int32_t sentinel;
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
if (_operation) {
|
||||
[_operation cancel];
|
||||
_operation = nil;
|
||||
}
|
||||
_imageURL = imageURL;
|
||||
sentinel = OSAtomicIncrement32(&_sentinel);
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
+ (dispatch_queue_t)setterQueue {
|
||||
static dispatch_queue_t queue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue = dispatch_queue_create("com.ibireme.webimage.setter", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
@end
|
228
Example/Pods/YYWebImage/YYWebImage/YYImageCache.h
generated
Normal file
228
Example/Pods/YYWebImage/YYWebImage/YYImageCache.h
generated
Normal file
@@ -0,0 +1,228 @@
|
||||
//
|
||||
// YYImageCache.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/15.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class YYMemoryCache, YYDiskCache;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Image cache type
|
||||
typedef NS_OPTIONS(NSUInteger, YYImageCacheType) {
|
||||
/// No value.
|
||||
YYImageCacheTypeNone = 0,
|
||||
|
||||
/// Get/store image with memory cache.
|
||||
YYImageCacheTypeMemory = 1 << 0,
|
||||
|
||||
/// Get/store image with disk cache.
|
||||
YYImageCacheTypeDisk = 1 << 1,
|
||||
|
||||
/// Get/store image with both memory cache and disk cache.
|
||||
YYImageCacheTypeAll = YYImageCacheTypeMemory | YYImageCacheTypeDisk,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
YYImageCache is a cache that stores UIImage and image data based on memory cache and disk cache.
|
||||
|
||||
@discussion The disk cache will try to protect the original image data:
|
||||
|
||||
* If the original image is still image, it will be saved as png/jpeg file based on alpha information.
|
||||
* If the original image is animated gif, apng or webp, it will be saved as original format.
|
||||
* If the original image's scale is not 1, the scale value will be saved as extended data.
|
||||
|
||||
Although UIImage can be serialized with NSCoding protocol, but it's not a good idea:
|
||||
Apple actually use UIImagePNGRepresentation() to encode all kind of image, it may
|
||||
lose the original multi-frame data. The result is packed to plist file and cannot
|
||||
view with photo viewer directly. If the image has no alpha channel, using JPEG
|
||||
instead of PNG can save more disk size and encoding/decoding time.
|
||||
*/
|
||||
@interface YYImageCache : NSObject
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
|
||||
/** The name of the cache. Default is nil. */
|
||||
@property (nullable, copy) NSString *name;
|
||||
|
||||
/** The underlying memory cache. see `YYMemoryCache` for more information.*/
|
||||
@property (strong, readonly) YYMemoryCache *memoryCache;
|
||||
|
||||
/** The underlying disk cache. see `YYDiskCache` for more information.*/
|
||||
@property (strong, readonly) YYDiskCache *diskCache;
|
||||
|
||||
/**
|
||||
Whether decode animated image when fetch image from disk cache. Default is YES.
|
||||
|
||||
@discussion When fetch image from disk cache, it will use 'YYImage' to decode
|
||||
animated image such as WebP/APNG/GIF. Set to 'NO' to ignore animated image.
|
||||
*/
|
||||
@property BOOL allowAnimatedImage;
|
||||
|
||||
/**
|
||||
Whether decode the image to memory bitmap. Default is YES.
|
||||
|
||||
@discussion If the value is YES, then the image will be decoded to memory bitmap
|
||||
for better display performance, but may cost more memory.
|
||||
*/
|
||||
@property BOOL decodeForDisplay;
|
||||
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
Returns global shared image cache instance.
|
||||
@return The singleton YYImageCache instance.
|
||||
*/
|
||||
+ (instancetype)sharedCache;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same path will make the
|
||||
cache unstable.
|
||||
|
||||
@param path Full path of a directory in which the cache will write data.
|
||||
Once initialized you should not read and write to this directory.
|
||||
@result A new cache object, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
||||
#pragma mark - Access Methods
|
||||
///=============================================================================
|
||||
/// @name Access Methods
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Sets the image with the specified key in the cache (both memory and disk).
|
||||
This method returns immediately and executes the store operation in background.
|
||||
|
||||
@param image The image to be stored in the cache. If nil, this method has no effect.
|
||||
@param key The key with which to associate the image. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)setImage:(UIImage *)image forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Sets the image with the specified key in the cache.
|
||||
This method returns immediately and executes the store operation in background.
|
||||
|
||||
@discussion If the `type` contain `YYImageCacheTypeMemory`, then the `image` will
|
||||
be stored in the memory cache; `imageData` will be used instead if `image` is nil.
|
||||
If the `type` contain `YYImageCacheTypeDisk`, then the `imageData` will
|
||||
be stored in the disk cache; `image` will be used instead if `imageData` is nil.
|
||||
|
||||
@param image The image to be stored in the cache.
|
||||
@param imageData The image data to be stored in the cache.
|
||||
@param key The key with which to associate the image. If nil, this method has no effect.
|
||||
@param type The cache type to store image.
|
||||
*/
|
||||
- (void)setImage:(nullable UIImage *)image
|
||||
imageData:(nullable NSData *)imageData
|
||||
forKey:(NSString *)key
|
||||
withType:(YYImageCacheType)type;
|
||||
|
||||
/**
|
||||
Removes the image of the specified key in the cache (both memory and disk).
|
||||
This method returns immediately and executes the remove operation in background.
|
||||
|
||||
@param key The key identifying the image to be removed. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)removeImageForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Removes the image of the specified key in the cache.
|
||||
This method returns immediately and executes the remove operation in background.
|
||||
|
||||
@param key The key identifying the image to be removed. If nil, this method has no effect.
|
||||
@param type The cache type to remove image.
|
||||
*/
|
||||
- (void)removeImageForKey:(NSString *)key withType:(YYImageCacheType)type;
|
||||
|
||||
/**
|
||||
Returns a Boolean value that indicates whether a given key is in cache.
|
||||
If the image is not in memory, this method may blocks the calling thread until
|
||||
file read finished.
|
||||
|
||||
@param key A string identifying the image. If nil, just return NO.
|
||||
@return Whether the image is in cache.
|
||||
*/
|
||||
- (BOOL)containsImageForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Returns a Boolean value that indicates whether a given key is in cache.
|
||||
If the image is not in memory and the `type` contains `YYImageCacheTypeDisk`,
|
||||
this method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the image. If nil, just return NO.
|
||||
@param type The cache type.
|
||||
@return Whether the image is in cache.
|
||||
*/
|
||||
- (BOOL)containsImageForKey:(NSString *)key withType:(YYImageCacheType)type;
|
||||
|
||||
/**
|
||||
Returns the image associated with a given key.
|
||||
If the image is not in memory, this method may blocks the calling thread until
|
||||
file read finished.
|
||||
|
||||
@param key A string identifying the image. If nil, just return nil.
|
||||
@return The image associated with key, or nil if no image is associated with key.
|
||||
*/
|
||||
- (nullable UIImage *)getImageForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Returns the image associated with a given key.
|
||||
If the image is not in memory and the `type` contains `YYImageCacheTypeDisk`,
|
||||
this method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the image. If nil, just return nil.
|
||||
@return The image associated with key, or nil if no image is associated with key.
|
||||
*/
|
||||
- (nullable UIImage *)getImageForKey:(NSString *)key withType:(YYImageCacheType)type;
|
||||
|
||||
/**
|
||||
Asynchronously get the image associated with a given key.
|
||||
|
||||
@param key A string identifying the image. If nil, just return nil.
|
||||
@param type The cache type.
|
||||
@param block A completion block which will be called on main thread.
|
||||
*/
|
||||
- (void)getImageForKey:(NSString *)key
|
||||
withType:(YYImageCacheType)type
|
||||
withBlock:(void(^)(UIImage * _Nullable image, YYImageCacheType type))block;
|
||||
|
||||
/**
|
||||
Returns the image data associated with a given key.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the image. If nil, just return nil.
|
||||
@return The image data associated with key, or nil if no image is associated with key.
|
||||
*/
|
||||
- (nullable NSData *)getImageDataForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Asynchronously get the image data associated with a given key.
|
||||
|
||||
@param key A string identifying the image. If nil, just return nil.
|
||||
@param block A completion block which will be called on main thread.
|
||||
*/
|
||||
- (void)getImageDataForKey:(NSString *)key
|
||||
withBlock:(void(^)(NSData * _Nullable imageData))block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
253
Example/Pods/YYWebImage/YYWebImage/YYImageCache.m
generated
Normal file
253
Example/Pods/YYWebImage/YYWebImage/YYImageCache.m
generated
Normal file
@@ -0,0 +1,253 @@
|
||||
//
|
||||
// YYImageCache.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/15.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYImageCache.h"
|
||||
#import "YYImage.h"
|
||||
#import "UIImage+YYWebImage.h"
|
||||
|
||||
#if __has_include(<YYImage/YYImage.h>)
|
||||
#import <YYImage/YYImage.h>
|
||||
#else
|
||||
#import "YYImage.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<YYCache/YYCache.h>)
|
||||
#import <YYCache/YYCache.h>
|
||||
#else
|
||||
#import "YYCache.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static inline dispatch_queue_t YYImageCacheIOQueue() {
|
||||
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
}
|
||||
|
||||
static inline dispatch_queue_t YYImageCacheDecodeQueue() {
|
||||
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
|
||||
}
|
||||
|
||||
|
||||
@interface YYImageCache ()
|
||||
- (NSUInteger)imageCost:(UIImage *)image;
|
||||
- (UIImage *)imageFromData:(NSData *)data;
|
||||
@end
|
||||
|
||||
|
||||
@implementation YYImageCache
|
||||
|
||||
- (NSUInteger)imageCost:(UIImage *)image {
|
||||
CGImageRef cgImage = image.CGImage;
|
||||
if (!cgImage) return 1;
|
||||
CGFloat height = CGImageGetHeight(cgImage);
|
||||
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
|
||||
NSUInteger cost = bytesPerRow * height;
|
||||
if (cost == 0) cost = 1;
|
||||
return cost;
|
||||
}
|
||||
|
||||
- (UIImage *)imageFromData:(NSData *)data {
|
||||
NSData *scaleData = [YYDiskCache getExtendedDataFromObject:data];
|
||||
CGFloat scale = 0;
|
||||
if (scaleData) {
|
||||
scale = ((NSNumber *)[NSKeyedUnarchiver unarchiveObjectWithData:scaleData]).doubleValue;
|
||||
}
|
||||
if (scale <= 0) scale = [UIScreen mainScreen].scale;
|
||||
UIImage *image;
|
||||
if (_allowAnimatedImage) {
|
||||
image = [[YYImage alloc] initWithData:data scale:scale];
|
||||
if (_decodeForDisplay) image = [image yy_imageByDecoded];
|
||||
} else {
|
||||
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale];
|
||||
image = [decoder frameAtIndex:0 decodeForDisplay:_decodeForDisplay].image;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
+ (instancetype)sharedCache {
|
||||
static YYImageCache *cache = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
|
||||
NSUserDomainMask, YES) firstObject];
|
||||
cachePath = [cachePath stringByAppendingPathComponent:@"com.ibireme.yykit"];
|
||||
cachePath = [cachePath stringByAppendingPathComponent:@"images"];
|
||||
cache = [[self alloc] initWithPath:cachePath];
|
||||
});
|
||||
return cache;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@throw [NSException exceptionWithName:@"YYImageCache init error" reason:@"YYImageCache must be initialized with a path. Use 'initWithPath:' instead." userInfo:nil];
|
||||
return [self initWithPath:@""];
|
||||
}
|
||||
|
||||
- (instancetype)initWithPath:(NSString *)path {
|
||||
YYMemoryCache *memoryCache = [YYMemoryCache new];
|
||||
memoryCache.shouldRemoveAllObjectsOnMemoryWarning = YES;
|
||||
memoryCache.shouldRemoveAllObjectsWhenEnteringBackground = YES;
|
||||
memoryCache.countLimit = NSUIntegerMax;
|
||||
memoryCache.costLimit = NSUIntegerMax;
|
||||
memoryCache.ageLimit = 12 * 60 * 60;
|
||||
|
||||
YYDiskCache *diskCache = [[YYDiskCache alloc] initWithPath:path];
|
||||
diskCache.customArchiveBlock = ^(id object) { return (NSData *)object; };
|
||||
diskCache.customUnarchiveBlock = ^(NSData *data) { return (id)data; };
|
||||
if (!memoryCache || !diskCache) return nil;
|
||||
|
||||
self = [super init];
|
||||
_memoryCache = memoryCache;
|
||||
_diskCache = diskCache;
|
||||
_allowAnimatedImage = YES;
|
||||
_decodeForDisplay = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image forKey:(NSString *)key {
|
||||
[self setImage:image imageData:nil forKey:key withType:YYImageCacheTypeAll];
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key withType:(YYImageCacheType)type {
|
||||
if (!key || (image == nil && imageData.length == 0)) return;
|
||||
|
||||
__weak typeof(self) _self = self;
|
||||
if (type & YYImageCacheTypeMemory) { // add to memory cache
|
||||
if (image) {
|
||||
if (image.yy_isDecodedForDisplay) {
|
||||
[_memoryCache setObject:image forKey:key withCost:[_self imageCost:image]];
|
||||
} else {
|
||||
dispatch_async(YYImageCacheDecodeQueue(), ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
[self.memoryCache setObject:[image yy_imageByDecoded] forKey:key withCost:[self imageCost:image]];
|
||||
});
|
||||
}
|
||||
} else if (imageData) {
|
||||
dispatch_async(YYImageCacheDecodeQueue(), ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
UIImage *newImage = [self imageFromData:imageData];
|
||||
[self.memoryCache setObject:newImage forKey:key withCost:[self imageCost:newImage]];
|
||||
});
|
||||
}
|
||||
}
|
||||
if (type & YYImageCacheTypeDisk) { // add to disk cache
|
||||
if (imageData) {
|
||||
if (image) {
|
||||
[YYDiskCache setExtendedData:[NSKeyedArchiver archivedDataWithRootObject:@(image.scale)] toObject:imageData];
|
||||
}
|
||||
[_diskCache setObject:imageData forKey:key];
|
||||
} else if (image) {
|
||||
dispatch_async(YYImageCacheIOQueue(), ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
NSData *data = [image yy_imageDataRepresentation];
|
||||
[YYDiskCache setExtendedData:[NSKeyedArchiver archivedDataWithRootObject:@(image.scale)] toObject:data];
|
||||
[self.diskCache setObject:data forKey:key];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeImageForKey:(NSString *)key {
|
||||
[self removeImageForKey:key withType:YYImageCacheTypeAll];
|
||||
}
|
||||
|
||||
- (void)removeImageForKey:(NSString *)key withType:(YYImageCacheType)type {
|
||||
if (type & YYImageCacheTypeMemory) [_memoryCache removeObjectForKey:key];
|
||||
if (type & YYImageCacheTypeDisk) [_diskCache removeObjectForKey:key];
|
||||
}
|
||||
|
||||
- (BOOL)containsImageForKey:(NSString *)key {
|
||||
return [self containsImageForKey:key withType:YYImageCacheTypeAll];
|
||||
}
|
||||
|
||||
- (BOOL)containsImageForKey:(NSString *)key withType:(YYImageCacheType)type {
|
||||
if (type & YYImageCacheTypeMemory) {
|
||||
if ([_memoryCache containsObjectForKey:key]) return YES;
|
||||
}
|
||||
if (type & YYImageCacheTypeDisk) {
|
||||
if ([_diskCache containsObjectForKey:key]) return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (UIImage *)getImageForKey:(NSString *)key {
|
||||
return [self getImageForKey:key withType:YYImageCacheTypeAll];
|
||||
}
|
||||
|
||||
- (UIImage *)getImageForKey:(NSString *)key withType:(YYImageCacheType)type {
|
||||
if (!key) return nil;
|
||||
if (type & YYImageCacheTypeMemory) {
|
||||
UIImage *image = [_memoryCache objectForKey:key];
|
||||
if (image) return image;
|
||||
}
|
||||
if (type & YYImageCacheTypeDisk) {
|
||||
NSData *data = (id)[_diskCache objectForKey:key];
|
||||
UIImage *image = [self imageFromData:data];
|
||||
if (image && (type & YYImageCacheTypeMemory)) {
|
||||
[_memoryCache setObject:image forKey:key withCost:[self imageCost:image]];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)getImageForKey:(NSString *)key withType:(YYImageCacheType)type withBlock:(void (^)(UIImage *image, YYImageCacheType type))block {
|
||||
if (!block) return;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
UIImage *image = nil;
|
||||
|
||||
if (type & YYImageCacheTypeMemory) {
|
||||
image = [_memoryCache objectForKey:key];
|
||||
if (image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(image, YYImageCacheTypeMemory);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & YYImageCacheTypeDisk) {
|
||||
NSData *data = (id)[_diskCache objectForKey:key];
|
||||
image = [self imageFromData:data];
|
||||
if (image) {
|
||||
[_memoryCache setObject:image forKey:key];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(image, YYImageCacheTypeDisk);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(nil, YYImageCacheTypeNone);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (NSData *)getImageDataForKey:(NSString *)key {
|
||||
return (id)[_diskCache objectForKey:key];
|
||||
}
|
||||
|
||||
- (void)getImageDataForKey:(NSString *)key withBlock:(void (^)(NSData *imageData))block {
|
||||
if (!block) return;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSData *data = (id)[_diskCache objectForKey:key];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
65
Example/Pods/YYWebImage/YYWebImage/YYWebImage.h
generated
Normal file
65
Example/Pods/YYWebImage/YYWebImage/YYWebImage.h
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// YYWebImage.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/23.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
FOUNDATION_EXPORT double YYWebImageVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char YYWebImageVersionString[];
|
||||
#import <YYWebImage/YYImageCache.h>
|
||||
#import <YYWebImage/YYWebImageOperation.h>
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#import <YYWebImage/UIImage+YYWebImage.h>
|
||||
#import <YYWebImage/UIImageView+YYWebImage.h>
|
||||
#import <YYWebImage/UIButton+YYWebImage.h>
|
||||
#import <YYWebImage/CALayer+YYWebImage.h>
|
||||
#import <YYWebImage/MKAnnotationView+YYWebImage.h>
|
||||
#else
|
||||
#import "YYImageCache.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "YYWebImageManager.h"
|
||||
#import "UIImage+YYWebImage.h"
|
||||
#import "UIImageView+YYWebImage.h"
|
||||
#import "UIButton+YYWebImage.h"
|
||||
#import "CALayer+YYWebImage.h"
|
||||
#import "MKAnnotationView+YYWebImage.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<YYImage/YYImage.h>)
|
||||
#import <YYImage/YYImage.h>
|
||||
#elif __has_include(<YYWebImage/YYImage.h>)
|
||||
#import <YYWebImage/YYImage.h>
|
||||
#import <YYWebImage/YYFrameImage.h>
|
||||
#import <YYWebImage/YYSpriteSheetImage.h>
|
||||
#import <YYWebImage/YYImageCoder.h>
|
||||
#import <YYWebImage/YYAnimatedImageView.h>
|
||||
#else
|
||||
#import "YYImage.h"
|
||||
#import "YYFrameImage.h"
|
||||
#import "YYSpriteSheetImage.h"
|
||||
#import "YYImageCoder.h"
|
||||
#import "YYAnimatedImageView.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<YYCache/YYCache.h>)
|
||||
#import <YYCache/YYCache.h>
|
||||
#elif __has_include(<YYWebImage/YYCache.h>)
|
||||
#import <YYWebImage/YYCache.h>
|
||||
#import <YYWebImage/YYMemoryCache.h>
|
||||
#import <YYWebImage/YYDiskCache.h>
|
||||
#import <YYWebImage/YYKVStorage.h>
|
||||
#else
|
||||
#import "YYCache.h"
|
||||
#import "YYMemoryCache.h"
|
||||
#import "YYDiskCache.h"
|
||||
#import "YYKVStorage.h"
|
||||
#endif
|
||||
|
312
Example/Pods/YYWebImage/YYWebImage/YYWebImageManager.h
generated
Normal file
312
Example/Pods/YYWebImage/YYWebImage/YYWebImageManager.h
generated
Normal file
@@ -0,0 +1,312 @@
|
||||
//
|
||||
// YYWebImageManager.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/19.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYImageCache.h>
|
||||
#else
|
||||
#import "YYImageCache.h"
|
||||
#endif
|
||||
|
||||
@class YYWebImageOperation;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The options to control image operation.
|
||||
typedef NS_OPTIONS(NSUInteger, YYWebImageOptions) {
|
||||
|
||||
/// Show network activity on status bar when download image.
|
||||
YYWebImageOptionShowNetworkActivity = 1 << 0,
|
||||
|
||||
/// Display progressive/interlaced/baseline image during download (same as web browser).
|
||||
YYWebImageOptionProgressive = 1 << 1,
|
||||
|
||||
/// Display blurred progressive JPEG or interlaced PNG image during download.
|
||||
/// This will ignore baseline image for better user experience.
|
||||
YYWebImageOptionProgressiveBlur = 1 << 2,
|
||||
|
||||
/// Use NSURLCache instead of YYImageCache.
|
||||
YYWebImageOptionUseNSURLCache = 1 << 3,
|
||||
|
||||
/// Allows untrusted SSL ceriticates.
|
||||
YYWebImageOptionAllowInvalidSSLCertificates = 1 << 4,
|
||||
|
||||
/// Allows background task to download image when app is in background.
|
||||
YYWebImageOptionAllowBackgroundTask = 1 << 5,
|
||||
|
||||
/// Handles cookies stored in NSHTTPCookieStore.
|
||||
YYWebImageOptionHandleCookies = 1 << 6,
|
||||
|
||||
/// Load the image from remote and refresh the image cache.
|
||||
YYWebImageOptionRefreshImageCache = 1 << 7,
|
||||
|
||||
/// Do not load image from/to disk cache.
|
||||
YYWebImageOptionIgnoreDiskCache = 1 << 8,
|
||||
|
||||
/// Do not change the view's image before set a new URL to it.
|
||||
YYWebImageOptionIgnorePlaceHolder = 1 << 9,
|
||||
|
||||
/// Ignore image decoding.
|
||||
/// This may used for image downloading without display.
|
||||
YYWebImageOptionIgnoreImageDecoding = 1 << 10,
|
||||
|
||||
/// Ignore multi-frame image decoding.
|
||||
/// This will handle the GIF/APNG/WebP/ICO image as single frame image.
|
||||
YYWebImageOptionIgnoreAnimatedImage = 1 << 11,
|
||||
|
||||
/// Set the image to view with a fade animation.
|
||||
/// This will add a "fade" animation on image view's layer for better user experience.
|
||||
YYWebImageOptionSetImageWithFadeAnimation = 1 << 12,
|
||||
|
||||
/// Do not set the image to the view when image fetch complete.
|
||||
/// You may set the image manually.
|
||||
YYWebImageOptionAvoidSetImage = 1 << 13,
|
||||
|
||||
/// This flag will add the URL to a blacklist (in memory) when the URL fail to be downloaded,
|
||||
/// so the library won't keep trying.
|
||||
YYWebImageOptionIgnoreFailedURL = 1 << 14,
|
||||
};
|
||||
|
||||
/// Indicated where the image came from.
|
||||
typedef NS_ENUM(NSUInteger, YYWebImageFromType) {
|
||||
|
||||
/// No value.
|
||||
YYWebImageFromNone = 0,
|
||||
|
||||
/// Fetched from memory cache immediately.
|
||||
/// If you called "setImageWithURL:..." and the image is already in memory,
|
||||
/// then you will get this value at the same call.
|
||||
YYWebImageFromMemoryCacheFast,
|
||||
|
||||
/// Fetched from memory cache.
|
||||
YYWebImageFromMemoryCache,
|
||||
|
||||
/// Fetched from disk cache.
|
||||
YYWebImageFromDiskCache,
|
||||
|
||||
/// Fetched from remote (web or file path).
|
||||
YYWebImageFromRemote,
|
||||
};
|
||||
|
||||
/// Indicated image fetch complete stage.
|
||||
typedef NS_ENUM(NSInteger, YYWebImageStage) {
|
||||
|
||||
/// Incomplete, progressive image.
|
||||
YYWebImageStageProgress = -1,
|
||||
|
||||
/// Cancelled.
|
||||
YYWebImageStageCancelled = 0,
|
||||
|
||||
/// Finished (succeed or failed).
|
||||
YYWebImageStageFinished = 1,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
The block invoked in remote image fetch progress.
|
||||
|
||||
@param receivedSize Current received size in bytes.
|
||||
@param expectedSize Expected total size in bytes (-1 means unknown).
|
||||
*/
|
||||
typedef void(^YYWebImageProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
|
||||
|
||||
/**
|
||||
The block invoked before remote image fetch finished to do additional image process.
|
||||
|
||||
@discussion This block will be invoked before `YYWebImageCompletionBlock` to give
|
||||
you a chance to do additional image process (such as resize or crop). If there's
|
||||
no need to transform the image, just return the `image` parameter.
|
||||
|
||||
@example You can clip the image, blur it and add rounded corners with these code:
|
||||
^(UIImage *image, NSURL *url) {
|
||||
// Maybe you need to create an @autoreleasepool to limit memory cost.
|
||||
image = [image yy_imageByResizeToSize:CGSizeMake(100, 100) contentMode:UIViewContentModeScaleAspectFill];
|
||||
image = [image yy_imageByBlurRadius:20 tintColor:nil tintMode:kCGBlendModeNormal saturation:1.2 maskImage:nil];
|
||||
image = [image yy_imageByRoundCornerRadius:5];
|
||||
return image;
|
||||
}
|
||||
|
||||
@param image The image fetched from url.
|
||||
@param url The image url (remote or local file path).
|
||||
@return The transformed image.
|
||||
*/
|
||||
typedef UIImage * _Nullable (^YYWebImageTransformBlock)(UIImage *image, NSURL *url);
|
||||
|
||||
/**
|
||||
The block invoked when image fetch finished or cancelled.
|
||||
|
||||
@param image The image.
|
||||
@param url The image url (remote or local file path).
|
||||
@param from Where the image came from.
|
||||
@param error Error during image fetching.
|
||||
@param finished If the operation is cancelled, this value is NO, otherwise YES.
|
||||
*/
|
||||
typedef void (^YYWebImageCompletionBlock)(UIImage * _Nullable image,
|
||||
NSURL *url,
|
||||
YYWebImageFromType from,
|
||||
YYWebImageStage stage,
|
||||
NSError * _Nullable error);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A manager to create and manage web image operation.
|
||||
*/
|
||||
@interface YYWebImageManager : NSObject
|
||||
|
||||
/**
|
||||
Returns global YYWebImageManager instance.
|
||||
|
||||
@return YYWebImageManager shared instance.
|
||||
*/
|
||||
+ (instancetype)sharedManager;
|
||||
|
||||
/**
|
||||
Creates a manager with an image cache and operation queue.
|
||||
|
||||
@param cache Image cache used by manager (pass nil to avoid image cache).
|
||||
@param queue The operation queue on which image operations are scheduled and run
|
||||
(pass nil to make the new operation start immediately without queue).
|
||||
@return A new manager.
|
||||
*/
|
||||
- (instancetype)initWithCache:(nullable YYImageCache *)cache
|
||||
queue:(nullable NSOperationQueue *)queue NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
Creates and returns a new image operation, the operation will start immediately.
|
||||
|
||||
@param url The image url (remote or local file path).
|
||||
@param options The options to control image operation.
|
||||
@param progress Progress block which will be invoked on background thread (pass nil to avoid).
|
||||
@param transform Transform block which will be invoked on background thread (pass nil to avoid).
|
||||
@param completion Completion block which will be invoked on background thread (pass nil to avoid).
|
||||
@return A new image operation.
|
||||
*/
|
||||
- (nullable YYWebImageOperation *)requestImageWithURL:(NSURL *)url
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
The image cache used by image operation.
|
||||
You can set it to nil to avoid image cache.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) YYImageCache *cache;
|
||||
|
||||
/**
|
||||
The operation queue on which image operations are scheduled and run.
|
||||
You can set it to nil to make the new operation start immediately without queue.
|
||||
|
||||
You can use this queue to control maximum number of concurrent operations, to obtain
|
||||
the status of the current operations, or to cancel all operations in this manager.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSOperationQueue *queue;
|
||||
|
||||
/**
|
||||
The shared transform block to process image. Default is nil.
|
||||
|
||||
When called `requestImageWithURL:options:progress:transform:completion` and
|
||||
the `transform` is nil, this block will be used.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) YYWebImageTransformBlock sharedTransformBlock;
|
||||
|
||||
/**
|
||||
The image request timeout interval in seconds. Default is 15.
|
||||
*/
|
||||
@property (nonatomic) NSTimeInterval timeout;
|
||||
|
||||
/**
|
||||
The username used by NSURLCredential, default is nil.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSString *username;
|
||||
|
||||
/**
|
||||
The password used by NSURLCredential, default is nil.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSString *password;
|
||||
|
||||
/**
|
||||
The image HTTP request header. Default is "Accept:image/webp,image/\*;q=0.8".
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSDictionary<NSString *, NSString *> *headers;
|
||||
|
||||
/**
|
||||
A block which will be invoked for each image HTTP request to do additional
|
||||
HTTP header process. Default is nil.
|
||||
|
||||
Use this block to add or remove HTTP header field for a specified URL.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSDictionary<NSString *, NSString *> *(^headersFilter)(NSURL *url, NSDictionary<NSString *, NSString *> * _Nullable header);
|
||||
|
||||
/**
|
||||
A block which will be invoked for each image operation. Default is nil.
|
||||
|
||||
Use this block to provide a custom image cache key for a specified URL.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSString *(^cacheKeyFilter)(NSURL *url);
|
||||
|
||||
/**
|
||||
Returns the HTTP headers for a specified URL.
|
||||
|
||||
@param url A specified URL.
|
||||
@return HTTP headers.
|
||||
*/
|
||||
- (nullable NSDictionary<NSString *, NSString *> *)headersForURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
Returns the cache key for a specified URL.
|
||||
|
||||
@param url A specified URL
|
||||
@return Cache key used in YYImageCache.
|
||||
*/
|
||||
- (NSString *)cacheKeyForURL:(NSURL *)url;
|
||||
|
||||
|
||||
/**
|
||||
Increments the number of active network requests.
|
||||
If this number was zero before incrementing, this will start animating the
|
||||
status bar network activity indicator.
|
||||
|
||||
This method is thread safe.
|
||||
|
||||
This method has no effect in App Extension.
|
||||
*/
|
||||
+ (void)incrementNetworkActivityCount;
|
||||
|
||||
/**
|
||||
Decrements the number of active network requests.
|
||||
If this number becomes zero after decrementing, this will stop animating the
|
||||
status bar network activity indicator.
|
||||
|
||||
This method is thread safe.
|
||||
|
||||
This method has no effect in App Extension.
|
||||
*/
|
||||
+ (void)decrementNetworkActivityCount;
|
||||
|
||||
/**
|
||||
Get current number of active network requests.
|
||||
|
||||
This method is thread safe.
|
||||
|
||||
This method has no effect in App Extension.
|
||||
*/
|
||||
+ (NSInteger)currentNetworkActivityCount;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
184
Example/Pods/YYWebImage/YYWebImage/YYWebImageManager.m
generated
Normal file
184
Example/Pods/YYWebImage/YYWebImage/YYWebImageManager.m
generated
Normal file
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// YYWebImageManager.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/19.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYWebImageManager.h"
|
||||
#import "YYImageCache.h"
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "YYImageCoder.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#define kNetworkIndicatorDelay (1/30.0)
|
||||
|
||||
|
||||
/// Returns nil in App Extension.
|
||||
static UIApplication *_YYSharedApplication() {
|
||||
static BOOL isAppExtension = NO;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class cls = NSClassFromString(@"UIApplication");
|
||||
if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
|
||||
if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
|
||||
});
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
return isAppExtension ? nil : [UIApplication performSelector:@selector(sharedApplication)];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
|
||||
@interface _YYWebImageApplicationNetworkIndicatorInfo : NSObject
|
||||
@property (nonatomic, assign) NSInteger count;
|
||||
@property (nonatomic, strong) NSTimer *timer;
|
||||
@end
|
||||
@implementation _YYWebImageApplicationNetworkIndicatorInfo
|
||||
@end
|
||||
|
||||
@implementation YYWebImageManager
|
||||
|
||||
+ (instancetype)sharedManager {
|
||||
static YYWebImageManager *manager;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
YYImageCache *cache = [YYImageCache sharedCache];
|
||||
NSOperationQueue *queue = [NSOperationQueue new];
|
||||
if ([queue respondsToSelector:@selector(setQualityOfService:)]) {
|
||||
queue.qualityOfService = NSQualityOfServiceBackground;
|
||||
}
|
||||
manager = [[self alloc] initWithCache:cache queue:queue];
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@throw [NSException exceptionWithName:@"YYWebImageManager init error" reason:@"Use the designated initializer to init." userInfo:nil];
|
||||
return [self initWithCache:nil queue:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue{
|
||||
self = [super init];
|
||||
if (!self) return nil;
|
||||
_cache = cache;
|
||||
_queue = queue;
|
||||
_timeout = 15.0;
|
||||
if (YYImageWebPAvailable()) {
|
||||
_headers = @{ @"Accept" : @"image/webp,image/*;q=0.8" };
|
||||
} else {
|
||||
_headers = @{ @"Accept" : @"image/*;q=0.8" };
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
|
||||
options:(YYWebImageOptions)options
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.timeoutInterval = _timeout;
|
||||
request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0;
|
||||
request.allHTTPHeaderFields = [self headersForURL:url];
|
||||
request.HTTPShouldUsePipelining = YES;
|
||||
request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?
|
||||
NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
|
||||
|
||||
YYWebImageOperation *operation = [[YYWebImageOperation alloc] initWithRequest:request
|
||||
options:options
|
||||
cache:_cache
|
||||
cacheKey:[self cacheKeyForURL:url]
|
||||
progress:progress
|
||||
transform:transform ? transform : _sharedTransformBlock
|
||||
completion:completion];
|
||||
|
||||
if (_username && _password) {
|
||||
operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession];
|
||||
}
|
||||
if (operation) {
|
||||
NSOperationQueue *queue = _queue;
|
||||
if (queue) {
|
||||
[queue addOperation:operation];
|
||||
} else {
|
||||
[operation start];
|
||||
}
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
|
||||
- (NSDictionary *)headersForURL:(NSURL *)url {
|
||||
if (!url) return nil;
|
||||
return _headersFilter ? _headersFilter(url, _headers) : _headers;
|
||||
}
|
||||
|
||||
- (NSString *)cacheKeyForURL:(NSURL *)url {
|
||||
if (!url) return nil;
|
||||
return _cacheKeyFilter ? _cacheKeyFilter(url) : url.absoluteString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark Network Indicator
|
||||
|
||||
+ (_YYWebImageApplicationNetworkIndicatorInfo *)_networkIndicatorInfo {
|
||||
return objc_getAssociatedObject(self, @selector(_networkIndicatorInfo));
|
||||
}
|
||||
|
||||
+ (void)_setNetworkIndicatorInfo:(_YYWebImageApplicationNetworkIndicatorInfo *)info {
|
||||
objc_setAssociatedObject(self, @selector(_networkIndicatorInfo), info, OBJC_ASSOCIATION_RETAIN);
|
||||
}
|
||||
|
||||
+ (void)_delaySetActivity:(NSTimer *)timer {
|
||||
UIApplication *app = _YYSharedApplication();
|
||||
if (!app) return;
|
||||
|
||||
NSNumber *visiable = timer.userInfo;
|
||||
if (app.networkActivityIndicatorVisible != visiable.boolValue) {
|
||||
[app setNetworkActivityIndicatorVisible:visiable.boolValue];
|
||||
}
|
||||
[timer invalidate];
|
||||
}
|
||||
|
||||
+ (void)_changeNetworkActivityCount:(NSInteger)delta {
|
||||
if (!_YYSharedApplication()) return;
|
||||
|
||||
void (^block)() = ^{
|
||||
_YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
|
||||
if (!info) {
|
||||
info = [_YYWebImageApplicationNetworkIndicatorInfo new];
|
||||
[self _setNetworkIndicatorInfo:info];
|
||||
}
|
||||
NSInteger count = info.count;
|
||||
count += delta;
|
||||
info.count = count;
|
||||
[info.timer invalidate];
|
||||
info.timer = [NSTimer timerWithTimeInterval:kNetworkIndicatorDelay target:self selector:@selector(_delaySetActivity:) userInfo:@(info.count > 0) repeats:NO];
|
||||
[[NSRunLoop mainRunLoop] addTimer:info.timer forMode:NSRunLoopCommonModes];
|
||||
};
|
||||
if ([NSThread isMainThread]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)incrementNetworkActivityCount {
|
||||
[self _changeNetworkActivityCount:1];
|
||||
}
|
||||
|
||||
+ (void)decrementNetworkActivityCount {
|
||||
[self _changeNetworkActivityCount:-1];
|
||||
}
|
||||
|
||||
+ (NSInteger)currentNetworkActivityCount {
|
||||
_YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
|
||||
return info.count;
|
||||
}
|
||||
|
||||
@end
|
97
Example/Pods/YYWebImage/YYWebImage/YYWebImageOperation.h
generated
Normal file
97
Example/Pods/YYWebImage/YYWebImage/YYWebImageOperation.h
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// YYWebImageOperation.h
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/15.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYWebImage/YYWebImage.h>)
|
||||
#import <YYWebImage/YYImageCache.h>
|
||||
#import <YYWebImage/YYWebImageManager.h>
|
||||
#else
|
||||
#import "YYImageCache.h"
|
||||
#import "YYWebImageManager.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The YYWebImageOperation class is an NSOperation subclass used to fetch image
|
||||
from URL request.
|
||||
|
||||
@discussion It's an asynchronous operation. You typically execute it by adding
|
||||
it to an operation queue, or calls 'start' to execute it manually. When the
|
||||
operation is started, it will:
|
||||
|
||||
1. Get the image from the cache, if exist, return it with `completion` block.
|
||||
2. Start an URL connection to fetch image from the request, invoke the `progress`
|
||||
to notify request progress (and invoke `completion` block to return the
|
||||
progressive image if enabled by progressive option).
|
||||
3. Process the image by invoke the `transform` block.
|
||||
4. Put the image to cache and return it with `completion` block.
|
||||
|
||||
*/
|
||||
@interface YYWebImageOperation : NSOperation
|
||||
|
||||
@property (nonatomic, strong, readonly) NSURLRequest *request; ///< The image URL request.
|
||||
@property (nullable, nonatomic, strong, readonly) NSURLResponse *response; ///< The response for request.
|
||||
@property (nullable, nonatomic, strong, readonly) YYImageCache *cache; ///< The image cache.
|
||||
@property (nonatomic, strong, readonly) NSString *cacheKey; ///< The image cache key.
|
||||
@property (nonatomic, readonly) YYWebImageOptions options; ///< The operation's option.
|
||||
|
||||
/**
|
||||
Whether the URL connection should consult the credential storage for authenticating
|
||||
the connection. Default is YES.
|
||||
|
||||
@discussion This is the value that is returned in the `NSURLConnectionDelegate`
|
||||
method `-connectionShouldUseCredentialStorage:`.
|
||||
*/
|
||||
@property (nonatomic) BOOL shouldUseCredentialStorage;
|
||||
|
||||
/**
|
||||
The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
|
||||
|
||||
@discussion This will be overridden by any shared credentials that exist for the
|
||||
username or password of the request URL, if present.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSURLCredential *credential;
|
||||
|
||||
/**
|
||||
Creates and returns a new operation.
|
||||
|
||||
You should call `start` to execute this operation, or you can add the operation
|
||||
to an operation queue.
|
||||
|
||||
@param request The Image request. This value should not be nil.
|
||||
@param options A mask to specify options to use for this operation.
|
||||
@param cache An image cache. Pass nil to avoid image cache.
|
||||
@param cacheKey An image cache key. Pass nil to avoid image cache.
|
||||
@param progress A block invoked in image fetch progress.
|
||||
The block will be invoked in background thread. Pass nil to avoid it.
|
||||
@param transform A block invoked before image fetch finished to do additional image process.
|
||||
The block will be invoked in background thread. Pass nil to avoid it.
|
||||
@param completion A block invoked when image fetch finished or cancelled.
|
||||
The block will be invoked in background thread. Pass nil to avoid it.
|
||||
|
||||
@return The image request opeartion, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
options:(YYWebImageOptions)options
|
||||
cache:(nullable YYImageCache *)cache
|
||||
cacheKey:(nullable NSString *)cacheKey
|
||||
progress:(nullable YYWebImageProgressBlock)progress
|
||||
transform:(nullable YYWebImageTransformBlock)transform
|
||||
completion:(nullable YYWebImageCompletionBlock)completion NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
868
Example/Pods/YYWebImage/YYWebImage/YYWebImageOperation.m
generated
Normal file
868
Example/Pods/YYWebImage/YYWebImage/YYWebImageOperation.m
generated
Normal file
@@ -0,0 +1,868 @@
|
||||
//
|
||||
// YYWebImageOperation.m
|
||||
// YYWebImage <https://github.com/ibireme/YYWebImage>
|
||||
//
|
||||
// Created by ibireme on 15/2/15.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYWebImageOperation.h"
|
||||
#import "UIImage+YYWebImage.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#if __has_include(<YYImage/YYImage.h>)
|
||||
#import <YYImage/YYImage.h>
|
||||
#else
|
||||
#import "YYImage.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define MIN_PROGRESSIVE_TIME_INTERVAL 0.2
|
||||
#define MIN_PROGRESSIVE_BLUR_TIME_INTERVAL 0.4
|
||||
|
||||
|
||||
/// Returns nil in App Extension.
|
||||
static UIApplication *_YYSharedApplication() {
|
||||
static BOOL isAppExtension = NO;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class cls = NSClassFromString(@"UIApplication");
|
||||
if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
|
||||
if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
|
||||
});
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
return isAppExtension ? nil : [UIApplication performSelector:@selector(sharedApplication)];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
/// Returns YES if the right-bottom pixel is filled.
|
||||
static BOOL YYCGImageLastPixelFilled(CGImageRef image) {
|
||||
if (!image) return NO;
|
||||
size_t width = CGImageGetWidth(image);
|
||||
size_t height = CGImageGetHeight(image);
|
||||
if (width == 0 || height == 0) return NO;
|
||||
CGContextRef ctx = CGBitmapContextCreate(NULL, 1, 1, 8, 0, YYCGColorSpaceGetDeviceRGB(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrderDefault);
|
||||
if (!ctx) return NO;
|
||||
CGContextDrawImage(ctx, CGRectMake( -(int)width + 1, 0, width, height), image);
|
||||
uint8_t *bytes = CGBitmapContextGetData(ctx);
|
||||
BOOL isAlpha = bytes && bytes[0] == 0;
|
||||
CFRelease(ctx);
|
||||
return !isAlpha;
|
||||
}
|
||||
|
||||
/// Returns JPEG SOS (Start Of Scan) Marker
|
||||
static NSData *JPEGSOSMarker() {
|
||||
// "Start Of Scan" Marker
|
||||
static NSData *marker = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
uint8_t bytes[2] = {0xFF, 0xDA};
|
||||
marker = [NSData dataWithBytes:bytes length:2];
|
||||
});
|
||||
return marker;
|
||||
}
|
||||
|
||||
|
||||
static NSMutableSet *URLBlacklist;
|
||||
static dispatch_semaphore_t URLBlacklistLock;
|
||||
|
||||
static void URLBlacklistInit() {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
URLBlacklist = [NSMutableSet new];
|
||||
URLBlacklistLock = dispatch_semaphore_create(1);
|
||||
});
|
||||
}
|
||||
|
||||
static BOOL URLBlackListContains(NSURL *url) {
|
||||
if (!url || url == (id)[NSNull null]) return NO;
|
||||
URLBlacklistInit();
|
||||
dispatch_semaphore_wait(URLBlacklistLock, DISPATCH_TIME_FOREVER);
|
||||
BOOL contains = [URLBlacklist containsObject:url];
|
||||
dispatch_semaphore_signal(URLBlacklistLock);
|
||||
return contains;
|
||||
}
|
||||
|
||||
static void URLInBlackListAdd(NSURL *url) {
|
||||
if (!url || url == (id)[NSNull null]) return;
|
||||
URLBlacklistInit();
|
||||
dispatch_semaphore_wait(URLBlacklistLock, DISPATCH_TIME_FOREVER);
|
||||
[URLBlacklist addObject:url];
|
||||
dispatch_semaphore_signal(URLBlacklistLock);
|
||||
}
|
||||
|
||||
|
||||
/// A proxy used to hold a weak object.
|
||||
@interface _YYWebImageWeakProxy : NSProxy
|
||||
@property (nonatomic, weak, readonly) id target;
|
||||
- (instancetype)initWithTarget:(id)target;
|
||||
+ (instancetype)proxyWithTarget:(id)target;
|
||||
@end
|
||||
|
||||
@implementation _YYWebImageWeakProxy
|
||||
- (instancetype)initWithTarget:(id)target {
|
||||
_target = target;
|
||||
return self;
|
||||
}
|
||||
+ (instancetype)proxyWithTarget:(id)target {
|
||||
return [[_YYWebImageWeakProxy alloc] initWithTarget:target];
|
||||
}
|
||||
- (id)forwardingTargetForSelector:(SEL)selector {
|
||||
return _target;
|
||||
}
|
||||
- (void)forwardInvocation:(NSInvocation *)invocation {
|
||||
void *null = NULL;
|
||||
[invocation setReturnValue:&null];
|
||||
}
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
|
||||
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
|
||||
}
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector {
|
||||
return [_target respondsToSelector:aSelector];
|
||||
}
|
||||
- (BOOL)isEqual:(id)object {
|
||||
return [_target isEqual:object];
|
||||
}
|
||||
- (NSUInteger)hash {
|
||||
return [_target hash];
|
||||
}
|
||||
- (Class)superclass {
|
||||
return [_target superclass];
|
||||
}
|
||||
- (Class)class {
|
||||
return [_target class];
|
||||
}
|
||||
- (BOOL)isKindOfClass:(Class)aClass {
|
||||
return [_target isKindOfClass:aClass];
|
||||
}
|
||||
- (BOOL)isMemberOfClass:(Class)aClass {
|
||||
return [_target isMemberOfClass:aClass];
|
||||
}
|
||||
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
|
||||
return [_target conformsToProtocol:aProtocol];
|
||||
}
|
||||
- (BOOL)isProxy {
|
||||
return YES;
|
||||
}
|
||||
- (NSString *)description {
|
||||
return [_target description];
|
||||
}
|
||||
- (NSString *)debugDescription {
|
||||
return [_target debugDescription];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface YYWebImageOperation() <NSURLConnectionDelegate>
|
||||
@property (readwrite, getter=isExecuting) BOOL executing;
|
||||
@property (readwrite, getter=isFinished) BOOL finished;
|
||||
@property (readwrite, getter=isCancelled) BOOL cancelled;
|
||||
@property (readwrite, getter=isStarted) BOOL started;
|
||||
@property (nonatomic, strong) NSRecursiveLock *lock;
|
||||
@property (nonatomic, strong) NSURLConnection *connection;
|
||||
@property (nonatomic, strong) NSMutableData *data;
|
||||
@property (nonatomic, assign) NSInteger expectedSize;
|
||||
@property (nonatomic, assign) UIBackgroundTaskIdentifier taskID;
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval lastProgressiveDecodeTimestamp;
|
||||
@property (nonatomic, strong) YYImageDecoder *progressiveDecoder;
|
||||
@property (nonatomic, assign) BOOL progressiveIgnored;
|
||||
@property (nonatomic, assign) BOOL progressiveDetected;
|
||||
@property (nonatomic, assign) NSUInteger progressiveScanedLength;
|
||||
@property (nonatomic, assign) NSUInteger progressiveDisplayCount;
|
||||
|
||||
@property (nonatomic, copy) YYWebImageProgressBlock progress;
|
||||
@property (nonatomic, copy) YYWebImageTransformBlock transform;
|
||||
@property (nonatomic, copy) YYWebImageCompletionBlock completion;
|
||||
@end
|
||||
|
||||
|
||||
@implementation YYWebImageOperation
|
||||
@synthesize executing = _executing;
|
||||
@synthesize finished = _finished;
|
||||
@synthesize cancelled = _cancelled;
|
||||
|
||||
/// Network thread entry point.
|
||||
+ (void)_networkThreadMain:(id)object {
|
||||
@autoreleasepool {
|
||||
[[NSThread currentThread] setName:@"com.ibireme.webimage.request"];
|
||||
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
||||
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
|
||||
[runLoop run];
|
||||
}
|
||||
}
|
||||
|
||||
/// Global image request network thread, used by NSURLConnection delegate.
|
||||
+ (NSThread *)_networkThread {
|
||||
static NSThread *thread = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
thread = [[NSThread alloc] initWithTarget:self selector:@selector(_networkThreadMain:) object:nil];
|
||||
if ([thread respondsToSelector:@selector(setQualityOfService:)]) {
|
||||
thread.qualityOfService = NSQualityOfServiceBackground;
|
||||
}
|
||||
[thread start];
|
||||
});
|
||||
return thread;
|
||||
}
|
||||
|
||||
/// Global image queue, used for image reading and decoding.
|
||||
+ (dispatch_queue_t)_imageQueue {
|
||||
#define MAX_QUEUE_COUNT 16
|
||||
static int queueCount;
|
||||
static dispatch_queue_t queues[MAX_QUEUE_COUNT];
|
||||
static dispatch_once_t onceToken;
|
||||
static int32_t counter = 0;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
|
||||
queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
|
||||
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
|
||||
for (NSUInteger i = 0; i < queueCount; i++) {
|
||||
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
|
||||
queues[i] = dispatch_queue_create("com.ibireme.image.decode", attr);
|
||||
}
|
||||
} else {
|
||||
for (NSUInteger i = 0; i < queueCount; i++) {
|
||||
queues[i] = dispatch_queue_create("com.ibireme.image.decode", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
int32_t cur = OSAtomicIncrement32(&counter);
|
||||
if (cur < 0) cur = -cur;
|
||||
return queues[(cur) % queueCount];
|
||||
#undef MAX_QUEUE_COUNT
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@throw [NSException exceptionWithName:@"YYWebImageOperation init error" reason:@"YYWebImageOperation must be initialized with a request. Use the designated initializer to init." userInfo:nil];
|
||||
return [self initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]] options:0 cache:nil cacheKey:nil progress:nil transform:nil completion:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
options:(YYWebImageOptions)options
|
||||
cache:(YYImageCache *)cache
|
||||
cacheKey:(NSString *)cacheKey
|
||||
progress:(YYWebImageProgressBlock)progress
|
||||
transform:(YYWebImageTransformBlock)transform
|
||||
completion:(YYWebImageCompletionBlock)completion {
|
||||
self = [super init];
|
||||
if (!self) return nil;
|
||||
if (!request) return nil;
|
||||
_request = request;
|
||||
_options = options;
|
||||
_cache = cache;
|
||||
_cacheKey = cacheKey ? cacheKey : request.URL.absoluteString;
|
||||
_shouldUseCredentialStorage = YES;
|
||||
_progress = progress;
|
||||
_transform = transform;
|
||||
_completion = completion;
|
||||
_executing = NO;
|
||||
_finished = NO;
|
||||
_cancelled = NO;
|
||||
_taskID = UIBackgroundTaskInvalid;
|
||||
_lock = [NSRecursiveLock new];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_lock lock];
|
||||
if (_taskID != UIBackgroundTaskInvalid) {
|
||||
[_YYSharedApplication() endBackgroundTask:_taskID];
|
||||
_taskID = UIBackgroundTaskInvalid;
|
||||
}
|
||||
if ([self isExecuting]) {
|
||||
self.cancelled = YES;
|
||||
self.finished = YES;
|
||||
if (_connection) {
|
||||
[_connection cancel];
|
||||
if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {
|
||||
[YYWebImageManager decrementNetworkActivityCount];
|
||||
}
|
||||
}
|
||||
if (_completion) {
|
||||
@autoreleasepool {
|
||||
_completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (void)_endBackgroundTask {
|
||||
[_lock lock];
|
||||
if (_taskID != UIBackgroundTaskInvalid) {
|
||||
[_YYSharedApplication() endBackgroundTask:_taskID];
|
||||
_taskID = UIBackgroundTaskInvalid;
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
#pragma mark - Runs in operation thread
|
||||
|
||||
- (void)_finish {
|
||||
self.executing = NO;
|
||||
self.finished = YES;
|
||||
[self _endBackgroundTask];
|
||||
}
|
||||
|
||||
// runs on network thread
|
||||
- (void)_startOperation {
|
||||
if ([self isCancelled]) return;
|
||||
@autoreleasepool {
|
||||
// get image from cache
|
||||
if (_cache &&
|
||||
!(_options & YYWebImageOptionUseNSURLCache) &&
|
||||
!(_options & YYWebImageOptionRefreshImageCache)) {
|
||||
UIImage *image = [_cache getImageForKey:_cacheKey withType:YYImageCacheTypeMemory];
|
||||
if (image) {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
if (_completion) _completion(image, _request.URL, YYWebImageFromMemoryCache, YYWebImageStageFinished, nil);
|
||||
}
|
||||
[self _finish];
|
||||
[_lock unlock];
|
||||
return;
|
||||
}
|
||||
if (!(_options & YYWebImageOptionIgnoreDiskCache)) {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([self.class _imageQueue], ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self || [self isCancelled]) return;
|
||||
UIImage *image = [self.cache getImageForKey:self.cacheKey withType:YYImageCacheTypeDisk];
|
||||
if (image) {
|
||||
[self.cache setImage:image imageData:nil forKey:self.cacheKey withType:YYImageCacheTypeMemory];
|
||||
[self performSelector:@selector(_didReceiveImageFromDiskCache:) onThread:[self.class _networkThread] withObject:image waitUntilDone:NO];
|
||||
} else {
|
||||
[self performSelector:@selector(_startRequest:) onThread:[self.class _networkThread] withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[self performSelector:@selector(_startRequest:) onThread:[self.class _networkThread] withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
|
||||
// runs on network thread
|
||||
- (void)_startRequest:(id)object {
|
||||
if ([self isCancelled]) return;
|
||||
@autoreleasepool {
|
||||
if ((_options & YYWebImageOptionIgnoreFailedURL) && URLBlackListContains(_request.URL)) {
|
||||
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{ NSLocalizedDescriptionKey : @"Failed to load URL, blacklisted." }];
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
if (_completion) _completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageFinished, error);
|
||||
}
|
||||
[self _finish];
|
||||
[_lock unlock];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_request.URL.isFileURL) {
|
||||
NSArray *keys = @[NSURLFileSizeKey];
|
||||
NSDictionary *attr = [_request.URL resourceValuesForKeys:keys error:nil];
|
||||
NSNumber *fileSize = attr[NSURLFileSizeKey];
|
||||
_expectedSize = fileSize ? fileSize.unsignedIntegerValue : -1;
|
||||
}
|
||||
|
||||
// request image from web
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
_connection = [[NSURLConnection alloc] initWithRequest:_request delegate:[_YYWebImageWeakProxy proxyWithTarget:self]];
|
||||
if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {
|
||||
[YYWebImageManager incrementNetworkActivityCount];
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
// runs on network thread, called from outer "cancel"
|
||||
- (void)_cancelOperation {
|
||||
@autoreleasepool {
|
||||
if (_connection) {
|
||||
if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {
|
||||
[YYWebImageManager decrementNetworkActivityCount];
|
||||
}
|
||||
}
|
||||
[_connection cancel];
|
||||
_connection = nil;
|
||||
if (_completion) _completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageCancelled, nil);
|
||||
[self _endBackgroundTask];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// runs on network thread
|
||||
- (void)_didReceiveImageFromDiskCache:(UIImage *)image {
|
||||
@autoreleasepool {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
if (image) {
|
||||
if (_completion) _completion(image, _request.URL, YYWebImageFromDiskCache, YYWebImageStageFinished, nil);
|
||||
[self _finish];
|
||||
} else {
|
||||
[self _startRequest:nil];
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_didReceiveImageFromWeb:(UIImage *)image {
|
||||
@autoreleasepool {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
if (_cache) {
|
||||
if (image || (_options & YYWebImageOptionRefreshImageCache)) {
|
||||
NSData *data = _data;
|
||||
dispatch_async([YYWebImageOperation _imageQueue], ^{
|
||||
[_cache setImage:image imageData:data forKey:_cacheKey withType:YYImageCacheTypeAll];
|
||||
});
|
||||
}
|
||||
}
|
||||
_data = nil;
|
||||
NSError *error = nil;
|
||||
if (!image) {
|
||||
error = [NSError errorWithDomain:@"com.ibireme.image" code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Web image decode fail." }];
|
||||
if (_options & YYWebImageOptionIgnoreFailedURL) {
|
||||
if (URLBlackListContains(_request.URL)) {
|
||||
error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{ NSLocalizedDescriptionKey : @"Failed to load URL, blacklisted." }];
|
||||
} else {
|
||||
URLInBlackListAdd(_request.URL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_completion) _completion(image, _request.URL, YYWebImageFromRemote, YYWebImageStageFinished, error);
|
||||
[self _finish];
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSURLConnectionDelegate runs in operation thread
|
||||
|
||||
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection {
|
||||
return _shouldUseCredentialStorage;
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
|
||||
@autoreleasepool {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
if (!(_options & YYWebImageOptionAllowInvalidSSLCertificates) &&
|
||||
[challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) {
|
||||
[challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
|
||||
} else {
|
||||
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
|
||||
}
|
||||
} else {
|
||||
if ([challenge previousFailureCount] == 0) {
|
||||
if (_credential) {
|
||||
[[challenge sender] useCredential:_credential forAuthenticationChallenge:challenge];
|
||||
} else {
|
||||
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
|
||||
}
|
||||
} else {
|
||||
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
|
||||
if (!cachedResponse) return cachedResponse;
|
||||
if (_options & YYWebImageOptionUseNSURLCache) {
|
||||
return cachedResponse;
|
||||
} else {
|
||||
// ignore NSURLCache
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
@autoreleasepool {
|
||||
NSError *error = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (id) response;
|
||||
NSInteger statusCode = httpResponse.statusCode;
|
||||
if (statusCode >= 400 || statusCode == 304) {
|
||||
error = [NSError errorWithDomain:NSURLErrorDomain code:statusCode userInfo:nil];
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
[_connection cancel];
|
||||
[self connection:_connection didFailWithError:error];
|
||||
} else {
|
||||
if (response.expectedContentLength) {
|
||||
_expectedSize = (NSInteger)response.expectedContentLength;
|
||||
if (_expectedSize < 0) _expectedSize = -1;
|
||||
}
|
||||
_data = [NSMutableData dataWithCapacity:_expectedSize > 0 ? _expectedSize : 0];
|
||||
if (_progress) {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) _progress(0, _expectedSize);
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
@autoreleasepool {
|
||||
[_lock lock];
|
||||
BOOL canceled = [self isCancelled];
|
||||
[_lock unlock];
|
||||
if (canceled) return;
|
||||
|
||||
if (data) [_data appendData:data];
|
||||
if (_progress) {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
_progress(_data.length, _expectedSize);
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
/*--------------------------- progressive ----------------------------*/
|
||||
BOOL progressive = (_options & YYWebImageOptionProgressive) > 0;
|
||||
BOOL progressiveBlur = (_options & YYWebImageOptionProgressiveBlur) > 0;
|
||||
if (!_completion || !(progressive || progressiveBlur)) return;
|
||||
if (data.length <= 16) return;
|
||||
if (_expectedSize > 0 && data.length >= _expectedSize * 0.99) return;
|
||||
if (_progressiveIgnored) return;
|
||||
|
||||
NSTimeInterval min = progressiveBlur ? MIN_PROGRESSIVE_BLUR_TIME_INTERVAL : MIN_PROGRESSIVE_TIME_INTERVAL;
|
||||
NSTimeInterval now = CACurrentMediaTime();
|
||||
if (now - _lastProgressiveDecodeTimestamp < min) return;
|
||||
|
||||
if (!_progressiveDecoder) {
|
||||
_progressiveDecoder = [[YYImageDecoder alloc] initWithScale:[UIScreen mainScreen].scale];
|
||||
}
|
||||
[_progressiveDecoder updateData:_data final:NO];
|
||||
if ([self isCancelled]) return;
|
||||
|
||||
if (_progressiveDecoder.type == YYImageTypeUnknown ||
|
||||
_progressiveDecoder.type == YYImageTypeWebP ||
|
||||
_progressiveDecoder.type == YYImageTypeOther) {
|
||||
_progressiveDecoder = nil;
|
||||
_progressiveIgnored = YES;
|
||||
return;
|
||||
}
|
||||
if (progressiveBlur) { // only support progressive JPEG and interlaced PNG
|
||||
if (_progressiveDecoder.type != YYImageTypeJPEG &&
|
||||
_progressiveDecoder.type != YYImageTypePNG) {
|
||||
_progressiveDecoder = nil;
|
||||
_progressiveIgnored = YES;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_progressiveDecoder.frameCount == 0) return;
|
||||
|
||||
if (!progressiveBlur) {
|
||||
YYImageFrame *frame = [_progressiveDecoder frameAtIndex:0 decodeForDisplay:YES];
|
||||
if (frame.image) {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
_completion(frame.image, _request.URL, YYWebImageFromRemote, YYWebImageStageProgress, nil);
|
||||
_lastProgressiveDecodeTimestamp = now;
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (_progressiveDecoder.type == YYImageTypeJPEG) {
|
||||
if (!_progressiveDetected) {
|
||||
NSDictionary *dic = [_progressiveDecoder framePropertiesAtIndex:0];
|
||||
NSDictionary *jpeg = dic[(id)kCGImagePropertyJFIFDictionary];
|
||||
NSNumber *isProg = jpeg[(id)kCGImagePropertyJFIFIsProgressive];
|
||||
if (!isProg.boolValue) {
|
||||
_progressiveIgnored = YES;
|
||||
_progressiveDecoder = nil;
|
||||
return;
|
||||
}
|
||||
_progressiveDetected = YES;
|
||||
}
|
||||
|
||||
NSInteger scanLength = (NSInteger)_data.length - (NSInteger)_progressiveScanedLength - 4;
|
||||
if (scanLength <= 2) return;
|
||||
NSRange scanRange = NSMakeRange(_progressiveScanedLength, scanLength);
|
||||
NSRange markerRange = [_data rangeOfData:JPEGSOSMarker() options:kNilOptions range:scanRange];
|
||||
_progressiveScanedLength = _data.length;
|
||||
if (markerRange.location == NSNotFound) return;
|
||||
if ([self isCancelled]) return;
|
||||
|
||||
} else if (_progressiveDecoder.type == YYImageTypePNG) {
|
||||
if (!_progressiveDetected) {
|
||||
NSDictionary *dic = [_progressiveDecoder framePropertiesAtIndex:0];
|
||||
NSDictionary *png = dic[(id)kCGImagePropertyPNGDictionary];
|
||||
NSNumber *isProg = png[(id)kCGImagePropertyPNGInterlaceType];
|
||||
if (!isProg.boolValue) {
|
||||
_progressiveIgnored = YES;
|
||||
_progressiveDecoder = nil;
|
||||
return;
|
||||
}
|
||||
_progressiveDetected = YES;
|
||||
}
|
||||
}
|
||||
|
||||
YYImageFrame *frame = [_progressiveDecoder frameAtIndex:0 decodeForDisplay:YES];
|
||||
UIImage *image = frame.image;
|
||||
if (!image) return;
|
||||
if ([self isCancelled]) return;
|
||||
|
||||
if (!YYCGImageLastPixelFilled(image.CGImage)) return;
|
||||
_progressiveDisplayCount++;
|
||||
|
||||
CGFloat radius = 32;
|
||||
if (_expectedSize > 0) {
|
||||
radius *= 1.0 / (3 * _data.length / (CGFloat)_expectedSize + 0.6) - 0.25;
|
||||
} else {
|
||||
radius /= (_progressiveDisplayCount);
|
||||
}
|
||||
image = [image yy_imageByBlurRadius:radius tintColor:nil tintMode:0 saturation:1 maskImage:nil];
|
||||
|
||||
if (image) {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
_completion(image, _request.URL, YYWebImageFromRemote, YYWebImageStageProgress, nil);
|
||||
_lastProgressiveDecodeTimestamp = now;
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
@autoreleasepool {
|
||||
[_lock lock];
|
||||
_connection = nil;
|
||||
if (![self isCancelled]) {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async([self.class _imageQueue], ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
|
||||
BOOL shouldDecode = (self.options & YYWebImageOptionIgnoreImageDecoding) == 0;
|
||||
BOOL allowAnimation = (self.options & YYWebImageOptionIgnoreAnimatedImage) == 0;
|
||||
UIImage *image;
|
||||
BOOL hasAnimation = NO;
|
||||
if (allowAnimation) {
|
||||
image = [[YYImage alloc] initWithData:self.data scale:[UIScreen mainScreen].scale];
|
||||
if (shouldDecode) image = [image yy_imageByDecoded];
|
||||
if ([((YYImage *)image) animatedImageFrameCount] > 1) {
|
||||
hasAnimation = YES;
|
||||
}
|
||||
} else {
|
||||
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:self.data scale:[UIScreen mainScreen].scale];
|
||||
image = [decoder frameAtIndex:0 decodeForDisplay:shouldDecode].image;
|
||||
}
|
||||
|
||||
/*
|
||||
If the image has animation, save the original image data to disk cache.
|
||||
If the image is not PNG or JPEG, re-encode the image to PNG or JPEG for
|
||||
better decoding performance.
|
||||
*/
|
||||
YYImageType imageType = YYImageDetectType((__bridge CFDataRef)self.data);
|
||||
switch (imageType) {
|
||||
case YYImageTypeJPEG:
|
||||
case YYImageTypeGIF:
|
||||
case YYImageTypePNG:
|
||||
case YYImageTypeWebP: { // save to disk cache
|
||||
if (!hasAnimation) {
|
||||
if (imageType == YYImageTypeGIF ||
|
||||
imageType == YYImageTypeWebP) {
|
||||
self.data = nil; // clear the data, re-encode for disk cache
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
self.data = nil; // clear the data, re-encode for disk cache
|
||||
} break;
|
||||
}
|
||||
if ([self isCancelled]) return;
|
||||
|
||||
if (self.transform && image) {
|
||||
UIImage *newImage = self.transform(image, self.request.URL);
|
||||
if (newImage != image) {
|
||||
self.data = nil;
|
||||
}
|
||||
image = newImage;
|
||||
if ([self isCancelled]) return;
|
||||
}
|
||||
|
||||
[self performSelector:@selector(_didReceiveImageFromWeb:) onThread:[self.class _networkThread] withObject:image waitUntilDone:NO];
|
||||
});
|
||||
if (![self.request.URL isFileURL] && (self.options & YYWebImageOptionShowNetworkActivity)) {
|
||||
[YYWebImageManager decrementNetworkActivityCount];
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
@autoreleasepool {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
if (_completion) {
|
||||
_completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageFinished, error);
|
||||
}
|
||||
_connection = nil;
|
||||
_data = nil;
|
||||
if (![_request.URL isFileURL] && (_options & YYWebImageOptionShowNetworkActivity)) {
|
||||
[YYWebImageManager decrementNetworkActivityCount];
|
||||
}
|
||||
[self _finish];
|
||||
|
||||
if (_options & YYWebImageOptionIgnoreFailedURL) {
|
||||
if (error.code != NSURLErrorNotConnectedToInternet &&
|
||||
error.code != NSURLErrorCancelled &&
|
||||
error.code != NSURLErrorTimedOut &&
|
||||
error.code != NSURLErrorUserCancelledAuthentication) {
|
||||
URLInBlackListAdd(_request.URL);
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Override NSOperation
|
||||
|
||||
- (void)start {
|
||||
@autoreleasepool {
|
||||
[_lock lock];
|
||||
self.started = YES;
|
||||
if ([self isCancelled]) {
|
||||
[self performSelector:@selector(_cancelOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
|
||||
self.finished = YES;
|
||||
} else if ([self isReady] && ![self isFinished] && ![self isExecuting]) {
|
||||
if (!_request) {
|
||||
self.finished = YES;
|
||||
if (_completion) {
|
||||
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey:@"request in nil"}];
|
||||
_completion(nil, _request.URL, YYWebImageFromNone, YYWebImageStageFinished, error);
|
||||
}
|
||||
} else {
|
||||
self.executing = YES;
|
||||
[self performSelector:@selector(_startOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
|
||||
if ((_options & YYWebImageOptionAllowBackgroundTask) && _YYSharedApplication()) {
|
||||
__weak __typeof__ (self) _self = self;
|
||||
if (_taskID == UIBackgroundTaskInvalid) {
|
||||
_taskID = [_YYSharedApplication() beginBackgroundTaskWithExpirationHandler:^{
|
||||
__strong __typeof (_self) self = _self;
|
||||
if (self) {
|
||||
[self cancel];
|
||||
self.finished = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[_lock lock];
|
||||
if (![self isCancelled]) {
|
||||
[super cancel];
|
||||
self.cancelled = YES;
|
||||
if ([self isExecuting]) {
|
||||
self.executing = NO;
|
||||
[self performSelector:@selector(_cancelOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
|
||||
}
|
||||
if (self.started) {
|
||||
self.finished = YES;
|
||||
}
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (void)setExecuting:(BOOL)executing {
|
||||
[_lock lock];
|
||||
if (_executing != executing) {
|
||||
[self willChangeValueForKey:@"isExecuting"];
|
||||
_executing = executing;
|
||||
[self didChangeValueForKey:@"isExecuting"];
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (BOOL)isExecuting {
|
||||
[_lock lock];
|
||||
BOOL executing = _executing;
|
||||
[_lock unlock];
|
||||
return executing;
|
||||
}
|
||||
|
||||
- (void)setFinished:(BOOL)finished {
|
||||
[_lock lock];
|
||||
if (_finished != finished) {
|
||||
[self willChangeValueForKey:@"isFinished"];
|
||||
_finished = finished;
|
||||
[self didChangeValueForKey:@"isFinished"];
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (BOOL)isFinished {
|
||||
[_lock lock];
|
||||
BOOL finished = _finished;
|
||||
[_lock unlock];
|
||||
return finished;
|
||||
}
|
||||
|
||||
- (void)setCancelled:(BOOL)cancelled {
|
||||
[_lock lock];
|
||||
if (_cancelled != cancelled) {
|
||||
[self willChangeValueForKey:@"isCancelled"];
|
||||
_cancelled = cancelled;
|
||||
[self didChangeValueForKey:@"isCancelled"];
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (BOOL)isCancelled {
|
||||
[_lock lock];
|
||||
BOOL cancelled = _cancelled;
|
||||
[_lock unlock];
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
- (BOOL)isConcurrent {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isAsynchronous {
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
|
||||
if ([key isEqualToString:@"isExecuting"] ||
|
||||
[key isEqualToString:@"isFinished"] ||
|
||||
[key isEqualToString:@"isCancelled"]) {
|
||||
return NO;
|
||||
}
|
||||
return [super automaticallyNotifiesObserversForKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *string = [NSMutableString stringWithFormat:@"<%@: %p ",self.class, self];
|
||||
[string appendFormat:@" executing:%@", [self isExecuting] ? @"YES" : @"NO"];
|
||||
[string appendFormat:@" finished:%@", [self isFinished] ? @"YES" : @"NO"];
|
||||
[string appendFormat:@" cancelled:%@", [self isCancelled] ? @"YES" : @"NO"];
|
||||
[string appendString:@">"];
|
||||
return string;
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user