译自 NSHispter。
NDA 终于结束了,我们终于可以聊聊 iOS 7 里超赞的新 API 了。而且新 API 有非常多。苹果在 WWDC Keynote 中说有“1500 个新 API”,。(尽管里面很大一部分只是把 id
换成了 instancetype
,但是不管怎么说还是非常多)
带着版本升级的激动心情,接下来的几周我们会深入讨论 iOS 7 的许多新特性,但是这周,我们来看下那些隐藏颇深的新特性:NSData
Base64 编码,NSURLComponents
, NSProgress
, NSProcessInfo
activities,CIDetectorSmile
, CIDetectorEyeBlink
, SSReadingList
,AVCaptureMetaDataOutput
, AVSpeechSynthesizer
和MKDistanceFormatter
。
Base64 是一种常用的把二进制编码成 ASCII 字符的手段。由于许多常用 web 技术只支持文本,并不支持二进制,所以 Base64 应用十分广泛。例如,CSS 可以用 inline data://
URIs 来内嵌图片,这种形式就经常使用 Base64 编码的文本。另一个例子是 Basic Authentication
headers,它使用 Base64 编码用户名/密码对,稍微比完全明文好一点吧。
长久以来,这个重要的函数压根就没有,数以千计的开发者治好到论坛上随便抓一段代码贴过来就用。如此显而易见的缺失就像 iOS 5 之前的 JSON 一样恼人。
但是现在!iOS 7 终于内置了 Base64:
NSString *string = @"Lorem ipsum dolor sit amet.";NSString *base64EncodedString = [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];NSLog(@"%@", base64EncodedString); // @"TG9yZW0gaXBzdW0gZG9sYXIgc2l0IGFtZXQu"
Foundation 框架里面有许多 URI 相关的功能。不过,很多功能是用 NSString
实现的,因为 NSURL
是不可改变的。
NSURLComponents
极大地改善了这种情况。把它当成是 NSMutableURL
就好了:
NSURLComponents *components = [NSURLComponents componentsWithString:@"http://nshipster.com"];components.path = @"/iOS7";components.query = @"foo=bar";NSLog(@"%@", components.scheme); // @"http"NSLog(@"%@", [components URL]); // @"http://nshipster.com/iOS7?foo=bar"
URL components 的每个属性都有一个 percentEncoded*
的变种(例如 user
和 percentEncodedUser
),这些变种不会对 URI 中的特殊字符进行百分号编码。
你说那些字符算是特殊字符?好吧,这跟字符处在 URL 的哪个部分有关。好消息是 iOS 7 中 NSCharacterSet
增加了一个新的 category 来查询允许出现在 URL 中的字符。
+ (id)URLUserAllowedCharacterSet
+ (id)URLPasswordAllowedCharacterSet
+ (id)URLHostAllowedCharacterSet
+ (id)URLPathAllowedCharacterSet
+ (id)URLQueryAllowedCharacterSet
+ (id)URLFragmentAllowedCharacterSet
NSProgess
是一个有点难描述的类。它既是一个观察者,又是一个委托/协调者,扮演者报告和监控过程的角色。它在 OS X 上的系统级进程进行了整合,不过也可以用于面向用户的 UI。它可以指定暂停和取消的处理逻辑,让这些代码来真正地暂停和取消操作。
任何具有完成状态和总量概念的东西都可以使用 NSProgress
,不管是写入文件的字节数、渲染的帧数,还是要从服务器上下载的文件数。
NSProgress
可以用本地化的方式来汇报总体进度:
NSProgress *progress = [NSProgress progressWithTotalUnitCount:100];progress.completedUnitCount = 42;NSLog(@"%@", [progress localizedDescription]); // 42% completed
…或者可以给一个代码块来让操作完全停止:
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(incrementCompletedUnitCount:) userInfo:nil repeats:YES];progress.cancellationHandler = ^{ [timer invalidate];};[progress cancel];
NSProgess
在 Mac OS X 10.9 Mavericks 下很有用,但是在当前也还是一个包装工作量这个模式的一个很好的类。
欢呼吧!NSArray
中避免 NSRangeException
的 -lastObject
终于有了对应的取第一个元素对应方法。(好吧,实际上这个方法在 iOS 4 的时候就已经成为私有 API 了,但是现在终于开放出来了)
注意了!
NSArray *array = @[@1, @2, @3];NSLog(@"First Object: %@", [array firstObject]); // 1NSLog(@"Last Object: %@", [array lastObject]); // 3
爽!
As a random aside, shouldn't it be a cause for concern that the device most capable of taking embarrassing photos of ourselves is also the device most capable of distributing it to millions or people? Just a thought.
iOS 5 以后,Core Image 在 CIDetector
类中加入了面部识别和检测功能。如果检测照片中的人脸还不够炫的话,在 iOS 7 中我们甚至能检测笑脸和闭眼。*发抖*
又一个免费 app 的点子,只保存笑脸照片的相机 app,其中的一段代码可能是这样的:
@import CoreImage;
CIDetector *smileDetector = [CIDetector detectorOfType:CIDetectorSmile context:context options:@{CIDetectorTracking: @YES, CIDetectorAccuracy: CIDetectorAccuracyLow}];NSArray *features = [smileDetector featuresInImage:image];if ([features count] > 0) { UIImageWriteToSavedPhotosAlbum(image, self, @selector(didFinishWritingImage), features);} else { self.label.text = @"Say Cheese!"}
iOS 7 里在 AVCaptureMetaDataOutput
中新增加了扫描 UPC、QR 码、普通条形码和其他变种的方法。只需要添加一个 AVCaptureSession
的输出,实现相应的captureOutput:didOutputMetadataObjects:fromConnection:
方法:
@import AVFoundation;
AVCaptureSession *session = [[AVCaptureSession alloc] init];AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];NSError *error = nil;AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];if (input) { [session addInput:input];} else { NSLog(@"Error: %@", error);}AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];[session addOutput:output];[session startRunning];
#pragma mark - AVCaptureMetadataOutputObjectsDelegate- (void)captureOutput:(AVCaptureOutput *)captureOutputdidOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{ NSString *QRCode = nil; for (AVMetadataObject *metadata in metadataObjects) { if ([metadata.type isEqualToString:AVMetadataObjectTypeQRCode]) { // This will never happen; nobody has ever scanned a QR code... ever QRCode = [(AVMetadataMachineReadableCodeObject *)metadata stringValue]; break; } } NSLog(@"QR Code: %@", QRCode);}
AVFoundation
支持你听说过的所有条形码(大概还有一些没听过的):
AVMetadataObjectTypeUPCECode
AVMetadataObjectTypeCode39Code
AVMetadataObjectTypeCode39Mod43Code
AVMetadataObjectTypeEAN13Code
AVMetadataObjectTypeEAN8Code
AVMetadataObjectTypeCode93Code
AVMetadataObjectTypeCode128Code
AVMetadataObjectTypePDF417Code
AVMetadataObjectTypeQRCode
AVMetadataObjectTypeAztecCode
就算没有其他用途,AVCaptureMetaDataOutput
也使得在 iPhone 和 iPad 上创建一个 Passbook 条码读取器变得非常简单。
即使真正会看保存在稍后阅读中的文章的人很少,可能只比扫描过 QR 码的人多一些。iOS 7 里新的 Safari Services framework 还是带来一个不错的功能:向 Safari 的阅读列表中添加文章。
@import SafariServices;
NSURL *URL = [NSURL URLWithString:@"http://nshipster.com/ios7"];[[SSReadingList defaultReadingList] addReadingListItemWithURL:URL title:@"NSHipster" previewText:@"..." error:nil];
语音合成(TTS)技术在 1960 年代后期出现以来,就成为了可用性和恶作剧爱好者的利器。
iOS 7 把 Siri 的方便易用的Speak & Spell 功能带到了一个新的类AVSpeechSynthesizer
中:
AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@"Hello, World!"];utterance.rate = AVSpeechUtteranceMinimumSpeechRate; // Tell it to me slowly[synthesizer speakUtterance:utterance];
最后,我们用 NSHipster 会喊出“终于来了!”的另一个类来结束我们的 iOS 7 的又新又炫的新 API 介绍,它就是:MKDistanceFormatter
。
就像上面说的,MKDistanceFormatter
提供了一种把公制/英制距离转换成本地化字符串的方法。
@import CoreLocation;@import MapKit;
CLLocation *sanFrancisco = [[CLLocation alloc] initWithLatitude:37.775 longitude:-122.4183333];CLLocation *portland = [[CLLocation alloc] initWithLatitude:45.5236111 longitude:-122.675];CLLocationDistance distance = [portland distanceFromLocation:sanFrancisco];MKDistanceFormatter *formatter = [[MKDistanceFormatter alloc] init];formatter.units = MKDistanceFormatterUnitsImperial;NSLog(@"%@", [formatter stringFromDistance:distance]); // 535 miles
就是这些了!这只是 iOS 7 超棒的新特性中很小的一部分。想要更多?看下 Apple Developer Center 中的文档 "What's New in iOS 7"