文档章节

iOS_获取网络图片的尺寸Size的最优方法(附:宏定义单例)

xiaobai1315
 xiaobai1315
发布于 2017/07/13 16:20
字数 1652
阅读 30
收藏 0
点赞 0
评论 0

        在iOS开发过程中经常需要通过网络请求加载图片,有时,需要在创建UIImageView或UIButton来显示图片之前需要提前知道图片的尺寸,根据图片尺寸创建对应大小的控件。但是对于网络图片来说,要想通过最优的方法获得尺寸就略微有点困难,大体思路有这么几种:

        1.通过服务器处理。即在下行图片路径时拼接该图片的宽高。这种方法最简单,避免了不必要的网络请求,只需要从URL中截取即可(需要前后台的配合);

        2.网络请求。如果有使用SDWebImage,则首先检查是否缓存过该图片,如果没有,先通过文件头获取图片大小(针对格式为png、gif、jpg文件获取其尺寸大小),如果获取失败,则下载完整的图片data,然后计算大小,如果有使用SDWebImage,则使用SDWebImage缓存该图片。

      ​(方法二的最大的缺点就是虽然获取头文件耗时比下载图片快很多,但是还是有延迟,会阻塞主线程UI的绘制,如果网速不好的话会影响用户体验;但是如果使用了SDWebImage缓存,只会在初次加载图片的时候卡顿而已。)

代码如下:​

//一个项目里面可能有好几个类都需要实现单例模式。为了更高效的编码,可以利用c语言中宏定义来实现。

//首先宏定义一个单例实现(在.pch/Header文件中拷贝如下代码加入即可)

//这里假设了实例的分享方法叫 shared"className"

//因为方法名 shared"className"是连在一起的,为了让宏能够正确替换掉签名中的“className”需要在前面加上 ##

//当宏的定义超过一行时,在末尾加上“\”表示下一行也在宏定义范围内。

//注意最后一行不需要加"\”。

// @interface

#define singleton_interface(className) \

+ (className *)shared##className;

// @implementation

#define singleton_implementation(className) \

static className *_instance; \

+ (id)allocWithZone:(NSZone *)zone \

{ \

static dispatch_once_t onceToken; \

dispatch_once(&onceToken, ^{ \

_instance = [super allocWithZone:zone]; \

}); \

return _instance; \

} \

+ (className *)shared##className \

{ \

static dispatch_once_t onceToken; \

dispatch_once(&onceToken, ^{ \

_instance = [[self alloc] init]; \

}); \

return _instance; \

}

********************************************************************

然后头文件如下

#import 《Foundation/Foundation.h》//特殊字符限制,将书名号改为尖括号

#import "Singleton.h"

@interface BLImageSize : NSObject

singleton_interface(BLImageSize)      //公共的访问单例对象的方法

+(CGSize)downloadImageSizeWithURL:(id)imageURL;

@end

*************************************************************************

最后是.m文件实现

#import "BLImageSize.h"

#import "SDImageCache.h"//如果未使用SDWebImage,则忽略

@implementation BLImageSize

singleton_implementation(BLImageSize)

//传入的参数imageURL可以为NSString类型,也可以为NSURL​类型

+(CGSize)getImageSizeWithURL:(id)imageURL

{

    NSURL* URL = nil;

    if([imageURL isKindOfClass:[NSURL class]]){

        URL = imageURL;

    }

    if([imageURL isKindOfClass:[NSString class]]){

        URL = [NSURL URLWithString:imageURL];

    }​

    if(URL == nil) {

        return CGSizeZero;    // url为空则返回CGSizeZero

}​

    

    NSString* absoluteString = URL.absoluteString;

​

    //如果未使用SDWebImage,则忽略;检查是否缓存过该图片

#ifdef dispatch_main_sync_safe

    if([[SDImageCache sharedImageCache] diskImageExistsWithKey:absoluteString])

    {

        UIImage* image = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:absoluteString];

        if(!image)

        {

            NSData* data = [[SDImageCache sharedImageCache] performSelector:@selector(diskImageDataBySearchingAllPathsForKey:) withObject:URL.absoluteString];

            image = [UIImage imageWithData:data];

        }

        if(image)

        {

            return image.size;

        }

    }

#endif

    

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];

    NSString* pathExtendsion = [URL.pathExtension lowercaseString];

    

    CGSize size = CGSizeZero;

    if([pathExtendsion isEqualToString:@"png"]){

        size =  [self getPNGImageSizeWithRequest:request];

    }

    else if([pathExtendsion isEqual:@"gif"])

    {

        size =  [self getGIFImageSizeWithRequest:request];

    }

    else{

        size = [self getJPGImageSizeWithRequest:request];

    }

    if(CGSizeEqualToSize(CGSizeZero, size))

    {

        // 如果获取文件头信息失败,发送异步请求请求原图

        NSData* data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:nil error:nil];

        UIImage* image = [UIImage imageWithData:data];

        if(image)

        {

//如果未使用SDWebImage,则忽略;缓存该图片

#ifdef dispatch_main_sync_safe

            [[SDImageCache sharedImageCache] storeImage:image recalculateFromImage:YES imageData:data forKey:URL.absoluteString toDisk:YES];

#endif

            size = image.size;

        }

    }



//如果对加载速度及用户体验要求不高的话,可以通过主线程获取图片大小

//会阻塞主线程,慎重使用!!!

if (CGSizeEqualToSize(CGSizeZero, size)) {

        //直接获取图片大小

        NSData *data = [NSData dataWithContentsOfURL:URL];

        UIImage *image = [UIImage imageWithData:data];

        size = image.size;

}

​

    return size;

}

​

​//  获取PNG图片的大小

+(CGSize)getPNGImageSizeWithRequest:(NSMutableURLRequest*)request

{

    [request setValue:@"bytes=16-23" forHTTPHeaderField:@"Range"];

    NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    if(data.length == 8)

    {

        int w1 = 0, w2 = 0, w3 = 0, w4 = 0;

        [data getBytes:&w1 range:NSMakeRange(0, 1)];

        [data getBytes:&w2 range:NSMakeRange(1, 1)];

        [data getBytes:&w3 range:NSMakeRange(2, 1)];

        [data getBytes:&w4 range:NSMakeRange(3, 1)];

        int w = (w1 << 24) + (w2 << 16) + (w3 << 8) + w4;

        int h1 = 0, h2 = 0, h3 = 0, h4 = 0;

        [data getBytes:&h1 range:NSMakeRange(4, 1)];

        [data getBytes:&h2 range:NSMakeRange(5, 1)];

        [data getBytes:&h3 range:NSMakeRange(6, 1)];

        [data getBytes:&h4 range:NSMakeRange(7, 1)];

        int h = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;

        return CGSizeMake(w, h);

    }

    return CGSizeZero;

}

​//  获取GIF图片的大小

+(CGSize)getGIFImageSizeWithRequest:(NSMutableURLRequest*)request

{

    [request setValue:@"bytes=6-9" forHTTPHeaderField:@"Range"];

    NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    if(data.length == 4)

    {

        short w1 = 0, w2 = 0;

        [data getBytes:&w1 range:NSMakeRange(0, 1)];

        [data getBytes:&w2 range:NSMakeRange(1, 1)];

        short w = w1 + (w2 << 8);

        short h1 = 0, h2 = 0;

        [data getBytes:&h1 range:NSMakeRange(2, 1)];

        [data getBytes:&h2 range:NSMakeRange(3, 1)];

        short h = h1 + (h2 << 8);

        return CGSizeMake(w, h);

    }

    return CGSizeZero;

}

​//  获取JPG图片的大小

+(CGSize)getJPGImageSizeWithRequest:(NSMutableURLRequest*)request

{

    [request setValue:@"bytes=0-209" forHTTPHeaderField:@"Range"];

    NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    

    if ([data length] <= 0x58) {

        return CGSizeZero;

    }

    

    if ([data length] < 210) {// 肯定只有一个DQT字段

        short w1 = 0, w2 = 0;

        [data getBytes:&w1 range:NSMakeRange(0x60, 0x1)];

        [data getBytes:&w2 range:NSMakeRange(0x61, 0x1)];

        short w = (w1 << 8) + w2;

        short h1 = 0, h2 = 0;

        [data getBytes:&h1 range:NSMakeRange(0x5e, 0x1)];

        [data getBytes:&h2 range:NSMakeRange(0x5f, 0x1)];

        short h = (h1 << 8) + h2;

        return CGSizeMake(w, h);

    } else {

        short word = 0x0;

        [data getBytes:&word range:NSMakeRange(0x15, 0x1)];

        if (word == 0xdb) {

            [data getBytes:&word range:NSMakeRange(0x5a, 0x1)];

            if (word == 0xdb) {// 两个DQT字段

                short w1 = 0, w2 = 0;

                [data getBytes:&w1 range:NSMakeRange(0xa5, 0x1)];

                [data getBytes:&w2 range:NSMakeRange(0xa6, 0x1)];

                short w = (w1 << 8) + w2;

                short h1 = 0, h2 = 0;

                [data getBytes:&h1 range:NSMakeRange(0xa3, 0x1)];

                [data getBytes:&h2 range:NSMakeRange(0xa4, 0x1)];

                short h = (h1 << 8) + h2;

                return CGSizeMake(w, h);

            } else {// 一个DQT字段

                short w1 = 0, w2 = 0;

                [data getBytes:&w1 range:NSMakeRange(0x60, 0x1)];

                [data getBytes:&w2 range:NSMakeRange(0x61, 0x1)];

                short w = (w1 << 8) + w2;

                short h1 = 0, h2 = 0;

                [data getBytes:&h1 range:NSMakeRange(0x5e, 0x1)];

                [data getBytes:&h2 range:NSMakeRange(0x5f, 0x1)];

                short h = (h1 << 8) + h2;

                return CGSizeMake(w, h);

            }

        } else {

            return CGSizeZero;

        }

    }

}

@end

 

PS:

png和gif格式的图片头文件固定,可以准确找到宽高所在字节,只需要极少流量就可以获得图片大小。但是jpeg的头文件很混乱,我真的想说它根本就不分header和body,绘图软件编辑一次保存一次就添加一个标记码,然后存放宽高的标记码就消失在数据流的某个地方了,因为不知道有多少个绘图软件编辑过,也不知道绘图软件标记码里面的内容格式,所以手动获取jpeg图片的宽高基本不可能了​。

不过博主偶然发现了一个方法,大家可以尝试一下:

就是在图片url地址后面拼接参数@100p,利用获取JPG格式图片的方法即可获得JPEG图片的大小了。​

if (![imgUrl containsString:@"@"]) {

            imgUrl = [imgUrl stringByAppendingString:@"@100p"];

}

参考:

http://bbs.itheima.com/thread-158741-1-1.html

http://www.oschina.net/code/snippet_2248391_53038

http://www.2cto.com/kf/201405/304877.html

http://www.cocoachina.com/bbs/read.php?tid=455783

© 著作权归作者所有

共有 人打赏支持
xiaobai1315
粉丝 3
博文 187
码字总数 78251
作品 0
程序员
iOS源码补完计划--AFNetworking 3.1.0源码研读

参拜一下AFNetworking的源码。 第四篇源码、暂时来看也是iOS方向的最后一篇、撸完准备趁着热乎撸一撸网络协议。 目录 准备工作 功能模块 AFURLSessionManager/AFHTTPSessionManager AFNetwo...

kirito_song ⋅ 05/25 ⋅ 0

iOS基础深入补完计划--NSURLSession使用详解(附Demo)

目录 前言 API Demo 前言 本文主要是把NSURLSession以及NSURLSessionTask相关的代理方法全部整理了一下。 旨在大体了解在一个iOS网络请求中、一个任务究竟经理了什么。 而我们、又能做些什么...

kirito_song ⋅ 05/10 ⋅ 0

Unity与IOS交互,调用IOS系统相机和相册

前面两篇总结了一下unity与android的简单交互和调用安卓系统相机和相册,比较蛋疼的是,后来发现不同的测试机上会有不同的bug。。。下阶段要一个一个的解决一下 今天总结一下与IOS的交互。这...

qq_32587659 ⋅ 05/16 ⋅ 0

从Xamarin.Essentials谈Xamarin库的封装

编者语:Xamarin在国内的推广还需要努力,其实这真的是移动端开发的一大福音,毕竟用一份代码的时间可以生成iOS/Android/Windows/Linux/macOS/Tizen多个平台,而且是原生的性能。Xamarin在B...

⋅ 05/10 ⋅ 0

iOS高仿微信、仪表盘、图片标注图片滤镜、高斯模糊、上拉加载、下拉刷新等源码

iOS精选源码 Swift-图片画框标注(http://www.code4app.com/thread-29884-1-1.html) Swift版的上拉加载, 下拉刷新控件(一句话集成, 超级易用)(http://www.code4app.com/thread-29885-1-1.html...

sunnyaigd ⋅ 05/22 ⋅ 0

iOS基于FreeStreamer的仿网易云音乐播放器

前言 前段时间写过一个基于VLCKit实现的网易云音乐播放demo-GKAudioPlayerDemo,以及3篇文章 iOS-VLCKit实现仿网易云音乐播放音乐(一) iOS-VLCKit实现仿网易云音乐播放音乐(二) iOS-VLCK...

QuintGao ⋅ 05/11 ⋅ 0

UISegmentedControl segmentedControl

一、简介 <UIControl-->UIView-->UIResponder-->NSObject 格式为 1-->初始化(作用) typedef NS_OPTIONS(NSUInteger, UIDataDetectorTypes) { UIDataDetector......

无邪8 ⋅ 05/22 ⋅ 0

iOS UIWebView 使用大全

一、UIWebView的基础使用 以上是IOS中UIWebView的基础使用要点详解,接下来一些UIWebView的常用注意点。 二、IOS中UIWebView常用注意点: 1、与UIWebView进行交互,调用web页面中的需要传参的...

朝雨晚风 ⋅ 2016/08/15 ⋅ 0

iOS逆向工程- 学习整理(工具详解)

前言 一、逆向工程的要求 具备丰富的 iOS 开发经验 最好能非常熟悉 iOS 设备的硬件构成,iOS 系统的运行原理。 拿到任意一个 App 之后能够大致推断出它的项目规模和使用的技术,比如它的MVC模...

_小迷糊 ⋅ 05/11 ⋅ 0

常见的移动端H5页面开发遇到的坑和解决办法

手机共通问题 问题一:用同等比例的图片在PC机上很清楚,但是手机上很模糊,原因是什么呢? 经研究发现是devicePixelRatio作怪,因为手机分辨率太小,如果按照分辨率来显示网页字会非常小,所以苹果...

webmirror ⋅ 05/02 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

解决CentOS6、7,/etc/sysconfig/下没有iptables的问题

一、Centos 6版本解决办法: 1.任意运行一条iptables防火墙规则配置命令: iptables -P OUTPUT ACCEPT 2.对iptables服务进行保存: service iptables save 3.重启iptables服务: service ...

寰宇01 ⋅ 24分钟前 ⋅ 2

数据库备份和恢复

备份:mysqldump -u root -p 数据库>磁盘路径 恢复:mysql -u root -p 数据库<sql脚本的磁盘路径

anlve ⋅ 今天 ⋅ 0

发生了什么?Linus 又发怒了?

在一个 Linux 内核 4.18-rc1 的 Pull Request 中,开发者 Andy Shevchenko 表示其在对设备属性框架进行更新时,移除了 union 别名,这引发了 Linus 的暴怒。 这一次 Linus Torvalds 发怒的原...

问题终结者 ⋅ 今天 ⋅ 0

在树莓派上搭建一个maven仓库

在树莓派上搭建一个maven仓库 20180618 lambo init 项目说明 家里有台树莓派性能太慢。想搭建一个maven私服, 使用nexus或者 jfrog-artifactory 运行的够呛。怎么办呢,手写一个吧.所在这个...

林小宝 ⋅ 今天 ⋅ 0

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 今天 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 今天 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部