引言
App跳转到设置界面这个话题看似简单,实则让人又爱又恨,爱其应用简单带来无穷的方便,恨其反复无常难以觅其宗旨。
需求
从应用跳到系统设置里的设置界面有这个需求存在吗?答案是肯定的。比如以下两个例子:
在没有网的状态下,你可能想提醒用户去设置界面连接WiFi。如果不能跳到WiFi界面,只能在APP里面做出文字提示。这样很多小白用户可能不会看提示,只会觉得APP没有做好。
还有一种情况,做智能家居的APP,智能硬件设备自带WiFi(局域网)。如果用户没有连接设备的WiFi进入APP时,需要提示用户去设置界面连接WiFi。
以上这两种情况只是举个例子,这个小功能的用处还是很多的,大家可以自行探索。
实现
info里面设置
在项目中的info.plist中添加 URL types 并设置一项URL Schemes为prefs,如下图:
实现代码(iOS10以下可用)
NSURL *url = [NSURL URLWithString:@"prefs:root=WIFI"];
if ([[UIApplication sharedApplication] canOpenURL:url])
{
[[UIApplication sharedApplication] openURL:url];
}
iOS10 新变化
作为UIApplication单例对象的方法 openURL: 在iOS开发中经常用来实现在当前应用打开外部链接的需求比如跳转到其他应用,跳转应用隐私设置界面;还有相关API canOpenURL: 进行跳转前判断.而这个古老(iOS2时出现)的 openURL 方法将被现今iOS10出现的新API openURL:options:completionHandler: 所替代.
新API的官方描述
首先来看官方文档对iOS10的 What’s New in iOS UIKit部分对新API的描述
The new UIApplication method openURL:options:completionHandler:, which is executed asynchronously and calls the specified completion handler on the main queue (this method replaces openURL:).
这段话清楚地指明了新API的两个特点:异步执行open操作和主线程执行回调.
新API的代码描述
接下来再看看具体的接口代码,可以看出判断链接打开是否成功的方式也从以前的根据 openURL
调用return的布尔值变成了查询completion的success参数;
old- (BOOL)openURL:(NSURL*)url
new- (void)openURL:(NSURL*)url options:(NSDictionary<NSString *, id> *)options completionHandler:(void (^ __nullable)(BOOL success))completion
并且额外地提供了一个用来URL处理的options字典参数,没有限定值时就要传空字典,就像下方代码一样调用.
[[UIApplication sharedApplication] openURL:URL options:@{} completionHandler:nil];
options目前可传入参数Key在UIApplication头文件只有一个:UIApplicationOpenURLOptionUniversalLinksOnly,其对应的Value为布尔值,默认为False.如该Key对应的Value为True,那么打开所传入的Universal Link时,只允许通过这个Link所代表的iOS应用跳转的方式打开这个链接,否则就会返回success为false,也就是说只有安装了Link所对应的App的情况下才能打开这个Universal Link,而不是通过启动Safari方式打开这个Link的代表的网站.
[application openURL:URL options:@{UIApplicationOpenURLOptionUniversalLinksOnly : @YES} completionHandler:nil];
注意:虽然有了新的Api方法但是iOS 10在应用自身上(除非通知栏)已经不允许任何跳转到系统设置的行为了,所以我们只能找替代方法了。
那么就有人提出疑问了,下面方法iOS10仍然可以跳转啊。
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
虽然可以正常跳转但只会跳到自身应用界面的系统设置,并没有达到我们的目的。
既然官方的正常的途径无法达到目的我们只能尝试私有API啦
使用MobileCoreServices.framework里的私有API:
- (BOOL)openSensitiveURL:(id)arg1 withOptions:(id)arg2;
头文件参考: LSApplicationWorkspace.h
使用方法:
//注意首字母改成了大写,prefs->Prefs
NSURL*url=[NSURL URLWithString:@"Prefs:root=Privacy&path=LOCATION"];
Class LSApplicationWorkspace = NSClassFromString(@"LSApplicationWorkspace");
[[LSApplicationWorkspace performSelector:@selector(defaultWorkspace)] performSelector:@selector(openSensitiveURL:withOptions:) withObject:url withObject:nil];
MobileCoreServices.framework 不是私有库,所以直接使用 performSelector: 即可调用私有API。
注意
- iOS10的系统URLScheme改成了首字母大写,使用小写的方式会无法打开。
- 使用私有API的app无法通过App Store审核。你也可以尝试把私有类名和selector字符串混淆一下,绕过审核。例如 这位仁兄 用ASCII混淆的方法:
- (void)returnToSystemWIFISettingMenu{
NSString * defaultWork = [self getDefaultWork];
NSString * privateMethod = [self privateMethod];
NSURL*url=[NSURL URLWithString:@"Prefs:root=WIFI"];
Class LSApplicationWorkspace = NSClassFromString(@"LSApplicationWorkspace");
[[LSApplicationWorkspace performSelector:NSSelectorFromString(defaultWork)] performSelector:NSSelectorFromString(privateMethod) withObject:url withObject:nil];
}
- (NSString *) getDefaultWork{
NSData *dataOne = [NSData dataWithBytes:(unsigned char []){0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x57,0x6f,0x72,0x6b,0x73,0x70,0x61,0x63,0x65} length:16];
NSString *method = [[NSString alloc] initWithData:dataOne encoding:NSASCIIStringEncoding];
return method;
}
- (NSString *)privateMethod {
NSData *dataOne = [NSData dataWithBytes:(unsigned char []){0x6f, 0x70, 0x65, 0x6e, 0x53, 0x65, 0x6e, 0x73, 0x69,0x74, 0x69,0x76,0x65,0x55,0x52,0x4c} length:16];
NSString *keyone = [[NSString alloc] initWithData:dataOne encoding:NSASCIIStringEncoding];
NSData *dataTwo = [NSData dataWithBytes:(unsigned char []){0x77,0x69,0x74,0x68,0x4f,0x70,0x74,0x69,0x6f,0x6e,0x73} length:11];
NSString *keytwo = [[NSString alloc] initWithData:dataTwo encoding:NSASCIIStringEncoding];
NSString *method = [NSString stringWithFormat:@"%@%@%@%@",keyone,@":",keytwo,@":"];
return method;
}
不过,还是不建议使用私有API,因为它是不可靠的。
最后附上常用参数:
英文 | 参数 | 中文 |
---|---|---|
About | prefs : root=General&path=About | 关于 |
Accessibility | prefs:root=General&path=ACCESSIBILITY | 辅助功能 |
Airplane Mode On | prefs:root=AIRPLANE_MODE | 飞行模式 |
Auto-Lock | prefs:root=General&path=AUTOLOCK | 锁屏 |
Brightness | prefs:root=Brightness | 亮度 |
Bluetooth | prefs:root=General&path=Bluetooth | 蓝牙 |
Date & Time | prefs:root=General&path=DATE_AND_TIME | 日期 |
FaceTime | prefs:root=FACETIME | FaceTime |
General | prefs:root=General | 通用 |
iCloud Storage & Backup | prefs:root=CASTLE&path=STORAGE_AND_BACKUP | 云存储与备份 |
iCloud | prefs:root=CASTLE | iCloud云 |
International | prefs:root=General&path=INTERNATIONAL | International |
Location Services | prefs:root=LOCATION_SERVICES | 位置服务 |
Music | prefs:root=MUSIC | 音乐 |
Music Equalizer | prefs:root=MUSIC&path=EQ | 音乐均衡器 |
Music Volume Limit | prefs:root=MUSIC&path=VolumeLimit | 音乐音量限制 |
Network | prefs:root=General&path=Network | 网络 |
Nike + iPod | prefs:root=NIKE_PLUS_IPOD | Nike + iPod |
Notes | prefs:root=NOTES | 备忘录 |
Notification | prefs:root=NOTIFICATIONS_ID | 通知 |
Phone | prefs:root=Phone | 电话 |
Photos | prefs:root=Photos | 照片 |
Profile | prefs:root=General&path=ManagedConfigurationList | Profile |
Reset | prefs:root=General&path=Reset | 重启 |
Safari | prefs:root=Safari | 浏览器 |
Software Update | prefs:root=General&path=SOFTWARE_UPDATE_LINK | 软件更新 |
Store | prefs:root=STORE | 应用商店 |
prefs:root=TWITTER | ||
Usage | prefs:root=General&path=USAGE | 用量 |
VPN | prefs:root=General&path=Network/VPN | VPN |
Wallpaper | prefs:root=Wallpaper | 壁纸 |
Wi-Fi | prefs:root=WIFI | 无线网络 |
Internet_tethering | prefs:root= INTERNET_TETHERING | 网络共享 |