i​OS 7

Mattt Thompson撰写、 Zihan Xu翻译、 发布于

随着保密协议的解除,我们终于可以开始聊一聊iOS 7里面精彩的新的API了。新的操作系统增加了非常多的API,根据Apple在WWDC上的统计,有1500之多。(同意,虽然其中很大一部分可能只是将id 变成了instancetype,但是不管怎么说,这是个大数目)。

在接下来的几周里,我们将会深入了解iOS 7的很多新增加的功能,但不管我们对这次版本的发布有多兴奋,本周的主题是谈一谈那些隐藏在平淡光彩下的宝石:NSData Base64 编码, NSURLComponents, NSProgress, NSProcessInfo activities, CIDetectorSmile, CIDetectorEyeBlink, SSReadingList, AVCaptureMetaDataOutput, AVSpeechSynthesizer, 以及 MKDistanceFormatter.


NSData (NSDataBase64Encoding)

Base64是作为ASCII文本为二进制编码的常用方式之一。因为很多核心技术都被设计用来支持文本,而不是原始二进制,所以Base64在网络上被广泛的使用。比如,CSS可以通过inline data:// URIs嵌入图像,这通常是Base64编码的。另一个例子是基本认证头标,这也是通过Base64来编码用户名/密码对,这也比它们完全暴露要稍好一些。

很长时间以来,这个重要却无聊的功能完全没有被实现,使得成千上万的开发者不得不从论坛中粘贴复制随机的代码片段。这个疏忽就和iOS5之前的JSON一样突出且恼人。

但这再也不是问题了!iOS7终于加入Base64了:

NSString *string = @"Lorem ipsum dolor sit amet.";
NSString *base64EncodedString = [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];

NSLog(@"%@", base64EncodedString); // @"TG9yZW0gaXBzdW0gZG9sYXIgc2l0IGFtZXQu"

NSURLComponents & NSCharacterSet (NSURLUtilities)

Foundation得天独厚地拥有许多能应用在URI上的功能。不幸的是,许多用来操控URL的API都因为NSURL的不可变性而仍在使用NSString.

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的property还有一个percentEncoded*的演变(比如user & percentEncodedUser),通过这个变形就不需要使用其他额外的URI特殊字符百分号编码。

你也许会问,哪些字符是特殊的呢?这取决于你所指的是URL的哪一部分。好消息是NSCharacterSet增加了一个用来在iOS 7里允许使用新的URL字符的新的类别:

  • + (id)URLUserAllowedCharacterSet
  • + (id)URLPasswordAllowedCharacterSet
  • + (id)URLHostAllowedCharacterSet
  • + (id)URLPathAllowedCharacterSet
  • + (id)URLQueryAllowedCharacterSet
  • + (id)URLFragmentAllowedCharacterSet

NSProgress

NSProgress 是一个很难描述的类。它的作用既像观察员又像代表/协调员,可以处理报告以及监测的进程。它可以与OS X上的系统级别进程进行集成,但也可以被插入到面向用户的用户界面。它可以指定处理器来暂停和取消,然后前进到实际进行这项工作的操作上去。

任何具有完成的和整体的概念的单位都属于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];

NSProgress在OS X Mavericks的使用环境下,会更有意义,但就目前而言,它仍然仅仅是一个用于封装工作单位共享模式的有用的类。

NSArray -firstObject

欢呼吧!NSRangeException中方便使用的-lastObject终于被延伸到NSArray的第一个成员了。(虽然_自从_iOS 4以来就有私有API可以实现这个功能,不过这个不再重要了。)

看哪!

NSArray *array = @[@1, @2, @3];

NSLog(@"First Object: %@", [array firstObject]); // 1
NSLog(@"Last Object: %@", [array lastObject]); // 3

真让人神清气爽啊!

随口说两句,最有能力照到我们尴尬照片的设备,同时也是最有能力把这些照片和数百万人分享的设备这件事情难道不应该引起人们的关注吗?这仅仅是一个想法。

自从iOS5以来,Core Image框架提供了通过CIDetector类可实现的面部监测与识别功能。如果在照片中识别面部还不够疯狂,那么在iOS 7中我们甚至可以识别这张脸是在微笑还是闭眼睛了。*不寒而栗*

这又是一个免费应用的想法,这里的代码片段或许可以被照相机使用来仅仅存储带有笑脸的照片:

@import CoreImage;
CIDetector *smileDetector = [CIDetector detectorOfType:CIDetectorTypeFace
                                context:context
                                options:@{CIDetectorTracking: @YES,
                                          CIDetectorAccuracy: CIDetectorAccuracyLow}];
NSArray *features = [smileDetector featuresInImage:image options:@{CIDetectorSmile:@YES}];
if (([features count] > 0) && (((CIFaceFeature *)features[0]).hasSmile)) {
    UIImageWriteToSavedPhotosAlbum(image, self, @selector(didFinishWritingImage), features);
} else {
    self.label.text = @"Say Cheese!"
}

AVCaptureMetaDataOutput

通过AVCaptureMetaDataOutput扫瞄各式各样的UPC,QR码和条形码,是iOS 7的新功能。你所需要做的就是将它设置为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 setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[session addOutput:output];

[session startRunning];
#pragma mark - AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(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的Pass阅读器成为可能。Passbook仍有很多没有被实现的潜力,所以我们希望这个API能够被广泛的应用。

SSReadingList

虽然阅读以前存储下来内容的人数仅仅比使用过QR码的人数多一点,但iOS 7增加了Safari服务框架功能以添加Safari阅读列表仍然是个好事情。

@import SafariServices;
NSURL *URL = [NSURL URLWithString:@"http://nshipster.com/ios7"];
[[SSReadingList defaultReadingList] addReadingListItemWithURL:URL
                                                        title:@"NSHipster"
                                                  previewText:@"..."
                                                        error:nil];

AVSpeechSynthesizer

文本到语音自从60年代面市以来,对于易接近性恶作剧爱好者来说一直是电脑的杀手级功能。

iOS 7将Siri的说&拼写的便捷的能力加入到了一个新的类AVSpeechSynthesizer

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@"Just what do you think you're doing, Dave?"];
utterance.rate = AVSpeechUtteranceMinimumSpeechRate; // Tell it to me slowly
[synthesizer speakUtterance:utterance];

MKDistanceFormatter

最后,我们由另外一个新的并且值得注意的类来结束这次展示。这个类的出现使得NSHipster们高兴地大呼“终于来了!”: 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开发者中心的"What's New in iOS 7"手册吧。