历代iOS版本 — iOS7
1.已禁用-[UIDevice uniqueldentifier]
苹果总是把用户的隐私看的很重要。-[UIDevice uniqueldentifier]在iOS5实际在
iOS5的时候已经被遗弃了,但是iOS7中已经完全的禁用了它。Xcode5甚至不会允
许你编译包含了指引到-[UIDevice uniqueldentifier]的app。 此外, iOS7之前的使用
了-[UIDevice uniqueldentifier]的app如果在iOS7.上运行,它不会返回设备的
UUID,而是会返回一串字符串,以FFFFFFFF开头,跟着-[UIDevice
identifierForVendor]的十六进制值。
2.UIPasteboard由共享变为沙盒化了
UIPasteboard过去是用来做app之间的数据分享的。UIPasteboard本无问题,但是
开发者开始使用它来存储标识符,和其他的相关app分享这些标识符的时候问题就出
现了。有一个使用这种把戏的就是OpenUDID。
在iOS7中,使用+[UlPasteboard pasteboardWithName:create:]和+
[UlPasteboard pasteboardWithUniqueName]创建剪贴板,而且只对相同的app
group可见,这样就让OpenUDID不那么有用了。
3.MAC地址不能再用来设别设备
还有- -个生成iOS设备唯一标示符的方 法是使用iOS设备的Media Access
Control (MAC) 地址。一个MAC地址是一个唯一的号码, 它是物理网络层级方面分
配给网络适配器的。这个地址苹果还有其他的名字,比如说是硬件地址(Hardware
Address)或是Wifi地址,都是指同样的东西。
有很多工程和框架都使用这个方法来生成唯一的设备ID。比如说ODIN。然而,苹果
并不希望有人通过MAC地址来分辨用户,所以如果你在iOS7系统上查询MAC地址,
它现在只会返回02:00:00:00:00:00。
4.iOS现在要求app如需使用麦克风,需要征得用户同意
以前如果app需要使用用户的位置,通讯录,日历,提醒以及照片,接受推送消息,
使用用户的社交网络的时候需要征得用户的同意。现在在iOS7当中,使用麦克风也
需要取得用户同意了。如果用户不允许app使用麦克风的话,那么需要使用麦克风的
app就不能接收不到任何声音。
5.-[NSArray firstObject]的实现
-[NSArray firstObject]可能是Objective-C中被调用最多的APl。在Open Radar上-
个简单的调查显示有一些需求苹果已经做了记录。好消息是现在这些需求已经得到
了解决。. firstObject的使用可以追溯到iOS4.0,但是那时仅仅是-个私有方法。
6.增加了instancetype
instancetype让iOS7API变得更加难懂。苹果改变了大部分initializer和简易构造函
数. (convenience constructors),用instancetype代替id作返回类型。 但是这个
instancetype是什么呢?
instancetype用来在声明一个方法时告诉编译器其返回类型,它表示返回调用该方
法的类的对象。这比之前返回id的通常做法要好,编译器可以对返回类型做一些检
查,如果出现错误,在编译时就能提醒你,而不是在程序运行时发生崩溃。同时,
在调用子类方法时,使用它还可以省去对返回值的强制类型转换,编译器能够正确
推断方法的返回值类型。
7.设置Ullmage的渲染模式: Ullmage.renderingMode
着色(Tint Color)是iOS7界面中的一一个重大改变,你可以设置一个Ullmage在渲染
时是否使用当前视图的Tint Color。Ullmage新增了一个只读属性:
renderingMode,对应的还有一个新增方 法: imageWithRenderingMode:, 它使用.
UlImageRenderingMode枚举值来设置图片的renderingMode属性
8.tintcolor VS barTintColor
iOS7中你可以使用一一个给定的颜色,甚至是记入颜色主题来给整个app着色,帮助
你的app脱颖而出。设置app的tint color很简答,只要使用UIView的新属性tintColor
即可。
9.去掉了纹理颜色
纹理颜色?对,不再使用他们了。不能再创建可以展现纹理的颜色。根据
UIInterface.h文件中的注释,-[UIColor groupTableViewBackgroundColor]应该是
要在iOS6当中即被删除了,但是它仅仅只是不像之前那样返回纹理颜色了。
10.UIButtonTypeRoundRect被UlButtonTypeSystem取代了
在iOS开发刚开始就陪伴着你的老朋友现在也被删除了,它就是
UlButtonTypeRoundRect,被新的UlButtonTypeSystem取代了 。
11.检查无线路由是否可用
定制一个视频播放器的能力在iOS版本每次的发布中一直有所进步。比如说,在
i0S6之前,你不能在MPVolumeView中改变AirPlay的icon。
在iOS7当中,你可以通过AirPlay,蓝牙或是其他的虚线机制了解是否有-个远程的
设备可用。了解它的话,就可以让你的app在恰当的时候做恰当的事,比如说,在没
有远程设备的时候就不显示AirPlay的icon。
12.了解蜂窝网络
在iOS7之前,是使用Reachability来检测设备是否连接到WWAN或是Wifi的。iOs7
在这个基础.上更进了一步,它会告诉你的设备连接.上的是那种蜂窝网络,比如说是
Edge网络,HSDPA网络,或是LTE网络。告诉用户他们连接上的是哪种网络可以优
化用户体验,因为这样他们会知道网速如何,不会去请求需要高网速的网络请求。
13.通过iCloud同步用户设备的密码
iOS7以及Mavericks增加了iCloud Keychain来提供密码,以及iCloud中 一些敏感数
据的同步。开发者可以通过keychain中的kSecAttrSynchronizable key来遍历
dictionary对象。
由于直接处理keychain比较难,封装库提供了一个简单的处理keychain的方法。
SSKeychain封装库可能是最有名的的-一个,作为一种福利,现在它支持在iCloud同
步。
14.使用NSAttributedString显示HTML
在app中使用Webviews有时会让人非常沮丧,即使只是显示沙量的
HTMLneirong ,Webviews也会消耗大量的内容。现在iOS7让这些变得简单了,你可
以从用少量代码在HTML文件中创建一个NSAttributedString
15.使用原生的Base64
Base64是使用ASCII码显示二进制数据的一种流行方法。直到现在,开发者还不得
不使用开源的工具来编码解码Base64的内容。
16.使用UlApplicationUserDidTakeScreenshotNotification来检查截图
在iOS7之前,像Snapshot或 是Facebook Poke这样的app是使用-些很精巧的方法
来检测用户是否有截图。然而,iOS7提供一个 崭新的推送方法:
UIApplicationUserDidTakeScreenshotNotification。只要像往常一样订阅即可知道
什么时候截图了。
17.实现多语言语音合成
如果可以让app说话会不会很好呢? iOS7加入了两个新类: AVSpeechSynthesizer
以及AVSpeechUtterance。这两个类可以给你的app发声。很有意思不是吗?有多.
种语言可供选择一Siri不 会说的语言也有,比如说巴西葡萄牙语。
18.使用了新的UIScreenEdgePanGestureRecognizer
UIScreenEdgePanGestureRecognizer继承自UIPanGestureRecognizer,它可以
让你从屏幕边界即可检测手势。
19.使用UIScrollViewKeyboardDismissMode实现了Message app的行为
像Messages app一样在滚动的时候可以让键盘消失是-种非常好的体验。然而,将
这种行为整合到你的app很难。幸运的是,苹果给UIScrollView添加了一个很好用的
属性keyboardDismissMode,这样可以方便很多
20.使用Core lmage来检测眨眼以及微笑
iOS给Core Image增加了两种人脸检测功能: CIDetectorEyeBlink以及
CIDetectorSmile。这也就是说你现在可以在照片中检测微笑以及眨眼。
21.给UITextView增加了链接
现在在iOS添加你自己的Twitter账户更加简单了,现在你可以给一个
NSAttributedString增加链接了,然后当它被点击的时候唤起一个定制的action。
22 .多任务强化
●经常需 要下载新内容的应用现在可以通过设置UlBackgroundModes为fetch来
实现后台下载内容了,需要在AppDelegate里实现
setMinimumBackgroundFetchInterval:以及
application:performFetchWithCompletionHandler:来处理完成的下载,这个为
后台运行代码提供了又一种选择。不过考虑到Apple如果继续严格审核的话,可
能只有杂志报刊类应用能够取得这个权限吧。另外需要注意开发者仅只能指定
一个最小间隔,最后下没下估计就得看系统娘的心情了。
●同样是后台下载,以前只能推送提醒用户进入应用下载,现在可以接到推送并
在后台下载。UIBackgroundModes设 为remote-notification,并实现
application:didReceiveRemoteNotification:fetchCompletionHandler:
为后台下载,开发者必须使用一个新的类NSURL Session,其实就是在
NSURLConnection上加了个后台处理,使用类似,API十分简单,
ios 7多任务和后台下载:苹果全新的多任务API允许用户在后台智能的更新和下载
内容。ios7新添加了两个可以在后台更新应用程序界面和内容的APls。第一个API是
后台获取(Background Fetch),允许你在定期间隔内从网络获取新内容。第二个
API是远程通知(Remote Notification),它是一个新特性,它在当新事件发生时利
用推送通知(Push Notifications)去告知程序。这两个新的机制,帮助你保持程序
界面最新,还可以在新的后台传输服务(Background Transfer Service)中安排任
务,这允许你在进程外执行网络传输(下载和上传)
后台获取(Background Fetch)和远程通知(Remote Notification) 基于简单的应
用程序委托钩子,在应用程序挂起之前的30秒时钟时间开始执行工作。它们不是用
于CPU频繁工作或者长时间运行任务,而是用来处理长时间运行的网络请求队列,
例如下载-部很大的电影,或者执行快速的内容更新。
在用户看来,多任务处理唯一明显的变化就是新的程序切换器(app switcher),
它会显示当程序退出前台时每一个程序的界面快照。显示这些快照是有原因的:当
完成后台工作时,开发者可以更新程序快照,显示新内容的预览。社交网络,新
闻,或者天气的应用程序,可以在用户不打开应用程序的情况下显示最新的内容。
接下来我们会展示怎么样更新快照。
后台获取(Background Fetch)
一种智能的轮询机制,它很适合需要经常更新内容的程序,像社交网络,新闻或天气的程序。为了在用户启动程序前提前触发后台获取,系统会根据用户行为唤醒应用程序。举个例子,如果用户经常在下午1点使用
某个应用程序,系统会学习,适应并在使用周期前执行后台获取。为了减少电池使
用,后台获取(Background Fetch)会跨应用程序被设备的无线电合并,如果你向
系统报告新数据无法获取,iOS会适应并使用此信息避免会继续获取。
开启后台获取的第一步 是在info plist文件中的UIBackgroundModes健值指定使用的
特性。最简单的途径是在Xcode5的project editor中新的性能标签页中
(Capabilities tab)设置,这个标签页包含了后台模式部分,可以方便配置多任务选
项。
-(BOOL)application: (UIApplication
)application didFinishLaunchingWithOptions : (NSDictionary )launchOptions
[application
setMinimumBackgroundFetchInterval :UIApplicationBackgroundFetchIntervalMinimum];
return
YES;
}
iOS默认不进行后台获取,所以你需要设置一个时间间隔,否则,你的应用程序永远
不行在后台进行获取数据。UlApplicationBackgroundFetchIntervalMinimum这个
值要求系统尽可能经常去管理应用程序什么时候会被唤醒,但如果不需要这个值,
你应该指定你的时间间隔。例如,一个天气的应用程序,可能只需要几个小时才更
新一次,iOS将会在后台获取之间至少等待你指定的时间间隔。
如果你的应用允许用户退出登录,那么就没有获取新数据的需要了,你应该把
minimumBackgroundFetchInterval设置为
UlApplicationBackgroundFetchIntervalNever,这样可以节省资源。
最后一步是在应用程序委托中实现下列方法:
-(void)application:(UlApplication * )application
performFetchWithCompletionHandler:(void(^)
(UlBackgroundFetchResult))completionHandler{
NSURLSessionConfiguration sessionConfiguration =
[NSURL SessionConfiguration defaultSessionConfiguration];
NSURLSessionsession = [NSURLSession
sessionWithConfiguration:sessionConfiguration];
NSURLurl = [[NSURL alloc] initWithString:@"http://yourserver.com/data.json"];
NSURL SessionDataTasktask = [session dataTaskWithURL:url
completionHandler:^(NSData*data, NSURLResponse *response, NSError *error)
if(error) {
completionHandler(UlBackgroundFetchResultFailed);
return;
}
//Parse response/data and determine whether new content was available
BOOLhasNewData = ..if(hasNewData) {
completionHandler(UlBackgroundFetchResultNewData);
}else{
completionHandler(UlBackgroundFetchResultNoData);
}
}];
//Start the task
[task resume];
系统唤醒应用程序后将会执行这个委托方法。需要注意的是,你只有30秒的时间来
确定获取的新内容是否可用,然后处理新内容并更新界面。30秒时间应该足够去从
网络获取数据和获取界面的缩略图,最多只有30秒。当完成了网络请求和更新界面
后,你应该调用完成的处理代码。
完成的处理代码有两个目的。首先,系统会估量你的进程消耗的电量,并根据你传
递的UlBackgroundFetchResult参数记录新数据是否可用。其次,当你调用完成的
处理代码时,应用的界面缩略图会被采用,并更新应用程序切换器。当用户在应用
间切换时,用户将会看到新内容。这种快照行为的完成代码,在新的多任务处理
APIs中,很很常见的。
在实际应用中,你应该将completionHandler传递到应用程序的子组件,然后在处
理完数据和更新界面后调用。
在这里,你可能想知道iOS是如何在应用程序后台运行时获得界面快照的,并且想知
道应用程序的生命周期与后台获取之间有什么关系。如果应用程序处于挂起状态,
系统会先唤醒应用,然后再调用application:
performFetchWithCompletionHandler:。如果应用程序还没有启动,系统将会启动
它,然后调用常见的委托方法,包括application:
didFinishL aunchingWithOptions:。你可以把这种应用程序运行的方式想像为用户
从Springboard启动这个程序,区别仅仅在于界面是看不见的,在屏幕外渲染的。
大多数情况下,无论应用在后台启动或者在前台,你会执行相同的工作,但你可以
通过查看UIApplication的applicationState属性来判断应用是不是从后台启动。
-(BOOL )application: (UIApplication
*)application didF inishLaunchingWithOptions : (NSDictionary *)launchOptions
{
NSLog(@"Launched
in background %d" ,
UIApplicat ionStateBackground == application. applicationState);
return
YES;
}
23 .远程通知(Remote Notifications)
远程通知允许你在重要事件发生时,告知你的应用。你可能需要发送新的即时信
息,突发新闻的提醒,或者用户喜爱电视的最新剧集已经可以下载以便离线观看的
消息。远程通知很适合偶尔出现,但当前很重要的内容,这在后台获取之间出现的
延迟是不允许的。远程通知会比后台获取更有效率,因为应用程序只有在需要的时
候才会启动。
一条远程通知实际上只是一 条普通的带有content- available标志的推送通知。当你
在后台更新界面时,你可以发送一条带有提醒信息的推送去告诉用户。但远程通知
可以做到在安静地,没有提醒消息或者任何声音的情况下,只去更新应用界面或者
触发后台工作。然后你可以在完成下载或者处理完新内容后,发送一条 本地通知。
静默的推送通知有速度限制,所以你可以勇敢地根据应用程序的需要发送通知。iOs
和苹果推送服务会控制推送通知多久被递送,发送很多推送通知是没有问题的。如
果你的推送通知被禁止,推送通知可能会被延迟,直到设备下次发送保持活动状态
的数据包,或者收到另外一个通知。
发送远程通知(Sending Remote Notifications)
要发送一条远程通知,需要在推送通知的有效负载(payload) 设置content-
available标志。content- available标志和用来通知Newsstand应用的健值是一样
的,因此,大多数推送脚本和库都已经支持远程通知。当你发送一条远程通知时,
你可能还想要包含-些通知有效负载(payload) 中的数据,让你应用程序可以引用.
时间。这可以为你节省一些网络请求,并提高应用程序的响应度。
我建议在开发的时候,使用Nomad CLI's Houston工具发送推送消息,你也可以使
用你喜欢的库或脚本。
iOS7添加了一个新的应用程序委托方法,当接收到一条带有content - available的推
送通知时,这个方法被调用:
-(void)application:(UIApplication )application didReceiveRemoteNotification:
(NSDictionary )userlInfo fetchCompletionHandler:
(void( ^(UIBackgroundFetchResult))completionHandler{
NSLog( @"Remote Notification userInfo is %@",userInfo);
NSNumber* contentlD = userInfo[ @"content-id"];
//Do something with the content ID
completionHandler(UIBackgroundFetchResultNewData);
然后,应用程序进入后台启动,有30秒的时间去获取新内容并更新界面,最后调用
完成的处理代码。我们可以像后台获取那样,执行快速的网络请求,但我们可以使
用新的强大的后台传输服务,处理任务队列,下面看看我们如何在任务完成后更新
界面。
NSURLSession and Background Transfer Service
NSURLSession是iOS7添加的一个新类,它也是Foundation networking中的新技
术。作为NSURLConnection的替代品,-些熟悉的概念和类都保留下来了,例如
NSURL,NSURL Request和NSURLRespond。所以,你可以使用
NSURLConnection的替代品一-NSURL SessionTask,处理网络请求及响应。一共
有3中会话任务:数据,下载和上传。每一种都向NSURLSessionTask添加了语法糖
(syntactic sugar),根据你的需要,适当选择-种。
一个NSURLSession对象协调一个或多个NSURLSessionTask对象,并根据
NSURLSessionTask创建的NSURL SessionConfiguration实现不同的功能。使用相
同的配置,你也可以创建多组具有相关任务的NSURLSession对象。要利用后台传
输服务,你将会使用[NSURL SessionConfiguration
backgroundSessionConfiguration]来创建一个会话配置 。添加到后台会话的任务在
外部进程运行,即使应用程序被挂起,崩溃,或者被杀死,依然会运行。
NSURL SessionConfiguration允许你设置默认的HTTP头部,配置缓存策略,限制使
用蜂窝数据等等。其中一个选项是discretionary标志,这个标志允许系统为分配任
务进行性能优化。这意味着只有当设备有足够电量时,设备才通过Wifi进行数据传
输。如果电量低,或者只仅有一个蜂窝连接,传输任务是不会运行的。后台传输总
是在discretionary模式下运行。
目前为止,我们大概了解了NSURLSession,以及- -个后台会话如何进行,接下
来,让我们回到远程通知的例子,添加一-些代码来处理后台传输服务的下载队列。
当下载完成后,我们会通知用户该文件已经可以使用了。
NSURL .SessionDownloadTask
首先,我们先处理一条远程通知,并把一个NSURL SessionDownloadTask添加到后
台传输服务的队列。在backgroundURL Session方法中,我们根据后台会话配置,
创建- -个NSURLSession对象,并把应用程序委托对象(application delegate)作
为会话的委托对象。文档反对对于相同的标识符(identifier) 创建多个会话对象,
所以我们使用dispatch once来避免潜在的问题。
-(NSURLSession *)backgroundURL Session
static NSURLSession *session = nil;
static dispatch once_ t onceToken;
dispatch_ once(&onceToken,
f
NSString identifier = @"io.objc. backgroundTransferExample";
NSURL SessionConfiguration sessionConfig = [NSURL SessionConfiguration
backgroundSessionConfiguration:identifier];
session= [NSURLSession sessionWithConfiguration:sessionConfig delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
]);
return session;
-(void)application:(UlApplication )application didReceiveRemoteNotification:
(NSDictionary )userlnfo fetchCompletionHandler:(void (~)
(UlBackgroundFetchResult))completionHandler[
NSLog(@"Receivedremote notification with userlnfo %@",userInfo);
NSNumber *contentlD = userlnfo[ @"content-id"];
NSString downloadURL String = [NSString stringWithFormat:@"http://
yourserver.com/downloads/%d.mp3",
[contentlD intValue]];
NSURL downloadURL = [NSURL URLWithString:downloadURLString];
NSURLRequest *request = [NSURL Request requestWithURL:downloadURL];
NSURL SessionDownloadTask *task = [[self backgroundURL Session]
downloadTaskWithRequest:request];
task.taskDescription= [NSString stringWithFormat:@"PodcastEpisode %d",
[contentlD intValue]];
[task resume];
completionHandler(UlBackgroundFetchResultNewData);
我们使用NSURLSession类方法创建-个下载任务,配置请求,并提供说明供以后
使用。因为所有会话任务一开始处于挂起状态,你必须谨记要调用[task resume]保
证开始了任务。
现在,我们需要实现NSURLSessionDownloadDelegate的委托方法,当下载完成
时,调用回调函数。如果你需要处理认证或会话生命周期的其他事件,你可能还需
要实现NSURL SessionDelegate或NSURL SessionTaskDelegate的方法。你应该阅
读Apple的Life Cycle of a URL Session with Custom Delegates文档,它讲解了所
有类型的会话任务的完整生命周期。
NSURL SessionDownloadDelegate中的委托方法全部是必须实现的,尽管在这个例
子中我们只需要用到[NSURLSession
downloadTask:didFinishDownloadingToURL:]。任务完成下载时,你会得到一个磁
盘.上该文件的临时URL。你必须把这个文件移动或复制你的应用程序空间,因为当
你从这个委托方法返回时,该文件将从临时存储中删除。
Pragma Mark - NSURLSessionDownloadDelegate
-(void)URLSession:(NSURLSession )session
downloadTask:
(NSURL SessionDownloadTask )downloadTaskdidFinishDownloadingToURL:
(NSURL)location
NSL og( @"downloadTask:%@didFinishDownloadingToURL:
%@" ,downloadTask.taskDescription, location);
//Copy file to your app's storage with NSFileManager
//
Notify your UI
-(void)URL Session:(NSURL Session )sessiondownloadTask:
(NSURL SessionDownloadTask )downloadTaskdidResumeAtOffset:
(int64_ t)fileOffsetexpectedTotalBytes:(int64_ t)expectedTotalBytes
-(void)URLSession:(NSURLSession )sessiondownloadTask:
(NSURLSessionDownloadTask)downloadTaskdidWriteData:
(int64_ t)bytesWrittentotalBytesWritten:
(int64 t)totalBytesWrittentotalBytesExpectedToWrite:
(int64_ _t)totalBytesExpectedToWrite
当后台会话任务完成时,如果你的应用程序仍然在前台运行,上 面的代码已经足够
了。然而,在大多数情况下,你的应用程序没有运行,或者在后台被挂起。在这些
情况下,你必须实现应用程序委托的两个方法,这样系统就可以唤醒你的应用程
序。不同于以往的委托回调,该应用程序委托会被调用两次,因为您的会话和任务
委托可能会收到一系列消息。应用程序委托的:
handleEventsForBackgroundURLSession: Fit, ZEiÙ INSURL Session JEáJ)#
息发送前被调用,然后,URLSessionDidFinishEventsForBackgroundURLSession
被调用。在前面的方法中,储存了一个后台完成处理代码(completionHandler) ,
并在后面的方法中调用该代码更新界面。
-(void)application:(UlApplication
)applicationhandleEventsForBackgroundURL Session:(NSString ')identifier
completionHandler:(void())completionHandler{
//You must re-establish а reference to the background session,
//or NSURLSessionDownloadDelegate and NSURL SessionDelegate methods will
not be called
//as no delegate is attached to the session. See backgroundURLSession above.
NSURL Session* backgroundSession = [self backgroundURL Session];
NSLog( @"Rejoiningsession with identifier %@ %@",identifier,
backgroundSession);
//Store the completion handler to update your UI after processing session
events
[selfaddCompletionHandler:completionHandler forSession:identifier];
-(void)URLSessionDidFinishEventsForBackgroundURL Session:
(NSURL Session* )session
NSLog( @"BackgroundURL session %@ finished events.(n",session);
if(session.configuration.identifier) f
//Call the handler we stored in -
application:handleEventsForBackgroundURL Session:
[selfcallCompletionHandlerForSession:session.configuration.identifier];
]}
-(void)addCompletionHandler:(CompletionHandlerType)handlerforSession:
(NSString* )identifier
if([self.completionHandlerDictionary objectForKey:identifier]) f
NSLog( @"Error:Got multiple handlers for a single session identifier. This should
not happen.(n");
[self.completionHandlerDictionarysetObject:handler forKey:identifier];
]
-(void)callCompletionHandlerForSession:(NSString *)identifier
f
CompletionHandlerType handler = [self.completionHandlerDictionary
objectForKey: identifier];
if(handler) f
[self.completionHandlerDictionaryremoveObjectForKey: identifier];
NSLog( @"Callingcompletion handler for session %@",identifier);
handler()}
}
如果当后台传输完成时,应用程序不再在前台,那么,对于更新程序界面来说,这
两步是必要的。此外,如果当后台传输完成时,应用程序根本没有在运行,iOS将会
在后台启动该应用程序,然后前面的应用程序和会话的委托方法会在
application:didFinishL aunchingWithOptions..方法被调用之后被调用。
配置和限制(Configuration and Limitation)
我们简单地体验了后台传输的强大之处,但你应该深入文档,阅读
NSURL SessionConfiguration部分,以便最好地满足你的情况。例如,
NSURL SessionTasks通过NSURL SessionConfiguration的
timeoutIntervalForResource属性,支持资源超时特性。你可以使用这个特性指定你
允许完成一个传输所需的最长时间。内容只在有限的时间可用,或者在用户只有有
限Wifi带宽的时间内无法下载或上传资源的情况下,你也可以使用这个特性。
除了下载任务,NSURL Session也全面支持,上传任务,因此,你可能会在后台将视
频_上传到服务器,这保证用户不需要再像iOS6那样离开正在运行的应用程序。如果
当传输完成时你的应用程序不需要在后台运行,一个比较好的做法是,把
NSURL SessionConfiguration的sessionSendsLaunchEvents属性设置为NO。高效
利用系统资源,是一件让iOS和用户都高兴的事。
最后,我们来说- -说使用后台会话的几个限制。作为一个必须实现的委托,您不能
对NSURLSession使用简单的基于块的回调方法。后台启动应用程序,是相对耗费
较多资源的,所以总是采用HTTP重定向。后台传输服务只支持HTTP和HTTPS,你
不能使用自定义的协议。系统会根据可用的资源进行优化,在任何时候你都不能强
制传输任务在后台进行。
另外,要注意,在后台会话中,NSURLSessionDataTasks 是完全不支持的,你应
该只出于短期的,小请求为目的使用这些任务,而不是用来下载或上传。
总结
iOS7中新添加的多任务处理和网络的APls十分强大,它们为现有和新的应用程序开
辟了一系列可能。如果你的应用程序可以从进程外的网络传输和数据中获益,那么
尽情地使用这些美妙的APIs! -般情况下,实现后台传输,可以假装你的应用程序
正在前台运行,并进行适当的界面更新,而这大部分的工作已经为你完成了。
使用适当的新API,为你的应用程序提供内容服务。
尽可能早地有效率调用完成处理代码。
让完成的处理代码为应用程序更新界面快照
24、地图
Apple在继续在地图应用上的探索,MapKit的改进也乏善可陈。我- -直相信地图类
应用的瓶颈一定在于数据,但是对于数据源的建立并不是一年 两年能够完成的。
Google在这一块凭借自己的搜索引擎有着得天独厚的优势,苹果还差的很远很远。
看看有哪些新东西吧:
●MKMapCamera, 可以将一个MKMapCamera对象添加到地图上,在指明位
置,角度和方向后将呈现3D的样子...大概可以想象成一个数字版的Google街
景..
●MKDirections 获取Apple提供的基于方向的路径,然后可以用来将路径绘制在
自己的应用中。这可能对一些小的地图服务提供商产生冲击,但是还是那句
话,地图是一个数据的世界,在拥有完备数据之前,Apple不 是Google的对
手。这个状况至少会持续好几年(也有可能是永远)
●MKGeodesicPolyline 创建一个随地球曲率的线,并附加到地图上,完成- -些视
觉效果。
●MKMapSnapshotter 使用其拍摄基于地图的照片,也许各类签到类应用会用到
●改变了overlay物件的渲染方式
25、点对点连接Peer-to-Peer Connectivity
可以看成是AirDrop不能直接使用的补偿,代价是需要自己实现。
MultipeerConnectivity框架可以用来发现和连接附近的设备,并传输数据,而这-
切并不需要有网络连接。可以看到Apple逐渐在文件共享方面一步步放开限制,但是:
当然所有这些都还是被限制在sandbox里的。
在I0S7中,介绍了一个全新的框架: MultipeerConnectivity, 这个框架可以通
过,同-Wifi,蓝牙,来近距离传输数据。
- MCAdvertiserAssistant //可 以接收,并处理用户请求连接的响应。没有回
调,会弹出默认的提示框,并处理连接。 - MCNearbyServiceAdvertiser //可以接收,并处理用户请求连接的响应。但
是,这个类会有回调,告知有用户要与您的设备连接,然后可以自定义提示
框,以及自定义连接处理。 - MCNearbyServiceBrowser //用于搜索附近的用户,并可以对搜索到的用户发
出邀请加入某个会话中。 - MCPeerlD //这表明是一-个用户
- MCSession //启用和管理Multipeer连接会话中的所有人之间的沟通。通过
Sesion,给别人发送数据。
流程基本上是:
1.MCNearByServiceBrowser发现用户
它有三个回调 - //开启浏览器失败
- (void)browser:
(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:
(NSError *)error
- (void)browser:
- //发现附近的用户
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:
(MCPeerlD *)peerlD withDiscoveryInfo:(NSDictionary *)info
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:
- //某个用户消失了
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:
(MCPeerID *)peerlD
2.通过MCNearByServicebrowser邀请某个用户加入某个会话
/下面的东西,需要你们自己实例化
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:
- [MCNearByServicebrowser invitePeer:MCPeerID toSession:MCSession with
Context:NSData timeout:10];
3.MCNearbyServiceAdvertiser处理接收到的邀请
//这个类,有下面这么一个回调 - //接收到邀请
- (void)advertiser:,
(MCNearbyServiceAdvertiser *)advertiser didReceivelnvitationFromPeer:
(MCPeerID *)peerlD withContext:(NSData * )context invitationHandler:
(void (^)(BOOL, MCSession *))invitationHandler
- (void)advertiser:,
- //其中: invitationHandler 这个参数是用来处理是否接收请求的,如下所示
- self.handler(YES,self.session); //这就表示你接受邀请了,系统将会你们的设
备进行匹配 - MCSession会话状态的改变,以及接收到的消息
//主要有2个回调,如下所示
//会话状态发生改变当我们接受别人的邀请的时候,就会触发下面的会
话回调,表明,有人连接我们了,或者拒绝我们的邀请了。
- (void)session:(MCSession *)session peer:
(MCPeerID *)peerID didChangeState:(MCSessionState)state
使用如下:
//会话状态发生改变 - (void)session:(MCSession *)session peer:
(MCPeerID *)peerID didChangeState:(MCSessionState)state
{
//接受邀请
if (state == MCSessionStateConnected && self.session) {
//发送通知,告诉有人连接
[[NSNotificationCenter defaultCenter] postNotificationName:PeerCon
nectionAcceptedNotification object:nil userlnfo:@{@"peer": peerlD,@"acc
ept":@YES}];
}
//邀请被拒绝
else if(state == MCSessionStateNotConnected && self. session){
//只处理不在会话中的
if (![self. session.connectedPeers containsObject:peerlD]) {
[[NSNotificationCenter defaultCenter] postNotificationName:PeerC
onnectionAcceptedNotification object:nil userlnfo:@{@"peer": peerlD,@"a
ccept":@NO}];
}
}
从会话中接收到数据如果有人给我们发送数据,则会走这个回调,从
这里,我们获取到别人发送给我们的数据 - (void)session:(MCSession *)session didReceiveData: .
(NSData *)data fromPeer:(MCPeerID *)peerID;
使用如下:
/从会话中接收到数据 - (void)session:(MCSession * )session didReceiveData:
(NSData *)data fromPeer:(MCPeerID *)peerID
{
//解析数据
NSString *msgStr = [[NSString alloc] initWithData:data encoding:NSUT
F8StringEncoding];
msgStr = [NSString stringWithFormat:@"%@:
%@",peerlD.displayName,msgStr];
//将 数据加入集合中
[self.messageListArray addObject:msgStr];
//发送通知,接收到数据
[[ NSNotificationCenter defaultCenter] postNotificationName:DataRecei
vedNotification object:nil];
}
26、Store Kit Framework
Store Kit在内购方面采用了新的订单系统,这将可以实现对订单的本机验证。这是
-次对应内购破解和有可能验证失败导致内购失败的更新,苹果希望藉此减少内购
的实现流程,减少出错,同时遏制内购破解泛滥。前者可能没有问题,但是后者的
话,因为objc的动态特性,决定了只要有越狱存在,内购破解也是早晚的事情。不
过这一点确实方便了没有能力架设验证服务器的小开发者,这方面来说还是很好
的。
-(void)buy:(int)type
f
buyType = type;
if ([SKPaymentQueue canMakePayments]) f
[self RequestProductData];
NSLog(@"允许程序内付费购买");
]
else
NSLog(@"不允许程序内付费购买"); .
UIAlertView *alerView = [[UlAlertView alloc] initWithTitle:@" tÊJл'
message:@"您的手机没有打开程序内付费购买"
delegate:nil cancelButtonTitle:NSLocalizedString(@"#f" ,nil)
otherButtonTitles:nil];
[alerView show];
]
-(void)RequestProductData
NSo_(@------请求对应的产品信---------V);
NSArray *product = nil;
switch (buyType) [
case IAP0p20:
product=[[NSArray alloc] initWithObjects:ProductlD_ lAP0p20,nil];
break;
case IAP1p100:
product=[[NSArray alloc] initWithObjects:ProductlD IAP1p100,niI];
break;
case IAP4p600:
product=[[NSArray alloc] initWithObjects:ProductlD lAP4p600,ni];
break;
case IAP9p1000:
product=[[NSArray alloc] initWithObjects:ProductlD lAP9p1000,ni];
break;
case IAP24p6000:
product=[[NSArray alloc] initWithObjects:ProductlD _lAP24p6000,nil];
break;
default:
break;
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request=[[SKProductsRequest alloc]
initWithProductldentifiers: nsset];
request.delegate=self;
}
[request start];
//<SKProductsRequestDelegate>请求协议
//收到的产品信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:
(SKProductsResponse *)response{
.SL-@---------收到产品反馈信息----------
NSArray *myProduct = response.products;
NSLog( @"产品Product ID:%@" ,response.invalidProductldentifiers);
NSLog(@"产品付费数量: %d", (int)[myProduct count]);
// populate UI
for(SKProduct *product in myProduct){
NSLog(@"product info");
NSLog(@" SKProduct描述信息%@", [product description]);
NSLog(@"产品标题%@" , product.localizedTitle);
NSLog(@"产品描述信息: %@" , product.localizedDescription);
NSLog(@"价格: %@",product.price);
NSLog(@"Product id: %@" , product.productldentifier);
}
SKPayment *payment = nil;
switch (buyType) [
case IAP0p20:
payment = [SKPayment
paymentWithProductldentifier:ProductlD_ IAP0p20]; //51T25
break;
case IAP1p100:
payment = [SKPayment
paymentWithProductldentifier:ProductlD IAP1p100]; //517108
break;
case IAP4p600:
payment = [SKPayment
paymentWithProductldentifier:ProductlD IAP4p600]; //E15618
break;
case IAP9p1000:
payment = [SKPayment
paymentWithProductldentifier:ProductlD IAP9p1000]; //5171048
break;
case lAP24p6000:
payment = [SKPayment
paymentWithProductldentifier:ProductlD _IAP24p6000];
1/5155898
break;
default:
break;
}
NSLog(@"--
.发送购买请---------);
[[SKPaymentQueue defaultQueue] addPayment:payment];
} - (void)requestProUpgradeProductData
NSLog------请求升级数据-------; .
NSSet productldentifiers = [NSSet setWithObject:@"com.productid"];
SKProductsRequest productsRequest = [[SKProductsRequest alloc]
initWithProductldentifiers:productldentifiers];
productsRequest.delegate = self;
[productsRequest start];
//弹出错误信息 - (void)request:( SKRequest *)request didFailWithError:(NSError *)errorf
NSLo-------弹出错误信息-------); .
UIAlertView *alerView = [[UlAlertView alloc]
initWithTitle:NSLocalizedString(@"Alert" ,NULL) message:[error
localizedDescription]
delegate:nil cancelButtonTitle:NSLocalizedString(@"Close" ,nil)
otherButtonTitles:nil;
[alerView show];
}
-(void) requestDidFinish:(SKRequest *)request
{
NSLo----------反馈信息结-----------;
}
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{
NSLog(@"----PurchasedTransaction----");
NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
[self paymentQueue:[SKPaymentQueue defaultQueue]
updatedTransactions:transactions];
}
//<SKPaymentTransactionObserver>千万不要忘记绑定,代码如下:
/----监听购买结果
/[SKPaymentQueue defaultQueue] addTransactionObserver:self]; - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:
(NSArray *)transactions//交易结果
NSLog(@"----paymentQueue------");
for (SKPaymentTransaction *transaction in transactions)
f
switch (transaction.transactionState)
case SKPaymentTransactionStatePurchased:/// 5 5t5X
[self completeTransaction:transaction];
NSLog-----交易完成------); .
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@""
message:@"购买成功"
delegate:nil cancelButtonTitle:NSLocalizedString(@"#",nil)
otherButtonTitles:nil];
[alerView show];
] break;
case SKPaymentTransactionStateFailed://X 5 #k
f [self failedTransaction:transaction];
NSLog(@-----交易失败------);
UlAlertView *alerView2 = [[UlAlertView alloc] initWithTitle:@"tÆ JT"
message:@"购买失败,请重新尝试购买"
delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭" ,nil)
otherButtonTitles:nil];
[alerView2 show];
}break;
case SKPaymentTransactionStateRestored://已经购买过该商品
[self restoreTransaction:transaction];
NSLo_(@-----已经购买过该商品------; .
case SKPaymentTransactionStatePurchasing: //商 品添加进列表
NSLog(@"-----商品添加进列表------");
break;
default:
break;
} - (void) completeTransaction: (SKPaymentTransaction *)transaction
NSLog(@"----completeTransact--------");
/1 Your application should implement these two methods.
NSString *product = transaction.payment.productldentifier;
if ([product length]> 0) {
NSArray *tt = [product componentsSeparatedByString:@"."];
NSString *bookid = [tt lastObject];
if ([bookid length]> 0) {
[self recordTransaction:bookid];
[self provideContent:bookid];
}
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
//记录交易
-(void)recordTransaction:(NSString *)product{
NSLog(@"----记录交易-------");
}
(void) failedTransaction: (SKPaymentTransaction *)transactionf
NSLog(@"失败");
if (transaction.error.code != SKErrorPaymentCancelled)
[
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
-(void) paymentQueueRestoreCompletedTransactionsFinished:
(SKPaymentTransaction *)transactionf
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
NSLog(@"交易恢复处理");
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue
restoreCompletedTransactionsFailedWithError:(NSError *)error[
NSLog(@"----paymentQueue---");
pragma mark connection delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData
*)data
{
NSL og(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); - (void)connectionDidFinishL oading:(NSURLConnection *)connection{
} - (void)connection:(NSURLConnection *)connection didReceiveResponse:
(NSURLResponse *)response{
switch([(NSHTTPURLResponse *)response statusCode]) {
case 200:
case 206:
break;
case 304:
break;
case 400:
break;
case 404:
break;
case 416:
break;
case 403:
break;
case 401:
case 500:
break;
default:
break;
}
} - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError
*)error {
NSLog(@"test");
-(void)dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
UI相关
简单总结来说,以现在上手体验看来新的UI变化改进有如下几点:
●状态栏,导航栏和应用实际展示内容不再界限:系统自带的应用都不再区分状
态栏和navigation bar,而是用统一的颜色力求简洁。这也算是-种趋势。
●Barltem的按钮全 部文字化:这点做的相当坚决,所有的导航和工具条按钮都取
消了拟物化,原来的文字(比如“Edit”, “Done”之类) 改为了简单的文字,原
来的图标(比如新建或者删除)也做了简化。
●程序打开加入了动画:从主界面到图标所在位置的一.个放大,同时显示应用的
载入界面。
2 UIKit的力学模型(UlKit Dynamics)
新增了UIDynamicltem委托,用来为UIView制定力学模型行为,当然其他任何对象
都能通过实现这组接口来定义动力学行为,只不过在UIKit中可 能应用最多。所谓动
力学行为,是指将现实世界的我们常见的力学行为或者特性引入到UI中,比如重力
等。通过实现UlDynamicltem,UIKit现在支 持如下行为:
●UlAttachmentBehavior 连接两个实现了UIDynamicltem的物体(以下简称动力
物体),一个物体移动时,另一个跟随移动
●UICollisionBehavior 指定边界,使两个动力物体可以进行碰撞
●UIGravityBehavior 顾名思义,为动力物体增加重力模拟
●UlPushBehavior 为动力物体施加持续的力
●UISnapBehavior为动力物体指定一个附着点,想象一下类似挂一幅画在图钉上的感觉.
如果有开发游戏的童鞋可能会觉得这些很多都是做游戏时候的需求,-种box2d之
类的2D物理引擎的既视感跃然而出。没错的亲,动态UI, 加上之后要介绍的Sprite
Kit,极大的扩展了使用UIKit进行游戏开发的可能性。另外要注意UIDynamicltem不
仅适用于UIKit,任何对象都可以实现接口来获得动态物体的一些特性,所以说用来
做一些3D的或者其他奇怪有趣的事情也不是没有可能。如果觉得Cocos2D+box2d
这样的组合使用起来不方便的话,现在动态UlKit+ SpriteKit给出了新的选择。
UlKit动力学最大的特点是将现实世界动力驱动的动画引入了UlKit,比如重力,铰链
连接,碰撞,悬挂等效果,即将2D物理引擎引入了UIKit
注意: UIKit动力学的引入,并不是为了替代CA或者UIView动画,在绝大多数情况下
CA或者UIView动画仍然是最优方案,只有在需要引入逼真的交互设计的时候,才需
要使用UIKit动力学它是作为现有交互设计和实现的-种补充
●其他2D仿真引擎:
BOX2D: C语言框架,免费
Chipmunk: C语言框架免费,其他版本收费
UIDynamic中的三个重要概念
●DynamicAnimator:动画者,为动力学元素提供物理学相关的能力及动画,同时为
这些元素提供相关的上下文,是动力学元素与底层iOS物理引擎之间的中介,将
Behavior对象添加到Animator即可实现动力仿真
●DynamicAnimatorltem:动力学元素,是任何遵守了UlDynamicltem协议的对象,
从iOS7.0开始,UIView和UICollectionViewL ayoutAttributes默认实现该协议。如果
自定义的对象实现了该协议,即可通过DynamicAnimator实现物理仿真
●UIDynamicBehavior:仿真行为,是动力学行为的父类,基本的动力学行为类
UIGravityBehavior、UICollisionBehavior、 UIAttachmentBehavior、
UISnapBehavior、UIPushBehavior以及 UIDynamicltemBehavior均继承自该父类动
力学动画元素(DynamicAnimator Item)协议
●只有遵守了UIDynamicltem协议的对象才可以参与到UI动力学仿真中
●从iOS7开始,UIView和UICollectionViewLayoutAttributes类默认实现 了该协议
●协议定义的属性:
bounds: Dynamic
animator需要动画元素的边框时调用,只读属性,用于计算物体的边界以及质量
center:动力学元素的中心点,读写属性
transform:动力学元素的旋转角度,读写属性(需要指定Layer的形 变属性)
动力学行为(Dynamic Behavior)
一个动力学行为可以为--个或者多个动力学元素赋予参与在二维动画中所具备的行
为
.iOS7.0中提供的动力学行为包括:
UIGravityBehavior:重力行为
UICollisionBehavior:碰撞行为
UlAttachmentBehavior:附着行为
UISnapBehavior:吸附行为
UIPushBehavior:推行为
UIDynamicltemBehavior:动力学元素行为
●所有的UIDynamicBehavior都是可以独立作用,同时也遵守力的合成。也就是说,
组合使用行为可以实现一些较复杂的效果
碰撞特性UlCollisionBehavior
( void )collisionBehavior:( UICollisionBehavior *)behavior
beganContactForltem:( id < UIDynamicltem > )item
withBoundaryldentifier:( id < NSCopying >)identifier a
tPoint:( CGPoint )p;
这个方法是在边界发生碰撞的时候才去执行的
UICollisionBehavior这个和tableview的委托方法一样理解,item是 碰撞的对象,
identifier为对象添加定义,p为发生碰撞的位置。
如何实现碰撞这个方法呢,如下:
UICollisionBehaviorDelegate >这个委托,然后把_ ground对象的委托给当前这个
viewController。方法如下:
import <UlKit/UlKit.h>
//new
@interface ViewController : UIViewController<UlCollisionBehaviorDelegate>
UIDynamicAnimator *. _animator;
UlGravityBehavior *. gravity;
UICollisionBehavior *. ground;
@end
- (void)viewDidLoad
[super viewDidLoad];
UIView * apple = [[UIView alloc] initWithFrame:CGRectMake(40,40, 40, 40)];
apple.backgroundColor = [UIColor redColor];
[self.view addSubview:apple];
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_gravity = [[UlGravityBehavior alloc] initWithltems:@[apple]];
[ animator addBehavior: gravity];
ground = [[UlCollisionBehavior alloc] initWithltems:@[apple]];
ground.translatesReferenceBoundsIntoBoundary = YES;
[ animator addBehavior: ground];
//new
. ground.collisionDelegate = self;
设置_ground.collisionDelegate为试图控制器,之后当界面在发生碰撞,就可以调
用- -开始所说的委托方法了。 - (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForltem:
(id<UlDynamicltem> )item withBoundaryldentifier:(id<NSCopying> )identifier
atPoint:(CGPoint)p{
NSLog(@"好疼,我撞在%f,%f, %@",p.x,p.y,identifier); .
}
游戏方面
Sprite Kit Framework
这是个人认为iOS7 SDK最大的亮点,也是最重要的部分,ios SDK终于有自己的精
灵系统了。Sprite Kit Framework使用硬件加速的动画系统来表现2D和2.5D的游
戏,它提供了制作游戏所需要的大部分的工具,包括图像渲染,动画系统,声音播
放以及图像模拟的物理引擎。可以说这个框架是iOs SDK自带了一个较完备的2D游
戏引擎,力图让开发者专注于更高层的实现和内容。和大多数游戏引擎一样, Sprite
Kit内的内容都按照场景(Scene) 来分开组织,-个场景可以包括贴图对象,视
频,形状,粒子效果甚至是Corelmage滤镜等等。相对于现有的2D引擎来说,由于
Sprite Kit是在系统层级进行的优化,渲染时间等都由框架决定,因此应该会有比较
高的效率。
另外,Xcode还提供了创建粒子系统和贴图Atlas的工具。使用Xcode来管理粒子效
果和贴图atlas,可以迅速在Sprite Kit中反应出来。
Game Controller Framework
为Made-for-iPhone/iPod/iPad (MFi) game controller设计的硬件的对应的框架,可
以让用户用来连接和控制专门的游戏硬件。参考WWDC 2013开场视频中开始的赛
车演示。现在想到的是,也许这货不仅可以用于游戏.. .或者苹果之后会扩展其应用,
因为使用普及率很高的iPhone作为物联网的入口,似乎会是很有前途的事情。