由于苹果公司对用户的隐私把控的很严格,所以一些设计到用户隐私的内容,发生了改动.
1.[UIDevice uniqueIdentifier] 禁用
-[UIDevice uniqueIdentifier]苹果在iOS5的时候已经将其遗弃,从iOS7中开始就完全的禁用。在 Xcode5中包含-[UIDevice uniqueIdentifier]的app也将会报错。此外,iOS7之前的使用了-[UIDevice uniqueIdentifier] 的app如果在iOS7上运行,它不会返回设备的UUID,而是会返回一串字符串,以FFFFFFFF开头,跟着-[UIDevice identifierForVendor]的十六进制值。
2.UIPasteboard由共享变为沙盒化
UIPasteboard过去是用来做app之间的数据分享的。UIPasteboard本无问题,但是开发者开始使用它来存储标识符,和其他的相关app分享这些标识符的时候问题就出现了。有一个使用这种把戏的就是OpenUDID。
在iOS7中,使用 +[UIPasteboard pasteboardWithName:create:]和 +[UIPasteboard pasteboardWithUniqueName]创建剪贴板,而且只对相同的app group可见,这样就让OpenUDID不那么有用了。
3.MAC地址不能再用来设别设备
现在仍可以使用这个MAC
还有一个生成iOS设备唯一标示符的方法是使用iOS设备的Media Access Control(MAC)地址。一个MAC地址是一个唯一的号码,它是物理网络层级方面分配给网络适配器的。这个地址苹果还有其他的名字,比如说是硬件地 址(Hardware Address)或是Wifi地址,都是指同样的东西。
有很多工程和框架都使用这个方法来生成唯一的设备ID。比如说ODIN。然而,苹果并不希望有人通过MAC地址来分辨用户,所以如果你在iOS7系统上查询MAC地址,它现在只会返回02:00:00:00:00:00。
现在苹果明确的表明你应该使用-[UIDevice identifierForVendor]或是-[ASIdentifierManager advertisingIdentifier]来作为你框架和应用的唯一标示符。坦白的来说,应对这些变化也不是那么的难,见以下代码片段:
- NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString];
- NSString *identifierForAdvertising = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
每种方法都适配一种特别的用法:
identifierForVendor对供应商来说是唯一的一个值,也就是说,由同一个公司发行的的app在相同的设备上运行的时候都会有这个相同的标识符。然而,如果用户删除了这个供应商的app然后再重新安装的话,这个标识符就会不一致。
advertisingIdentifier会返回给在这个设备上所有软件供应商相同的 一个值,所以只能在广告的时候使用。这个值会因为很多情况而有所变化,比如说用户初始化设备的时候便会改变。
4.iOS现在要求app如需使用麦克风,需要征得用户同意
以前如果app需要使用用户的位置,通讯录,日历,提醒以及照片,接受推送消息,使用用户的社交网络的时候需要征得用户的同意。现在在iOS7 当中,使用麦克风也需要取得用户同意了。如果用户不允许app使用麦克风的话,那么需要使用麦克风的app就不能接收不到任何声音。
以下的代码是用来查询用户是否允许app使用麦克风:
- //第一次调用这个方法的时候,系统会提示用户让他同意你的app获取麦克风的数据
- // 其他时候调用方法的时候,则不会提醒用户
- // 而会传递之前的值来要求用户同意
- [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
- if (granted) {
- // 用户同意获取数据
- } else {
- // 可以显示一个提示框告诉用户这个app没有得到允许?
- }
- }];
你同时还要注意,如果你在获得用户的同意之前使用任何方法来使用麦克风的话,会引起iOS系统弹出以下警示栏:
5.-[NSArray firstObject]的实现
-[NSArray firstObject]可能是Objective-C中被调用做多的API。在Open Radar上一个简单的调查显示有一些需求苹果已经做了记录。好消息是现在这些需求已经得到了解决。. firstObject的使用可以追溯到iOS4.0,但是那时仅仅是一个私有方法。在iOS7以前,工程师用下面的方式来使用它:
- NSArray *arr = @[];
- id item = [arr firstObject];
- // 之前你需要做以下工作
- id item = [arr count] > 0 ? arr[0] : nil;
因为上面的方式很平常,有些人将它作为一个类增加到NSArray中,然后创建他们自己的firstObject方法。在Github上做一个快速搜索你可以看到过去这种方式是有多么的常用。
这个方法的问题是这个方法的名字必须是唯一的,否则的话这个方法所引发的问题无法预估。请确保检查你是否有任何自定义的代码在NSArray上实现了firstObject,如果有的话看看它是否是必须的,不是必须的话就把它全部移除。
6.增加了instancetype
instancetype让iOS7API变得更加难懂。苹果改变了大部分 initializer和简易构造函数(convenience constructors),用instancetype代替id作返回类型。但是这个instancetype是什么呢?
instancetype用来在声明一个方法时告诉编译器其返回类型,它表示返回调用该方法的类的对象。这比之前返回id的通常做法要好,编译 器可以对返回类型做一些检查,如果出现错误,在编译时就能提醒你,而不是在程序运行时发生崩溃。同时,在调用子类方法时,使用它还可以省去对返回值的强制 类型转换,编译器能够正确推断方法的返回值类型。
要说到instancetaype的缺点和优点吗?基本上,在任何可能的情况下都可以使用它。
如果需要更多关于instancetype的信息,你可以看看这篇文章。
7.设置UIImage的渲染模式:UIImage.renderingMode
着色(Tint Color)是iOS7界面中的一个重大改变,你可以设置一个UIImage在渲染时是否使用当前视图的Tint Color。UIImage新增了一个只读属性:renderingMode,对应的还有一个新增方 法:imageWithRenderingMode:,它使用UIImageRenderingMode枚举值来设置图片的renderingMode属 性。该枚举中包含下列值:
- UIImageRenderingModeAutomatic // 根据图片的使用环境和所处的绘图上下文自动调整渲染模式。
- UIImageRenderingModeAlwaysOriginal // 始终绘制图片原始状态,不使用Tint Color。
- UIImageRenderingModeAlwaysTemplate // 始终根据Tint Color绘制图片,忽略图片的颜色信息。
renderingMode属性的默认值是UIImageRenderingModeAutomatic,即UIImage是否使用Tint Color取决于它显示的位置。其他情况可以看下面的图例
以下的代码说明了使用一个既定的rendering模式创建图片是多么简单:
- UIImage *img = [UIImage imageNamed:@"myimage"];
- img = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
8.tintcolor VS barTintColor
iOS7中你可以使用一个给定的颜色,甚至是记入颜色主题来给整个app着色,帮助你的app脱颖而出。设置app的tint color很简答,只要使用UIView的新属性tintColor即可。
这个属性是否听上去很熟悉呢?应该很熟悉,有些类,比如说UINaviagtionBar,UISearchBar,UITabBar以及UIToolbar已经有了这么命名的属性。他们现在有了一个新的属性:barTintColor。
为了避免使用新属性的时候犯错误,如果你的appp需要支持iOS6以前的系统的时候,请检查一下。
- UINavigationBar *bar = self.navigationController.navigationBar;
- UIColor *color = [UIColor greenColor];
- if ([bar respondsToSelector:@selector(setBarTintColor:)]) { // iOS 7+
- bar.barTintColor = color;
- } else { // what year is this? 2012?
- bar.tintColor = color;
- }
9.去掉了纹理颜色
纹理颜色?对,不再使用他们了。不能再创建可以展现纹理的颜色。根据UIInterface.h文件中的注释,-[UIColor groupTableViewBackgroundColor]应该是要在iOS6当中即被删除了,但是它仅仅只是不像之前那样返回纹理颜色了。然而,以 下的颜色在iOS7当中被删除了:
- + (UIColor *)viewFlipsideBackgroundColor;
- + (UIColor *)scrollViewTexturedBackgroundColor;
- + (UIColor *)underPageBackgroundColor;
10.UIButtonTypeRoundRect被UIButtonTypeSystem取代了
在iOS开发刚开始就陪伴着你的老朋友现在也被删除了,它就是UIButtonTypeRoundRect ,被新的UIButtonTypeSystem取代了。
非常好的消息:新功能
如果每次iOS系统的发布都没有一些新的功能会是什么样子?这些新功能相信大部分开发者已经知道了,你可能会发现一些新颖的方式将它们整合到你的app中去!
11.检查无线路由是否可用
定制一个视频播放器的能力在iOS版本每次的发布中一直有所进步。比如说,在iOS6之前,你不能在MPVolumeView中改变AirPlay的icon。
在iOS7当中,你可以通过AirPlay,蓝牙或是其他的虚线机制了解是否有一个远程的设备可用。了解它的话,就可以让你的app在恰当的时候做恰当的事,比如说,在没有远程设备的时候就不显示AirPlay的icon。
以下是新增加到MPVolumeView的新属性和推送
- @property (nonatomic, readonly) BOOL wirelessRoutesAvailable; // 是否有设备可以连接的无线线路?
- @property (nonatomic, readonly) BOOL wirelessRouteActive; // 设备现在是否连接上了网络
- NSString *const MPVolumeViewWirelessRoutesAvailableDidChangeNotification;
- NSString *const MPVolumeViewWirelessRouteActiveDidChangeNotification;
12.了解蜂窝网络
在iOS7之前,是使用Reachability来检测设备是否连接到WWAN或是Wifi的。iOS7在这个基础上更进了一步,它会告诉你的 设备连接上的是那种蜂窝网络,比如说是Edge网络,HSDPA网络,或是LTE网络。告诉用户他们连接上的是哪种网络可以优化用户体验,因为这样他们会 知道网速如何,不会去请求需要高网速的网络请求。
这是CTTelephonyNetworkInfo的部分功能,它是CoreTelephony框架的一部分。iOS7还增加了 currentRadioAccessTechnology属性和 CTRadioAccessTechnologyDidChangeNotification到这个类。还有一些新的字符串常量来定义可能的值,比如说是 CTRadioAccessTechnologyLTE。
以下代码告诉你在app delegate中如何使用这个新功能:
- @import CoreTelephony.CTTelephonyNetworkInfo; // new modules syntax!
- @interface AppDelegate ()
- // we need to keep a reference to the CTTelephonyNetworkInfo object, otherwise the notifications won’t be fired!
- @property (nonatomic, strong) CTTelephonyNetworkInfo *networkInfo;
- @end
- @implementation ViewController
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- // whatever stuff your method does…
- self.networkInfo = [[CTTelephonyNetworkInfo alloc] init];
- NSLog(@”Initial cell connection: %@”, self.networkInfo.currentRadioAccessTechnology);
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(radioAccessChanged) name:
- CTRadioAccessTechnologyDidChangeNotification object:nil];
- // whatever stuff your method does…
- }
- - (void)radioAccessChanged {
- NSLog(@”Now you’re connected via %@”, self.networkInfo.currentRadioAccessTechnology);
- }
- @end
注意:研究一下CTTelephonyNetworkInfo.h 文件来看看是否有其他无线网络类型的的字符串常量。如果设备没有连上的话,currentRadioAccessTechnology 则会返回nil。
13.通过iCloud同步用户设备的密码
iOS7以及Mavericks增加了iCloud Keychain来提供密码,以及iCloud中一些敏感数据的同步。开发者可以通过keychain中的kSecAttrSynchronizable key来遍历dictionary对象。
由于直接处理keychain比较难,封装库提供了一个简单的处理keychain的方法。SSKeychain封装库可能是最有名的的一个,作为一种福利,现在它支持在iCloud同步。
以下代码片段显示了如何使用SSKeychain:
- #import <SSKeychain.h>
- - (BOOL)saveCredentials:(NSError **)error {
- SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
- query.password = @”MySecretPassword”;
- query.service = @”MyAwesomeService”;
- query.account = @”John Doe”;
- query.synchronizable = YES;
- return [query save:&error];
- }
- - (NSString *)savedPassword:(NSError **)error {
- SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
- query.service = @”MyAwesomeService”;
- query.account = @”John Doe”;
- query.synchronizable = YES;
- query.password = nil;
- if ([query fetch:&error]) {
- return query.password;
- }
- return nil;
- }
不要忘记CocoaPods是快速便捷安装SSKeychian的好方法。
14.使用NSAttributedString显示HTML
在app中使用Webviews有时会让人非常沮丧,即使只是显示少量的HTMLneirong ,Webviews也会消耗大量的内容。现在iOS7让这些变得简单了,你可以从用少量代码在HTML文件中创建一个NSAttributedString,比如:
- NSString *html = @”<bold>Wow!</bold> Now <em>iOS</em> can create <h3>NSAttributedString</h3> from HTMLs!”;
- NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
- NSAttributedString *attrString = [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding]
- options:options documentAttributes:nil error:nil];
- 现在你可以在任意的UIKit对象上使用NSAttributedString 了,比如说是一个UILabel或是一个UITextField,见以下代码:
- #import <SSKeychain.h>
- - (BOOL)saveCredentials:(NSError **)error {
- SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
- query.password = @”MySecretPassword”;
- query.service = @”MyAwesomeService”;
- query.account = @”John Doe”;
- query.synchronizable = YES;
- return [query save:&error];
- }
- - (NSString *)savedPassword:(NSError **)error {
- SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
- query.service = @”MyAwesomeService”;
- query.account = @”John Doe”;
- query.synchronizable = YES;
- query.password = nil;
- if ([query fetch:&error]) {
- return query.password;
- }
- return nil;
注意:NSHTMLTextDocumentType 只是NSDocumentTypeDocumentAttribute key一种可能的值。你还可以使用NSPlainTextDocumentType,NSRTFTextDocumentType或是 NSRTFDTextDocumentType。
你还可以从NSAttributedString中创建一个HTML字符串,如下:
- NSAttributedString *attrString; // from previous code
- NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
- NSData *htmlData = [attrString dataFromRange:NSMakeRange(0, [attrString length]) documentAttributes:options error:nil];
- NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
现在你估计在app中会更多的使用HTML了。
15.使用原生的Base64
Base64是使用ASCII码显示二进制数据的一种流行方法。直到现在,开发者还不得不使用开源的工具来编码解码Base64的内容。
现在iOS7引入了以下四种新的NSData方法来操作Base64编码的数据:
- // From NSData.h
- /* Create an NSData from a Base-64 encoded NSString using the given options. By default, returns nil when the input is not recognized
- as valid Base-64.
- */
- - (id)initWithBase64EncodedString:(NSString *)base64String options:(NSDataBase64DecodingOptions)options;
- /* Create a Base-64 encoded NSString from the receiver’s contents using the given options.
- */
- - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)options;
- /* Create an NSData from a Base-64, UTF-8 encoded NSData. By default, returns nil when the input is not recognized as valid Base-64.
- */
- - (id)initWithBase64EncodedData:(NSData *)base64Data options:(NSDataBase64DecodingOptions)options;
- /* Create a Base-64, UTF-8 encoded NSData from the receiver’s contents using the given options.
- */
- - (NSData *)base64EncodedDataWithOptions:(NSDataBase64EncodingOptions)options;
这些方法可以帮助你轻易的将NSData对象转化为Base64,或者将Base64转化为NSData object。见以下的例子:
- NSData* sampleData = [@"Some sample data" dataUsingEncoding:NSUTF8StringEncoding];
- NSString * base64String = [sampleData base64EncodedStringWithOptions:0];
- NSLog(@”Base64-encoded string is %@”, base64String); // prints ”U29tZSBzYW1wbGUgZGF0YQ==”
- NSData* dataFromString = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
- NSLog(@”String is %@”,[NSString stringWithUTF8String:[dataFromString bytes]]); // prints ”String is Some sample data”
如果你需要支持iOS6或者更早以前的系统,你可以使用以下两个方法:
- /* These methods first appeared in NSData.h on OS X 10.9 and iOS 7.0. They are deprecated in the same releases in favor of
- the methods in the <code>NSDataBase64Encoding</code> category. However, these methods have existed for several releases, so
- they may be used for applications targeting releases prior to OS X 10.9 and iOS 7.0.
- */
- - (id)initWithBase64Encoding:(NSString *)base64String;
- - (NSString *)base64Encoding;
16.使用UIApplicationUserDidTakeScreenshotNotification来检查截图
在iOS7之前,像Snapshot或是Facebook Poke这样的app是使用一些很精巧的方法来检测用户是否有截图。然而,iOS7提供一个崭新的推送方 法:UIApplicationUserDidTakeScreenshotNotification。只要像往常一样订阅即可知道什么时候截图了。
注意:UIApplicationUserDidTakeScreenshotNotification 将会在截图完成之后显示。现在在截图截取之前无法得到通知。希望苹果会在iOS8当中增加 UIApplicationUserWillTakeScreenshotNotification。
17.实现多语言语音合成
如果可以让app说话会不会很好呢?iOS7加入了两个新类:AVSpeechSynthesizer 以及AVSpeechUtterance。这两个类可以给你的app发声。很有意思不是吗?有多种语言可供选择——Siri不会说的语言也有,比如说巴西葡萄牙语。
使用这两个类给app提供语言合成的功能非常简单。AVSpeechUtterance 代表你想说什么,如何说。AVSpeechSynthesizer 用来发出这些声音,见以下代码片段:
- AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];
- AVSpeechUtterance *utterance =
- [AVSpeechUtterance speechUtteranceWithString:@"Wow, I have such a nice voice!"];
- utterance.rate = AVSpeechUtteranceMaximumSpeechRate / 4.0f;
- utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"]; // defaults to your system language
- [synthesizer speakUtterance:utterance];
18.使用了新的UIScreenEdgePanGestureRecognizer
UIScreenEdgePanGestureRecognizer 继承自UIPanGestureRecognizer ,它可以让你从屏幕边界即可检测手势。
使用新的手势识别器很简单,见以下:
- UIScreenEdgePanGestureRecognizer *recognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:
- @selector(handleScreenEdgeRecognizer:)];
- recognizer.edges = UIRectEdgeLeft; // accept gestures that start from the left; we’re probably building another hamburger menu!
- [self.view addGestureRecognizer:recognizer];
19.使用UIScrollViewKeyboardDismissMode实现了Message app的行为
像Messages app一样在滚动的时候可以让键盘消失是一种非常好的体验。然而,将这种行为整合到你的app很难。幸运的是,苹果给UIScrollView添加了一个很好用的属性keyboardDismissMode,这样可以方便很多。
现在仅仅只需要在Storyboard中改变一个简单的属性,或者增加一行代码,你的app可以和办到和Messages app一样的事情了。
这个属性使用了新的UIScrollViewKeyboardDismissMode enum枚举类型。这个enum枚举类型可能的值如下:
- UIScrollViewKeyboardDismissModeNone // the keyboard is not dismissed automatically when scrolling
- UIScrollViewKeyboardDismissModeOnDrag // dismisses the keyboard when a drag begins
- UIScrollViewKeyboardDismissModeInteractive // the keyboard follows the dragging touch off screen, and may be
- pulled upward again to cancel the dismiss
以下是让键盘可以在滚动的时候消失需要设置的属性:
20.使用Core Image来检测眨眼以及微笑
iOS给Core Image增加了两种人脸检测功能:CIDetectorEyeBlink以及CIDetectorSmile。这也就是说你现在可以在照片中检测微笑以及眨眼。
以下是在app中使用它的方法:
- UIImage *image = [UIImage imageNamed:@"myImage"];
- CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
- context:nil
- options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
- NSDictionary *options = @{ CIDetectorSmile: @YES, CIDetectorEyeBlink: @YES };
- NSArray *features = [detector featuresInImage:image.CIImage options:options];
- for (CIFaceFeature *feature in features) {
- NSLog(@”Bounds: %@”, NSStringFromCGRect(feature.bounds));
- if (feature.hasSmile) {
- NSLog(@”Nice smile!”);
- } else {
- NSLog(@”Why so serious?”);
- }
- if (feature.leftEyeClosed || feature.rightEyeClosed) {
- NSLog(@”Open your eyes!”);
- }
- }
21.给UITextView增加了链接
现在在iOS添加你自己的Twitter账户更加简单了,现在你可以给一个NSAttributedString增加链接了,然后当它被点击的时候唤起一个定制的action。
首先,创建一个NSAttributedString然后增加给它增加一个NSLinkAttributeName 属性,见以下:
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@”This is an example by @marcelofabri_”];
- [attributedString addAttribute:NSLinkAttributeName
- value:@"username://marcelofabri_"
- range:[[attributedString string] rangeOfString:@”@marcelofabri_”]];
- NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor],
- NSUnderlineColorAttributeName: [UIColor lightGrayColor],
- NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};
- // assume that textView is a UITextView previously created (either by code or Interface Builder)
- textView.linkTextAttributes = linkAttributes; // customizes the appearance of links
- textView.attributedText = attributedString;
- textView.delegate = self;
这样就可以让链接在文本中显示。然而,你也可以控制当链接被点击的时候会发生什么,实现这个可以使用UITextViewDelegate协议的新的shouldInteractWithURL方法,就像这样:
- - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
- if ([[URL scheme] isEqualToString:@”username”]) {
- NSString *username = [URL host];
- // do something with this username
- // …
- return NO;
- }
- return YES; // let the system open this URL
- }