美文网首页Demo集合从五开始——iOS图片处理探索之路
项目3:制作图片下载、缓存、图像处理的iOS静态库

项目3:制作图片下载、缓存、图像处理的iOS静态库

作者: 肠粉白粥_Hoben | 来源:发表于2019-04-29 17:45 被阅读1次

    一. 项目需求

    二. 项目架构

    • HobenImageManager提供图片下载、处理接口

    • HobenImageCache用于下载与缓存

    • HobenImageProcessManager用于图片处理

    三. 图片下载、处理接口管理

    1. 下载、处理图片

    对于暴露给外界的接口,根据图片的URL地址来向图片下载缓存管理请求返回image和下载进度,获得image后再向图片处理管理请求处理图片,请求完毕后根据回调返回结果给调用接口的方法。

    /**
    *
     根据处理类型下载图片
     
     @param url             图片的URL地址
     @param processType     图片处理类型
     @param progressBlock   包含CGFloat类型的下载进度回调
     @param completedBlock  包含UIImage类型的下载完成回调
    *
    **/
    - (void)requestImageWithUrl:(NSString *)url
                    processType:(HobenImageProcessType)processType
                  progressBlock:(HobenImageProgressBlock)progressBlock
                 completedBlock:(HobenImageCompletedBlock)completedBlock {
        [[HobenImageCache sharedInstance] requestImageWithUrl:url progressBlock:progressBlock completedBlock:^(UIImage * _Nullable image) {
            [[HobenImageProcessManager sharedInstance] processImage:image processType:processType completedBlock:completedBlock];
        }];
    }
    

    2. 清除缓存

    清除缓存主要向图片下载缓存管理请求清除缓存,清除缓存完成后会返回一个完成的回调。

    /**
     *
     清除图片缓存
     
     @param completionBlock  清除缓存成功后的回调
     *
     **/
    - (void)removeCacheWithCompletionBlock:(void (^)(void))completionBlock {
        [[HobenImageCache sharedInstance] removeCacheWithCompletionBlock:completionBlock];
    }
    

    四. 图片下载、缓存管理

    1. 图片的下载和缓存

    流程图如下,为确保图片不被重复下载,该模块使用单例模式,且保存了正在请求图片的URL,如果存在缓存,则直接回调,如果不存在,则调用下载方法,下载完成后缓存该图片,并及时回调图片和进度。

    流程图

    图片的缓存和下载主要使用了第三方库SDWebImage

    1) 读取缓存图片

    以URL为key,直接返回SDImageCache存储的图片。

    - (UIImage *)_imageWithUrl:(NSString *)url {
        return [[SDImageCache sharedImageCache] imageFromCacheForKey:url];
    }
    
    2) 下载图片和回调进度

    调用SDWebImageloadImageWithURL:options:progress:completed:方法,加载完成后,第三方库会自动将该图片缓存起来。

    - (void)_requestImageWithUrl:(NSString *)url
                   progressBlock:(HobenImageProgressBlock)progreeBlock
                  completedBlock:(HobenImageCompletedBlock)completedBlock {
        WEAK_SELF_DECLARED
        [[SDWebImageManager sharedManager] loadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
            CGFloat progress = receivedSize * 1.f / expectedSize;
            if (progreeBlock) {
                progreeBlock(progress);
            }
        } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
            STRONG_SELF_BEGIN
            if (image) {
                [strongSelf _cacheImageWithUrl:url];
                [strongSelf.requestList removeObject:url];
                if (completedBlock) {
                    completedBlock(image);
                }
            } else {
                NSLog(@"Cache ERROR in URL:%@", url);
                [strongSelf.requestList removeObject:url];
                if (completedBlock) {
                    completedBlock(nil);
                }
            }
            STRONG_SELF_END
        }];
    }
    

    2. 清除图片缓存

    调用SDImageCache的相应方法以清除缓存。

    - (void)removeCacheWithCompletionBlock:(void (^)(void))completionBlock {
        [self.requestList removeAllObjects];
        [[SDImageCache sharedImageCache] clearMemory];
        [[SDImageCache sharedImageCache] clearDiskOnCompletion:completionBlock];
    }
    

    五. 图像处理

    图像处理提供的方法主要有以下三种:

    typedef NS_ENUM(NSUInteger, HobenImageProcessType) {
        HobenImageProcessTypeCommon = 0,        // 不处理
        HobenImageProcessTypeGaussian,          // 高斯模糊
        HobenImageProcessTypeWatermark,         // 水印
    };
    

    1. 高斯模糊处理

    iOS绘图系列:图像模糊之均值模糊详细描述了相关原理,在此不赘述。

    - (void)_processGaussianImage:(UIImage *)image completedBlock:(void (^)(UIImage * _Nullable image))completeBlock {
        CGFloat blur = 0.5f;
        int boxSize = (int)(blur * 40);
        
        // 确保为奇数
        boxSize = boxSize - (boxSize % 2) + 1;
        
        CGImageRef imageRef = image.CGImage;
        vImage_Buffer inBuffer, outBuffer;
        vImage_Error error;
        void *pixelBuffer;
        
        // 从CGImage中获取数据
        CGDataProviderRef inProvider = CGImageGetDataProvider(imageRef);
        CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
        
        // 设置从CGImage获取对象的属性
        pixelBuffer = malloc(CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef));
        
        outBuffer.width = inBuffer.width = CGImageGetWidth(imageRef);
        outBuffer.height = inBuffer.height = CGImageGetHeight(imageRef);
        outBuffer.rowBytes = inBuffer.rowBytes = CGImageGetBytesPerRow(imageRef);
        inBuffer.data = (void *)CFDataGetBytePtr(inBitmapData);
        outBuffer.data = pixelBuffer;
        
        // 均值模糊
        error = vImageBoxConvolve_ARGB8888(&inBuffer,
                                           &outBuffer,
                                           NULL,
                                           0,
                                           0,
                                           boxSize,
                                           boxSize,
                                           NULL,
                                           kvImageEdgeExtend);
        
        if (error) {
            NSLog(@"Error from convolution %ld", error);
        }
        
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = CGBitmapContextCreate(outBuffer.data, outBuffer.width, outBuffer.height, 8, outBuffer.rowBytes, colorSpace, CGImageGetBitmapInfo(imageRef));
        CGImageRef resultImageRef = CGBitmapContextCreateImage(context);
        UIImage *resultImage = [UIImage imageWithCGImage:resultImageRef];
        
        CGColorSpaceRelease(colorSpace);
        free(pixelBuffer);
        CFRelease(inBitmapData);
        CGColorSpaceRelease(colorSpace);
        CGImageRelease(resultImageRef);
        
        if (completeBlock) {
            completeBlock(resultImage);
        }
    }
    

    2. 水印处理

    iOS绘图系列:Core Graphics绘图提到了相关原理,在此不赘述。

    - (void)_processWatermarkImage:(UIImage *)image text:(NSString *)text completedBlock:(HobenImageCompletedBlock)completeBlock {
        UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
        [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
        
        NSDictionary *dictionary = @{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont boldSystemFontOfSize:image.size.height / 10]};
        [text drawAtPoint:CGPointZero withAttributes:dictionary];
        
        UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
        
        UIGraphicsEndImageContext();
        
        if (completeBlock) {
            completeBlock(resultImage);
        }
    }
    

    六. 导出Framework

    之前学习过如何封装Framework,主要的步骤如下:

    1. 选择暴露的头文件
    2. 分别在真机环境和模拟器环境下导出Framework
    3. 通过lipo -create方法将两个Framework,合并成一个Framework后output出来
    4. 将得到的Framework导入到需要用的文件中

    这次使用shell脚本来导出Framework,步骤如注释所示:

    #工作路径
    WORKSPACE="/Users/xxx/Desktop/HobenImageLib/HobenImageLib.xcworkspace"
    CONFIG="Release"
    USER_HOME=$(eval echo ~${SUDO_USER})
    #project名字
    FMK_NAME=${PROJECT_NAME}
    
    OUTPUT_DIR=${USER_HOME}/Desktop/Framework_build_output
    INSTALL_DIR=${OUTPUT_DIR}/${FMK_NAME}.framework
    DEVICE_DIR=${BUILD_DIR}/${CONFIG}-iphoneos/${FMK_NAME}.framework
    TEMP_DEVICE_DIR=${OUTPUT_DIR}/TEMP_DEIVCE_DIR/${FMK_NAME}.framework
    SIMULATOR_DIR=${BUILD_DIR}/${CONFIG}-iphonesimulator/${FMK_NAME}.framework 
    
    if [ -d "${INSTALL_DIR}" ]
    then
    rm -rf "${INSTALL_DIR}"
    fi
    
    #XCode导出真机Framework,注意要先clean再build
    xcodebuild -workspace "${WORKSPACE}" -configuration "Release" -scheme "${FMK_NAME}" -sdk iphoneos clean build OBJROOT="${OBJROOT}/DependentBuilds"
    
    mkdir -p "${TEMP_DEVICE_DIR}"
    cp -R "${DEVICE_DIR}/" "${TEMP_DEVICE_DIR}/"
    
    #XCode导出模拟器Framework,注意要先clean再build
    xcodebuild -workspace "${WORKSPACE}" -configuration "Release" -scheme "${FMK_NAME}" -sdk iphonesimulator clean build OBJROOT="${OBJROOT}/DependentBuilds"
    
    mkdir -p "${INSTALL_DIR}"
    cp -R "${TEMP_DEVICE_DIR}/" "${INSTALL_DIR}/"
    
    #将两个Framework合并且导出
    lipo -create "${TEMP_DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
    
    rm -rf "${TEMP_DEVICE_DIR}"
    
    # 保存静态库打包的版本信息
    
    hash=$(git log -1 --pretty=format:"%h")
    date=$(git log -1 --pretty=format:"%ad" --date=format:"%Y-%m-%d %H:%M:%S")
    branch=$(git symbolic-ref --short -q HEAD)
    
    info_plist="${INSTALL_DIR}/Info.plist" 
    
    open "${INSTALL_DIR}/../"
    

    七. 导入Framework

    在Demo中导入Framework并引用头文件,使用Framework里面暴露的头文件方法:

    #import <HobenImageLib/HobenImageLib.h>
    
    ...
    
    WEAK_SELF_DECLARED
    void (^progressBlock)(CGFloat progress) = ^(CGFloat progress) {
        dispatch_async(dispatch_get_main_queue(), ^{
            STRONG_SELF_BEGIN
            NSLog(@"progress: %lf", progress);
            [strongSelf setProgressLabelValue:progress];
            strongSelf.progressView.progress = progress;
            STRONG_SELF_END
        });
    };
    
    void (^completedBlock)(UIImage * _Nullable image) = ^(UIImage * _Nullable image) {
        STRONG_SELF_BEGIN
        PreviewController *vc = [[PreviewController alloc] initWithImage:image];
        [strongSelf.navigationController pushViewController:vc animated:YES];
        STRONG_SELF_END
    };
    
    [[HobenImageManager sharedInstance] requestImageWithUrl:url
                                                processType:self.processType
                                              progressBlock:progressBlock
                                             completedBlock:completedBlock];
    

    八. 成果展示

    为了使得加载进度效果显示出来,在模拟器模拟了弱网效果。

    成果展示如下:

    九. Demo

    我的Demo在这里,欢迎star~

    相关文章

      网友评论

        本文标题:项目3:制作图片下载、缓存、图像处理的iOS静态库

        本文链接:https://www.haomeiwen.com/subject/lsfunqtx.html