文章目录
  1. 1. 前言
  2. 2. 那么问题来了
  3. 3. 前提
  4. 4. 照片哪里来
  5. 5. 动手吧!
  6. 6. 结语
  7. 7. 补充
  8. 8. PhotoKit补充资料

前言

为了提供更好的体验,iOS上的App通常都会在条件允许的情况下尽可能多地获取用户信息(说好的隐私呢?!)。比如说,在用户选取了相册中的某张图片后,自动补充图片的拍摄日期、时间、地点等等,给用户一个非常智能的感觉。当然,前提是用户给予了我们这样做的权限。

那么问题来了

如何获得这些信息呢?

稍微查一下可以知道,有一种叫做EXIF(Exchangeable image file format)的东西可以用来在图片中保存一定的数据(像素、ISO等)。这种标准通常应用在数码相机上,在用户照相同时,默默把当前的日期和时间给记录下来。而如今的智能手机在这个基础上还加入了地理位置等信息,包装成了一大个MetaData,信息数据应有尽有,可谓是给各位开发者提供了大大的方便啊。

可是,照片上的这些信息我们要怎样读取出来呢?

前提

首先你的这张照片一定要存有这些信息才能被读取出来吧?

喂!这真的不是废话!

虽然现在的手机已经非常智能了,但是可以照相的应用却是五花八门,并不是每一位开发者都会考虑那么多,有相当一部分应用不管这部分数据的“死活”,它们想要的只是那一张图片而已。

所以,手机相册中的照片也不是百分百的带有我们想要的数据。有可能数据是完整的,但更多情况下,数据是残缺的,甚至完全没有这些数据。碰上这种情况,而你又迫切需要这些信息,那就只好求我们的用户大发慈悲地给我输入一下了。

特殊情况,特殊处理,这里不处理…
就先把我们的用户当成一个非常有耐心、对手机非常信任、照相时网络状况良好的上帝吧:)

照片哪里来

对于直接从照相机获取的照片来说,因为过程太短,所以并不能指望这张照片上面有多少我们想要的信息。如果条件允许的话,我们可以自己在后台采集(记录时间、定位等)。

从相册里面拿出来的照片就好多了。因为照片有一个保存的过程,耗时相对比较长,该采集的信息也采集得差不多了,没采集的我们也没有什么办法,所以就在这些已有的数据上下功夫就好了。

题外话:在用UIImagePickerController的时候,不小心遇到了一个问题,虽然和这篇文章的主题没有太大关系,不过我还是在完整代码里把解决方案写了下来。

动手吧!

用UIImagePickerController来获取相册照片的过程就不多说了,我们主要来看它的一个代理方法-[imagePickerController:didFinishPickingMediaWithInfo:]

这个代理方法会给我们一个字典info,如果用户选择的是一张图片的话,这个info里面除了有这张图片以外,还有图片的URL等信息。
这张图片对我们接下来要做的事情作用不大,要关注的是这个URL。

在继续之前,我们还要加入这两个库:(下面的代码其实不需要ImageIO库,看完整代码就知道怎么回事了)

1
2
3
 
#import <AssetsLibrary/AssetsLibrary.h>
#import <ImageIO/ImageIO.h>

在UIImagePickerController.h里面,对这个info字典的key给出了定义:

1
2
3
4
5
6
7
8
// info dictionary keys
UIKIT_EXTERN NSString *const UIImagePickerControllerMediaType; // an NSString (UTI, i.e. kUTTypeImage)
UIKIT_EXTERN NSString *const UIImagePickerControllerOriginalImage; // a UIImage
UIKIT_EXTERN NSString *const UIImagePickerControllerEditedImage; // a UIImage
UIKIT_EXTERN NSString *const UIImagePickerControllerCropRect; // an NSValue (CGRect)
UIKIT_EXTERN NSString *const UIImagePickerControllerMediaURL; // an NSURL
UIKIT_EXTERN NSString *const UIImagePickerControllerReferenceURL NS_AVAILABLE_IOS(4_1); // an NSURL that references an asset in the AssetsLibrary framework
UIKIT_EXTERN NSString *const UIImagePickerControllerMediaMetadata NS_AVAILABLE_IOS(4_1); // an NSDictionary containing metadata from a captured photo

而我们需要的只有UIImagePickerControllerReferenceURL对应的值。

因为UIKit都已经帮我们封装好了,所以真正要做的只剩下很少一部分。
起作用的代码就只有下面这几行:

1
2
3
4
5
6
7
8
9
10
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; // 1
[library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) { // 2

NSDictionary *imageInfo = [asset defaultRepresentation].metadata;

} failureBlock:^(NSError *error) {

...

}];

打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 1001;
PixelWidth = 1500;
"{Exif}" = {
ColorSpace = 1;
ComponentsConfiguration = (
1,
2,
3,
0
);
ExifVersion = (
2,
2,
1
);
FlashPixVersion = (
1,
0
);
PixelXDimension = 1500;
PixelYDimension = 1001;
SceneCaptureType = 0;
};
"{TIFF}" = {
Orientation = 1;
ResolutionUnit = 2;
XResolution = 72;
YResolution = 72;
};
}

这里做了什么呢?

  • 初始化了一个ALAssetsLibrary,可以把它想象成当前应用与系统相册之间的一个通道,通过它我们就可以对系统的相册进行一定程度上的数据存取;
  • 根据UIImagePickerController提供的URL,找到这张照片在相册中对应的对象(ALAsset),并返回给我们操作。

剩下的事情就一目了然了,metadata就是我们想要的数据字典

结语

读取照片中的信息并没有我们想象中的复杂,网上有许多实现用到了一些更底层的方法,我在代码里也有写下来。更完整的叙述可以看Stackoverflow上的这个回答。
完整代码传送门

补充

iOS9之后<AssetsLibrary/AssetsLibrary.h>被废弃了 而是使用了PhotoKit 这个在2014年WWDC推出的库.详细视频地址

代码:

1
2
3
4
5
6
7
8
9
PHFetchOptions * options = [PHFetchOptions new];

PHFetchResult *result= [PHAsset fetchAssetsWithALAssetURLs:@[url] options:options];

NSLog(@"%@",result.firstObject);

PHAsset *asset = result.firstObject;
NSLog(@"pixelWidth%lu\npixelHeight%lu",(unsigned long)asset.pixelWidth,(unsigned long)asset.pixelHeight);
NSLog(@"%@",asset);

打印:

1
<PHAsset: 0x1004634f0> 83C6A822-B627-4947-B2A8-E7F9483AC329/L0/001 mediaType=1/0, sourceType=1, (1280x2274), creationDate=2016-11-25 15:09:16 +0000, location=0, hidden=0, favorite=1

具体全部打印,没看到类似metadata的属性,属性都在PHAsset中

补充后的demo地址

PhotoKit补充资料

原demo地址:https://github.com/davidleee/GetExifInfoDemo
原创作者博客Lee的笔记本
iOS10之后info.plist中添加访问相册的字段Privacy - Photo Library Usage Description : 允许访问图册?

信息打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
{
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 2002;
PixelWidth = 3000;
ProfileName = "sRGB IEC61966-2.1";
"{ExifAux}" = {
ImageNumber = 8458;
LensID = 170;
LensInfo = (
24,
120,
4,
4
);
LensModel = "24.0-120.0 mm f/4.0";
SerialNumber = 6001440;
};
"{Exif}" = {
ApertureValue = "6.643855776306108";
ColorSpace = 1;
ComponentsConfiguration = (
1,
2,
3,
0
);
Contrast = 0;
CustomRendered = 0;
DateTimeDigitized = "2012:08:08 11:52:11";
DateTimeOriginal = "2012:08:08 11:52:11";
DigitalZoomRatio = 1;
ExifVersion = (
2,
3
);
ExposureBiasValue = 0;
ExposureMode = 1;
ExposureProgram = 1;
ExposureTime = 4;
FNumber = 10;
FileSource = 3;
Flash = 16;
FlashPixVersion = (
1,
0
);
FocalLenIn35mmFilm = 24;
FocalLength = 24;
FocalPlaneResolutionUnit = 4;
FocalPlaneXResolution = "204.840206185567";
FocalPlaneYResolution = "204.840206185567";
GainControl = 0;
ISOSpeedRatings = (
200
);
LightSource = 0;
MaxApertureValue = 4;
MeteringMode = 5;
PixelXDimension = 3000;
PixelYDimension = 2002;
Saturation = 0;
SceneCaptureType = 0;
SensingMethod = 2;
Sharpness = 0;
ShutterSpeedValue = "-2";
SubjectDistRange = 0;
SubjectDistance = "2.99";
SubsecTimeDigitized = 9;
SubsecTimeOriginal = 9;
WhiteBalance = 0;
};
"{GPS}" = {
Altitude = 103;
GPSVersion = (
2,
3,
0,
0
);
ImgDirection = "302.4";
ImgDirectionRef = T;
Latitude = "65.682895";
LatitudeRef = N;
Longitude = "17.54892833333333";
LongitudeRef = W;
MapDatum = "WGS-84";
Speed = "1.6";
SpeedRef = K;
};
"{IPTC}" = {
Byline = (
"Nicolas Cornet"
);
City = "Lj\U00f3savatn";
CopyrightNotice = "Nicolas Cornet";
"Country/PrimaryLocationName" = Iceland;
DateCreated = 20120808;
DigitalCreationDate = 20120808;
DigitalCreationTime = 115211;
ObjectName = Godafoss;
"Province/State" = Northeast;
SubLocation = Godafoss;
TimeCreated = 115211;
};
"{TIFF}" = {
Artist = "Nicolas Cornet";
Copyright = "Nicolas Cornet";
DateTime = "2012:08:08 11:52:11";
Make = "NIKON CORPORATION";
Model = "NIKON D800E";
Orientation = 1;
ResolutionUnit = 2;
Software = "Aperture 3.4.5";
XResolution = 72;
YResolution = 72;
};
}
文章目录
  1. 1. 前言
  2. 2. 那么问题来了
  3. 3. 前提
  4. 4. 照片哪里来
  5. 5. 动手吧!
  6. 6. 结语
  7. 7. 补充
  8. 8. PhotoKit补充资料