iOS面試題:iOS13有哪些適配

iOS 13 支持適配的機(jī)型

iPhone X、iPhone XR、iPhone XS、iPhone XS Max
iPhone 8、iPhone 8 Plus
iPhone 7、iPhone 7 Plus
iPhone 6s、iPhone 6s Plus
iPhone SE
iPod touch (第七代)

一、UI層面
1.Dark Mode - 審核強(qiáng)制要求適配黑夜模式。
2.Sign In with Apple - 提供第三方登錄
3.模態(tài)彈出默認(rèn)交互改變(必須)
4.UISegmentedControl 默認(rèn)樣式改變(必須)
5.h5的適配,內(nèi)嵌WebView,一些圖片路徑,文件路徑,不用寫(xiě)絕對(duì)路徑,直接寫(xiě)文件名字即可

二、代碼層面
1.私有方法 KVC 不允許使用:valueForKey、setValue:forKey:(必須)
2.推送的 deviceToken 獲取到的格式發(fā)生變化(必須)
3.UISearchBar 黑線處理導(dǎo)致崩潰
4.使用 UISearchDisplayController 導(dǎo)致崩潰
5.模態(tài)彈出默認(rèn)交互改變(必須)
6.MPMoviePlayerController 在iOS13被棄用
7.LaunchImage 被棄用(必須)
8.Xcode 11 創(chuàng)建的工程在低版本設(shè)備上運(yùn)行黑屏
9.使用 @available 導(dǎo)致舊版本 Xcode 編譯出錯(cuò)。(必須)
10.textfield.leftview(必須)
11.NSAttributedString優(yōu)化
12.TabBar紅點(diǎn)偏移
13.廢棄UIWebView(必須)
14.WKWebView 中測(cè)量頁(yè)面內(nèi)容高度的方式變更
15.使用MJExtension 中處理NSNull的不同(必須)
16.StatusBar 與之前版本不同(必須)
17.UIActivityIndicatorView(必須)
18.藍(lán)牙權(quán)限需要申請(qǐng)
19.CNCopyCurrentNetworkInfo
20.下面這種獲取網(wǎng)絡(luò)狀態(tài)用到KVC的方法會(huì)發(fā)生崩潰
21.MPRemoteCommandCenter 音頻后臺(tái)播放控制器

一、UI層面

注:必須適配的點(diǎn)以“(必須)”標(biāo)出,有的是看項(xiàng)目需求,大家可忽略

1.Dark Mode - 審核強(qiáng)制要求適配黑夜模式。

iOS 13 推出暗黑模式,UIKit 提供新的系統(tǒng)顏色和 api 來(lái)適配不同顏色模式,xcassets 對(duì)素材適配也做了調(diào)整,官方具體適配可見(jiàn): Implementing Dark Mode on iOS
適配方案:

image

參考鏈接:
https://mp.weixin.qq.com/s/qliFbqRdkkE30vslojfJCA
https://juejin.im/post/5cf6276be51d455a68490b26

2.Sign In with Apple - 提供第三方登錄

Sign In with Apple will be available for beta testing this summer. It will be required as an option for users in apps that support third-party sign-in when it is commercially available later this year.
如果你的應(yīng)用支持使用第三方登錄,那么就必須加上蘋(píng)果新推出的登錄方式:Introducing Sign In with Apple。目前蘋(píng)果只在 News and Updates 上提到正式發(fā)布時(shí)要求加上,具體發(fā)布時(shí)間還沒(méi)確定。

3.模態(tài)彈出默認(rèn)交互改變(必須)

在 iOS 13 中此枚舉值直接成為了模態(tài)彈出的默認(rèn)值,因此 presentViewController 方式打開(kāi)視圖是如下的視差效果,默認(rèn)是下滑返回。

image

iOS13下仍然可以做到全屏彈出,運(yùn)行代碼發(fā)現(xiàn)presentViewController和之前彈出的樣式不一樣

會(huì)出現(xiàn)這種情況是主要是因?yàn)槲覀冎皩?duì)UIViewController里面的一個(gè)屬性,即modalPresentationStyle(該屬性是控制器在模態(tài)視圖時(shí)將要使用的樣式)沒(méi)有設(shè)置需要的類型。在iOS13中modalPresentationStyle的默認(rèn)改為UIModalPresentationAutomatic,而在之前默認(rèn)是UIModalPresentationFullScreen。

ViewController *vc = [[ViewController alloc] init];
vc.title = @"presentVC";
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
nav.modalPresentationStyle = UIModalPresentationFullScreen;
[self.window.rootViewController presentViewController:nav animated:YES completion:nil];

注意:如果你某個(gè)控制器想使用卡片模式,需要注意你這個(gè)控制器底部是否有控件。卡片式的底部的控件容易被遮擋。

比方說(shuō)TZPhotoPickerController 這個(gè)常用的開(kāi)源相冊(cè)控件,當(dāng)選擇照片時(shí)底部的確定按鈕就被遮擋,無(wú)法選中.

4.UISegmentedControl 默認(rèn)樣式改變(必須)

默認(rèn)樣式變?yōu)榘椎缀谧郑绻O(shè)置修改過(guò)顏色的話,頁(yè)面需要修改。

image

原本設(shè)置選中顏色的 tintColor 已經(jīng)失效,新增了 selectedSegmentTintColor 屬性用以修改選中的顏色。
Web Content適配

 if ( @available(iOS 13.0, *)) {
           self.segmentedControl.selectedSegmentTintColor = [UIColor clearColor];

       }else
       {
           self.segmentedControl.tintColor = [UIColor clearColor];

       }

5.h5的適配,參考鏈接:

內(nèi)嵌WebView,一些圖片路徑,文件路徑,不用寫(xiě)絕對(duì)路徑,直接寫(xiě)文件名字即可
https://blog.csdn.net/u012413955/article/details/92198556

二、代碼層面

1.私有方法 KVC 不允許使用(必須)

在 iOS 13 中不再允許使用 valueForKey、setValue:forKey: 等方法獲取或設(shè)置私有屬性,雖然編譯可以通過(guò),但是在運(yùn)行時(shí)會(huì)直接崩潰,并提示一下崩潰信息:

// 使用的私有方法
[_textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];

// 崩潰提示信息
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug' 

解決方案一:使用其他方法:(建議使用此種方法,因?yàn)榈诙N方法不知道能否過(guò)審)

// 替換的方案
_textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"輸入"attributes:@{NSForegroundColorAttributeName: [UIColor redColor]}]; 

解決方案二:去掉keypath中的“_”
如果需要修改UISearchBar的placeholder,需要獲取其searchTextField,可用category實(shí)現(xiàn):

@implementation UISearchBar (SearchTextField)

- (UITextField *)atu_searchTextField{
    if ([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {
        //判斷Xcode版本
#ifdef __IPHONE_13_0
        return self.searchTextField;
#else
        return [self valueForKey:@"searchTextField"];
#endif
    }

    //嘗試過(guò)遍歷subviews來(lái)找到,但是subviews中并不包含searchField!沒(méi)有找到更好的辦法
    return [self valueForKey:@"searchField"];
}

@end

大家可以集思廣益

2.推送的 deviceToken 獲取到的格式發(fā)生變化(必須)

原本可以直接將 NSData 類型的 deviceToken 轉(zhuǎn)換成 NSString 字符串,然后替換掉多余的符號(hào)即可:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [deviceToken description];
    for (NSString *symbol in @[@" ", @"<", @">", @"-"]) {
        token = [token stringByReplacingOccurrencesOfString:symbol withString:@""];
    }
    NSLog(@"deviceToken:%@", token);
}

在 iOS 13 中,這種方法已經(jīng)失效,NSData類型的 deviceToken 轉(zhuǎn)換成的字符串變成了:

{length = 32, bytes = 0xd7f9fe34 69be14d1 fa51be22 329ac80d ... 5ad13017 b8ad0736 } 

需要進(jìn)行一次數(shù)據(jù)格式處理,參考友盟的做法,可以適配新舊系統(tǒng),獲取方式如下:

#include <arpa/inet.h>
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    if (![deviceToken isKindOfClass:[NSData class]]) return;
    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                          ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                          ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                          ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
    NSLog(@"deviceToken:%@", hexToken);
}

3.UISearchBar 黑線處理導(dǎo)致崩潰

之前為了處理搜索框的黑線問(wèn)題,通常會(huì)遍歷 searchBar 的 subViews,找到并刪除 UISearchBarBackground,在 iOS13 中這么做會(huì)導(dǎo)致 UI 渲染失敗,然后直接崩潰,崩潰信息如下:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Missing or detached view for search bar layout'

解決辦法是設(shè)置 UISearchBarBackground 的 layer.contents 為 nil:

for (UIView *view in _searchBar.subviews.lastObject.subviews) {
    if ([view isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
        // [view removeFromSuperview];
        view.layer.contents = nil;
        break;
    }
} 

4.使用 UISearchDisplayController 導(dǎo)致崩潰

在 iOS 8 之前,我們?cè)?UITableView 上添加搜索框需要使用 UISearchBar + UISearchDisplayController 的組合方式,而在 iOS 8 之后,蘋(píng)果就已經(jīng)推出了 UISearchController 來(lái)代替這個(gè)組合方式。在 iOS 13 中,如果還繼續(xù)使用 UISearchDisplayController 會(huì)直接導(dǎo)致崩潰,崩潰信息如下:

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'UISearchDisplayController is no longer supported when linking against this version of iOS. Please migrate your application to UISearchController.' 

另外說(shuō)一下,在 iOS 13 中終于可以獲取直接獲取搜索的文本框:

_searchBar.searchTextField.text = @“search";

5.模態(tài)彈出默認(rèn)交互改變(必須)

在iOS13中運(yùn)行代碼發(fā)現(xiàn)presentViewController和之前彈出的樣式不一樣。是因?yàn)橹皩?duì)UIViewController里面的一個(gè)屬性,即modalPresentationStyle(該屬性是控制器在模態(tài)視圖時(shí)將要使用的樣式)沒(méi)有設(shè)置需要的類型。在iOS13中modalPresentationStyle的默認(rèn)改為UIModalPresentationAutomatic,而在之前默認(rèn)是UIModalPresentationFullScreen。

注意:如果你某個(gè)控制器想使用卡片模式,需要注意你這個(gè)控制器底部是否有控件。卡片式的底部的控件容易被遮擋。比方說(shuō)TZPhotoPickerController 這個(gè)常用的開(kāi)源相冊(cè)控件。當(dāng)選擇照片時(shí)底部的確定按鈕就被遮擋。無(wú)法選中

如果需要做成全屏顯示的界面,需要手動(dòng)設(shè)置彈出樣式:

- (UIModalPresentationStyle)modalPresentationStyle {
    return UIModalPresentationFullScreen;
} 
//或者在present之前:
ctr.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:ctr animated:animated completion:completion];

如果項(xiàng)目里面有大量的位置使用了presentViewController,然后你不想每個(gè)位置都顯式的手動(dòng)修改,推薦這篇文章https://juejin.im/post/5d5f96866fb9a06b0517f78c
里面的方法,原理是:

通過(guò)runtime修改ctr.modalPresentationStyle的默認(rèn)值為UIModalPresentationFullScreen(原默認(rèn)值為UIModalPresentationPageSheet)如果需要禁止自動(dòng)修改默認(rèn)值
ctr.LL_automaticallySetModalPresentationStyle = NO;

不太多的話還是建議老老實(shí)實(shí)的一個(gè)個(gè)手動(dòng)修改,以免以后官方api再次變動(dòng)。

有一點(diǎn)注意的是,ctr的生命周期方法調(diào)用情況會(huì)改變,假設(shè)有a,b兩個(gè)ctr,在a中present出b:

全屏present時(shí)(UIModalPresentationFullScreen)的方法調(diào)用順序:

a---viewWillDisappear:
b---viewWillAppear:
b---viewDidAppear:
a---viewDidDisappear:

dissmiss時(shí)的方法調(diào)用順序:

b---viewWillDisappear:
a---viewWillAppear:
a---viewDidAppear:
b---viewDidDisappear:

非全屏presnet時(shí)(UIModalPresentationPageSheet)的方法調(diào)用順序:

b---viewWillAppear:
b---viewDidAppear:

dissmiss時(shí)的方法調(diào)用順序:

b---viewWillDisappear:
b---viewDidDisappear:

可以看出,以UIModalPresentationPageSheet的方式來(lái)present/dismiss時(shí),分別少調(diào)用了a的兩個(gè)方法,如果之前在這個(gè)位置有相關(guān)的邏輯代碼,比如網(wǎng)絡(luò)請(qǐng)求,UI刷新,要注意

6.MPMoviePlayerController 在iOS13被棄用

在 iOS 9 之前播放視頻可以使用 MediaPlayer.framework 中的MPMoviePlayerController類來(lái)完成,它支持本地視頻和網(wǎng)絡(luò)視頻播放。但是在 iOS 9 開(kāi)始被棄用,如果在 iOS 13 中繼續(xù)使用的話會(huì)直接拋出異常:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.'

解決方案是使用 AVFoundation 里的 AVPlayer。

7.LaunchImage 被棄用(必須)

iOS 8 之前我們是在LaunchImage 來(lái)設(shè)置啟動(dòng)圖,但是隨著蘋(píng)果設(shè)備尺寸越來(lái)越多,我們需要在對(duì)應(yīng)的 aseets 里面放入所有尺寸的啟動(dòng)圖,這是非常繁瑣的一個(gè)步驟。因此在 iOS 8 蘋(píng)果引入了 LaunchScreen.storyboard,支持界面布局用的 AutoLayout + SizeClass ,可以很方便適配各種屏幕。
需要注意的是,蘋(píng)果在 Modernizing Your UI for iOS 13 section 中提到,從2020年4月開(kāi)始,所有支持 iOS 13 的 App 必須提供 LaunchScreen.storyboard,否則將無(wú)法提交到 App Store 進(jìn)行審批。

8.Xcode 11 創(chuàng)建的工程在低版本設(shè)備上運(yùn)行黑屏

使用 Xcode 11 創(chuàng)建的工程,運(yùn)行設(shè)備選擇 iOS 13.0 以下的設(shè)備,運(yùn)行應(yīng)用時(shí)會(huì)出現(xiàn)黑屏。這是因?yàn)?Xcode 11 默認(rèn)是會(huì)創(chuàng)建通過(guò) UIScene 管理多個(gè) UIWindow 的應(yīng)用,工程中除了 AppDelegate 外會(huì)多一個(gè) SceneDelegate.

image

這是為了 iPadOS 的多進(jìn)程準(zhǔn)備的,也就是說(shuō) UIWindow 不再是 UIApplication 中管理。但是舊版本根本沒(méi)有 UIScene,因此解決方案就是在 AppDelegate 的頭文件加上:

@property (strong, nonatomic) UIWindow *window;

9.使用 @available 導(dǎo)致舊版本 Xcode 編譯出錯(cuò)。(必須)

在 Xcode 11 的 SDK 工程的代碼里面使用了 @available 判斷當(dāng)前系統(tǒng)版本,打出來(lái)的包放在 Xcode 10 中編譯,會(huì)出現(xiàn)一下錯(cuò)誤:

Undefine symbols for architecture i386:
    "__isPlatformVersionAtLeast", referenced from:
        ...
ld: symbol(s) not found for architecture i386

從錯(cuò)誤信息來(lái)看,是 __isPlatformVersionAtLeast 方法沒(méi)有具體的實(shí)現(xiàn),但是工程里根本沒(méi)有這個(gè)方法。實(shí)際測(cè)試無(wú)論在哪里使用@available ,并使用 Xcode 11 打包成動(dòng)態(tài)庫(kù)或靜態(tài)庫(kù),把打包的庫(kù)添加到 Xcode 10 中編譯都會(huì)出現(xiàn)這個(gè)錯(cuò)誤,因此可以判斷是 iOS 13 的 @available 的實(shí)現(xiàn)中使用了新的 api。如果你的 SDK 需要適配舊版本的 Xcode,那么需要避開(kāi)此方法,通過(guò)獲取系統(tǒng)版本來(lái)進(jìn)行判斷:

if ([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {
    ...
}

另外,在 Xcode 10 上打開(kāi) SDK 工程也應(yīng)該可以正常編譯,這就需要加上編譯宏進(jìn)行處理:

#ifndef __IPHONE_13_0
#define __IPHONE_13_0 130000
#endif

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
...
#endif

10.textfield.leftview(必須)

如下方式,直接給 textfield.leftView 賦值一個(gè) UILabel 對(duì)象,他的寬高會(huì)被 sizeToFit,而不是創(chuàng)建時(shí)的值。

// left view label
UILabel *phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 63, 50)];
phoneLabel.text = @"手機(jī)號(hào)";
phoneLabel.font = [UIFont systemFontOfSize:16];
// set textfield left view
self.textfieldName.leftView = phoneLabel;

如所看到,實(shí)際leftview的width為59,height為19:

image

解決方法:嵌套一個(gè)UIView

// label
UILabel *phoneLabel = [[UILabel alloc] init];
phoneLabel.text = @"手機(jī)號(hào)";
phoneLabel.font = [UIFont systemFontOfSize:16];
[phoneLabel sizeToFit];
phoneLabel.centerY = 50/2.f;
// left view
UIView *leftView = [[UIView alloc] initWithFrame:(CGRect){0, 0, 63, 50}];
[leftView addSubview:phoneLabel];
// set textfield left view
self.textfieldName.leftView = leftView;

11.NSAttributedString優(yōu)化

對(duì)于UILabel、UITextField、UITextView,在設(shè)置NSAttributedString時(shí)也要考慮適配Dark Mode,否則在切換模式時(shí)會(huì)與背景色融合,造成不好的體驗(yàn)
不建議的做法

NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];

推薦的做法

// 添加一個(gè)NSForegroundColorAttributeName屬性
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor labelColor]};
NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];

12.TabBar紅點(diǎn)偏移

如果之前有通過(guò)TabBar上圖片位置來(lái)設(shè)置紅點(diǎn)位置,在iOS13上會(huì)發(fā)現(xiàn)顯示位置都在最左邊去了。遍歷UITabBarButton的subViews發(fā)現(xiàn)只有在TabBar選中狀態(tài)下才能取到UITabBarSwappableImageView,解決辦法是修改為通過(guò)UITabBarButton的位置來(lái)設(shè)置紅點(diǎn)的frame

13.廢棄UIWebView(必須)

UIWebView在12.0就已經(jīng)被廢棄,部分APP使用webview時(shí), 審核被拒

14.WKWebView 中測(cè)量頁(yè)面內(nèi)容高度的方式變更

iOS 13以前 document.body.scrollHeight
iOS 13中 document.documentElement.scrollHeight 兩者相差55 應(yīng)該是瀏覽器定義高度變了

15.使用MJExtension 中處理NSNull的不同(必須)

這個(gè)直接會(huì)導(dǎo)致Crash的在將服務(wù)端數(shù)據(jù)字典轉(zhuǎn)換為模型時(shí),如果遇到服務(wù)端給的數(shù)據(jù)為NSNull時(shí), mj_JSONObject,其中 class_copyPropertyList方法得到的屬性里,多了一種EFSQLBinding類型的東西,而且屬性數(shù)量也不準(zhǔn)確, 那就沒(méi)辦法了, 我只能改寫(xiě)這個(gè)方法了,這個(gè)組件沒(méi)有更新的情況下,寫(xiě)了一個(gè)方法swizzling掉把當(dāng)遇到 NSNull時(shí),直接轉(zhuǎn)為nil了。

有人問(wèn)這個(gè)方法怎么實(shí)現(xiàn),其實(shí)目前我們項(xiàng)目在ios13下面還沒(méi)遇到這種情況,發(fā)一個(gè)之前項(xiàng)目處理null的方法,在項(xiàng)目里面寫(xiě)一個(gè)NSObject的分類,添加下面的方法就可以了。大家請(qǐng)根據(jù)項(xiàng)目情況、數(shù)據(jù)格式修改下這個(gè)方法,MJ庫(kù)里面自己會(huì)進(jìn)行替換的:

- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property {
    //為了解決json字符串先賦值給oc字典后,類型轉(zhuǎn)換crash問(wèn)題,如:
    //json->oldValue:0
    //model中值為NSString類型
    //如果先將json轉(zhuǎn)為dic,dic中對(duì)應(yīng)value值為NSNumber類型,則向oldValue發(fā)送isEqualToString消息會(huì)crash
    id tempValue = oldValue;

    if ([property.type.code isEqualToString:@"NSString"]) {
        tempValue = [NSString stringWithFormat:@"%@", tempValue];

        if ([tempValue isKindOfClass:[NSNull class]] || tempValue == nil || [tempValue isEqual:[NSNull null]] ||  [tempValue isEqualToString:@"(null)"] ||  [tempValue isEqualToString:@"(\n)"] ) {
            return @"";
        }
    }
    if ([property.type.code isEqualToString:@"NSNumber"]) {
//        tempValue = [NSNumber numberWithFloat:[tempValue floatValue]];

        if ([tempValue isKindOfClass:[NSNull class]] || tempValue == nil || [tempValue isEqual:[NSNull null]] ||  [tempValue isEqualToString:@"(null)"] ||  [tempValue isEqualToString:@"(\n)"] ) {
            return @0;
        }
    }

    return tempValue;
}

16.StatusBar 與之前版本不同(必須)

目前狀態(tài)欄也增加了一種模式,由之前的兩種,變成了三種, 其中default由之前的黑色內(nèi)容,變成了會(huì)根據(jù)系統(tǒng)模式,自動(dòng)選擇當(dāng)前展示lightContent還是darkContent。

public enum UIStatusBarStyle : Int {
  case `default` // Automatically chooses light or dark content based on the user interface style

  @available(iOS 7.0, *)
  case lightContent // Light content, for use on dark backgrounds

  @available(iOS 13.0, *)
  case darkContent // Dark content, for use on light backgrounds
}
我們?cè)谑褂玫臅r(shí)候,就可以重寫(xiě)preferredStatusBarStyle的get方法:

override var preferredStatusBarStyle: UIStatusBarStyle{
    get{
        return .lightContent
    }
}

17.UIActivityIndicatorView(必須)

之前的 UIActivityIndicatorView 有三種 style 分別為 whiteLarge, white 和 gray,現(xiàn)在全部廢棄。
增加兩種 style 分別為 medium 和 large,指示器顏色用 color 屬性修改。

18.藍(lán)牙權(quán)限需要申請(qǐng)

CBCentralManager,iOS13以前,使用藍(lán)牙時(shí)可以直接用,不會(huì)出現(xiàn)權(quán)限提示,iOS13后,再使用就會(huì)提示了。 在info.plist里增加

<key>NSBluetoothAlwaysUsageDescription</key> 
<string>我們要一直使用您的藍(lán)牙</string>

在iOS13中,藍(lán)牙變成了和位置,通知服務(wù)等同樣的可以針對(duì)單個(gè)app授權(quán)的服務(wù)。

- (NSString*) getWifiSsid {
  if (@available(iOS 13.0, *)) {
    //用戶明確拒絕,可以彈窗提示用戶到設(shè)置中手動(dòng)打開(kāi)權(quán)限
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
      NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
      //使用下面接口可以打開(kāi)當(dāng)前應(yīng)用的設(shè)置頁(yè)面
      //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
      return nil;
    }
    CLLocationManager* cllocation = [[CLLocationManager alloc] init];
    if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
      //彈框提示用戶是否開(kāi)啟位置權(quán)限
      [cllocation requestWhenInUseAuthorization];
      usleep(50);
      //遞歸等待用戶選選擇
      return [self getWifiSsidWithCallback:callback];
    }
  }
  NSString *wifiName = nil;
  CFArrayRef wifiInterfaces = CNCopySupportedInterfaces();
  if (!wifiInterfaces) {
    return nil;
  }
  NSArray *interfaces = (__bridge NSArray *)wifiInterfaces;
  for (NSString *interfaceName in interfaces) {
    CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName));

    if (dictRef) {
      NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef;
      NSLog(@"network info -> %@", networkInfo);
      wifiName = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID];
      CFRelease(dictRef);
    }
  }
  CFRelease(wifiInterfaces);
  return wifiName;
}

19.CNCopyCurrentNetworkInfo

iOS13 以后只有開(kāi)啟了 Access WiFi Information capability,才能獲取到 SSID 和 BSSID wi-fi or wlan 相關(guān)使用變更
最近收到了蘋(píng)果的郵件,說(shuō)獲取WiFi SSID的接口CNCopyCurrentNetworkInfo 不再返回SSID的值。不仔細(xì)看還真會(huì)被嚇一跳,對(duì)物聯(lián)網(wǎng)的相關(guān)APP簡(jiǎn)直是炸彈。仔細(xì)看郵件還好說(shuō)明了可以先獲取用戶位置權(quán)限才能返回SSID。
注意:目本身已經(jīng)打開(kāi)位置權(quán)限,則可以直接獲取

20.下面這種獲取網(wǎng)絡(luò)狀態(tài)用到KVC的方法會(huì)發(fā)生崩潰


 UIApplication *app = [UIApplication sharedApplication];
    NSArray *children = nil;
    // 不能用 [[self deviceVersion] isEqualToString:@"iPhone X"] 來(lái)判斷,因?yàn)槟M器不會(huì)返回 iPhone X
    id statusBar = [app valueForKeyPath:@"statusBar"];
    if ([statusBar isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
        children = [[[statusBar valueForKey:@"statusBar"] valueForKey:@"foregroundView"] subviews];
    } else {
        children = [[statusBar valueForKey:@"foregroundView"] subviews];
    }
    NSString *state = [[NSString alloc] init];
    int netType = 0;
    for (id child in children) {
        if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
            //獲取到狀態(tài)欄
            netType = [[child valueForKeyPath:@"dataNetworkType"] intValue];
            switch (netType) {
                case 0:
                    state = @"none";
                    break;
                case 1:
                    state = @"2G";
                    break;
                case 2:
                    state = @"3G";
                    break;
                case 3:
                    state = @"4G";
                    break;
                case 5:
                    state = @"WIFI";
                    break;
                default:
                    break;
            }
        }
    }

21.MPRemoteCommandCenter 音頻后臺(tái)播放控制器

- (void)addTarget:(id)target action:(SEL)action; 在iOS 13中selector 一定要返回MPRemoteCommandHandlerStatus,要不然會(huì)閃退。

// Target-action style for adding handlers to commands.
// Actions receive an MPRemoteCommandEvent as the first parameter.
// Targets are not retained by addTarget:action:, and should be removed from the
// command when the target is deallocated.
//
// Your selector should return a MPRemoteCommandHandlerStatus value when
// possible. This allows the system to respond appropriately to commands that
// may not have been able to be executed in accordance with the application's
// current state.
- (void)addTarget:(id)target action:(SEL)action;
- (void)removeTarget:(id)target action:(nullable SEL)action;
- (void)removeTarget:(nullable id)target;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • iOS 13 支持適配的機(jī)型 iPhone X、iPhone XR、iPhone XS、iPhone XS Max...
    不成活不瘋魔閱讀 44,232評(píng)論 22 131
  • iOS 13 支持適配的機(jī)型 iPhone X、iPhone XR、iPhone XS、iPhone XS Max...
    你買票嗎閱讀 8,195評(píng)論 2 18
  • @TOC 3. IOS 最新系統(tǒng)適配問(wèn)題 蘋(píng)果官方資料: WWDC19視頻Xcode 11 beta 下載macO...
    孔雨露閱讀 2,980評(píng)論 0 12
  • 9月20號(hào)iOS 13 已正式發(fā)布,第一時(shí)間更新了最新系統(tǒng),網(wǎng)上對(duì)其用戶體驗(yàn)上的新特性的描述也很多,而對(duì)于開(kāi)發(fā)者來(lái)...
    杰小冷_4957閱讀 624評(píng)論 0 2
  • 真是計(jì)劃沒(méi)有變化快,剛說(shuō)完開(kāi)始7天運(yùn)動(dòng)計(jì)劃,昨天第一天就泡湯了。去設(shè)計(jì)院開(kāi)個(gè)會(huì)竟然也能開(kāi)到晚上7點(diǎn),有什么好說(shuō)的,...
    橢圓儀閱讀 162評(píng)論 0 0