項目中經過StatusBar來獲取手機當前狀態,可是在iOS 13中便獲取不到了,調試了一下發現是UIApplication
沒法獲取到statusBar。git
UIApplication *app = [UIApplication sharedApplication];
id _statusBar = [app valueForKeyPath:@"_statusBar"];
複製代碼
因而改爲以下的方式經過UIStatusBarManager獲取statusBar。github
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager;
id _statusBar = nil;
if ([statusBarManager respondsToSelector:@selector(createLocalStatusBar)]) {
UIView *_localStatusBar = [statusBarManager performSelector:@selector(createLocalStatusBar)];
if ([_localStatusBar respondsToSelector:@selector(statusBar)]) {
_statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
}
複製代碼
若是隻是往StatusBar上添加View,那麼到這裏就已經能夠獲取到StatusBar了。bash
舊版本中,獲取網絡狀態的代碼以下,原理就是獲取StatusBar中的網絡信號圖標,而後經過獲取信號圖標來獲取網絡狀態。網絡
- (LLNetworkStatus)networkStateFromStatebar {
__block LLNetworkStatus returnValue = LLNetworkStatusNotReachable;
if (![[NSThread currentThread] isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
returnValue = [self networkStateFromStatebar];
});
return returnValue;
}
UIApplication *app = [UIApplication sharedApplication];
id _statusBar = [app valueForKeyPath:@"_statusBar"];
if ([_statusBar isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
// For iPhoneX
NSArray *children = [[[_statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
for (UIView *view in children) {
for (id child in view.subviews) {
if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarWifiSignalView")]) {
returnValue = LLNetworkStatusReachableViaWiFi;
break;
}
if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarStringView")]) {
NSString *originalText = [child valueForKey:@"_originalText"];
if ([originalText containsString:@"G"]) {
if ([originalText isEqualToString:@"2G"]) {
returnValue = LLNetworkStatusReachableViaWWAN2G;
} else if ([originalText isEqualToString:@"3G"]) {
returnValue = LLNetworkStatusReachableViaWWAN3G;
} else if ([originalText isEqualToString:@"4G"]) {
returnValue = LLNetworkStatusReachableViaWWAN4G;
} else {
returnValue = LLNetworkStatusReachableViaWWAN;
}
break;
}
}
}
}
} else {
// For others iPhone
NSArray *children = [[_statusBar valueForKeyPath:@"foregroundView"] subviews];
int type = -1;
for (id child in children) {
if ([child isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]]) {
type = [[child valueForKeyPath:@"dataNetworkType"] intValue];
}
}
switch (type) {
case 0:
returnValue = LLNetworkStatusNotReachable;
break;
case 1:
returnValue = LLNetworkStatusReachableViaWWAN2G;
break;
case 2:
returnValue = LLNetworkStatusReachableViaWWAN3G;
break;
case 3:
returnValue = LLNetworkStatusReachableViaWWAN4G;
break;
case 4:
returnValue = LLNetworkStatusReachableViaWWAN;
break;
case 5:
returnValue = LLNetworkStatusReachableViaWiFi;
break;
default:
break;
}
}
return returnValue;
}
複製代碼
雖然在iOS 13中已經能夠獲取到StatusBar,可是不斷遞推[StatusBar subviews]時,卻不能發現任何一個有關網絡信息的View,因此舊的方式並不適用與iOS 13,因此咱們打印出StatusBar中全部的屬性,查找接下來的思路。app
(lldb) po [[[_statusBar valueForKeyPath:@"_statusBar"] class] LL_getPropertyNames]
<__NSArrayM 0x600000192be0>(
items,
displayItemStates,
updateCompletionHandler,
foregroundView,
targetActionable,
accessibilityHUDGestureManager,
visualProviderClassName,
visualProviderClass,
visualProvider,
regions,
dataAggregator,
currentAggregatedData,
containerView,
animationContextId,
animationsEnabled,
styleAttributes,
action,
targetScreen,
style,
foregroundColor,
mode,
orientation,
currentData,
dependentDataEntryKeys,
overlayData,
actionGestureRecognizer,
enabledPartIdentifiers,
avoidanceFrame,
hash,
superclass,
description,
debugDescription
)
複製代碼
在打印的屬性中,咱們只須要具體分析currentData就能夠。(爲何只分析currentData,由於控制導航欄信息的數據都存在currentData中)ide
(lldb) po [[_statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"currentData"]
<_UIStatusBarData: 0x7fdc464362e0:
mainBatteryEntry=<_UIStatusBarDataBatteryEntry: 0x600000187c30: isEnabled=1, capacity=100, state=2, saverModeActive=0, prominentlyShowsDetailString=0, detailString=100%>,
secondaryCellularEntry=<_UIStatusBarDataCellularEntry: 0x600002b25440: isEnabled=1, rawValue=0, displayValue=0, displayRawValue=0, status=0, lowDataModeActive=0, type=5, wifiCallingEnabled=0, callForwardingEnabled=0, showsSOSWhenDisabled=0>,
dateEntry=<_UIStatusBarDataStringEntry: 0x600000f17f00: isEnabled=1, stringValue=Tue Aug 27>,
timeEntry=<_UIStatusBarDataStringEntry: 0x600000f17640: isEnabled=1, stringValue=6:34 PM>,
cellularEntry=<_UIStatusBarDataCellularEntry: 0x600002b254a0: isEnabled=1, rawValue=0, displayValue=0, displayRawValue=0, status=1, lowDataModeActive=0, type=5, string=Carrier, wifiCallingEnabled=0, callForwardingEnabled=0, showsSOSWhenDisabled=0>,
wifiEntry=<_UIStatusBarDataWifiEntry: 0x600001aa1c40: isEnabled=1, rawValue=0, displayValue=3, displayRawValue=0, status=5, lowDataModeActive=0, type=0>,
shortTimeEntry=<_UIStatusBarDataStringEntry: 0x600000f16ac0: isEnabled=1, stringValue=6:34>,
// some descriptions.
複製代碼
這裏只是展現了一部分log,若是你想查看所有的屬性,能夠本身調試看看,在這些屬性中,咱們能夠看到這裏有關於時間的dateEntry
和timeEntry
,還有關於網絡的cellularEntry
和wifiEntry
,在全部的Entry
中都有isEnabled
屬性,只有當isEnabled
爲true
時,這個屬性纔有意義。經過判斷wifiEntry
是否可用,來肯定是不是WiFi,經過判斷cellularEntry
的type
來判斷具體是4G/3G,因此獲取網絡狀態的代碼以下:ui
id _statusBar = nil;
if (@available(iOS 13.0, *)) {
/*
We can still get statusBar using the following code, but this is not recommended.
*/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager;
if ([statusBarManager respondsToSelector:@selector(createLocalStatusBar)]) {
UIView *_localStatusBar = [statusBarManager performSelector:@selector(createLocalStatusBar)];
if ([_localStatusBar respondsToSelector:@selector(statusBar)]) {
_statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
}
#pragma clang diagnostic pop
if (_statusBar) {
// _UIStatusBarDataCellularEntry
id currentData = [[_statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"currentData"];
id _wifiEntry = [currentData valueForKeyPath:@"wifiEntry"];
id _cellularEntry = [currentData valueForKeyPath:@"cellularEntry"];
if (_wifiEntry && [[_wifiEntry valueForKeyPath:@"isEnabled"] boolValue]) {
// If wifiEntry is enabled, is WiFi.
returnValue = LLNetworkStatusReachableViaWiFi;
} else if (_cellularEntry && [[_cellularEntry valueForKeyPath:@"isEnabled"] boolValue]) {
NSNumber *type = [_cellularEntry valueForKeyPath:@"type"];
if (type) {
switch (type.integerValue) {
case 5:
returnValue = LLNetworkStatusReachableViaWWAN4G;
break;
case 4:
returnValue = LLNetworkStatusReachableViaWWAN3G;
break;
// case 1: // Return 1 when 1G.
// break;
case 0:
// Return 0 when no sim card.
returnValue = LLNetworkStatusNotReachable;
default:
returnValue = LLNetworkStatusReachableViaWWAN;
break;
}
}
}
}
}
複製代碼
完整的代碼以下,固然你也能夠查看LLDebugTool - LLNetworkHelper.m 來查看具體的代碼。this
- (LLNetworkStatus)networkStateFromStatebar {
__block LLNetworkStatus returnValue = LLNetworkStatusNotReachable;
if (![[NSThread currentThread] isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
returnValue = [self networkStateFromStatebar];
});
return returnValue;
}
id _statusBar = nil;
if (@available(iOS 13.0, *)) {
/*
We can still get statusBar using the following code, but this is not recommended.
*/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager;
if ([statusBarManager respondsToSelector:@selector(createLocalStatusBar)]) {
UIView *_localStatusBar = [statusBarManager performSelector:@selector(createLocalStatusBar)];
if ([_localStatusBar respondsToSelector:@selector(statusBar)]) {
_statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
}
#pragma clang diagnostic pop
if (_statusBar) {
// _UIStatusBarDataCellularEntry
id currentData = [[_statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"currentData"];
id _wifiEntry = [currentData valueForKeyPath:@"wifiEntry"];
id _cellularEntry = [currentData valueForKeyPath:@"cellularEntry"];
if (_wifiEntry && [[_wifiEntry valueForKeyPath:@"isEnabled"] boolValue]) {
// If wifiEntry is enabled, is WiFi.
returnValue = LLNetworkStatusReachableViaWiFi;
} else if (_cellularEntry && [[_cellularEntry valueForKeyPath:@"isEnabled"] boolValue]) {
NSNumber *type = [_cellularEntry valueForKeyPath:@"type"];
if (type) {
switch (type.integerValue) {
case 5:
returnValue = LLNetworkStatusReachableViaWWAN4G;
break;
case 4:
returnValue = LLNetworkStatusReachableViaWWAN3G;
break;
// case 1: // Return 1 when 1G.
// break;
case 0:
// Return 0 when no sim card.
returnValue = LLNetworkStatusNotReachable;
default:
returnValue = LLNetworkStatusReachableViaWWAN;
break;
}
}
}
}
} else {
UIApplication *app = [UIApplication sharedApplication];
_statusBar = [app valueForKeyPath:@"_statusBar"];
if ([_statusBar isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
// For iPhoneX
NSArray *children = [[[_statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
for (UIView *view in children) {
for (id child in view.subviews) {
if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarWifiSignalView")]) {
returnValue = LLNetworkStatusReachableViaWiFi;
break;
}
if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarStringView")]) {
NSString *originalText = [child valueForKey:@"_originalText"];
if ([originalText containsString:@"G"]) {
if ([originalText isEqualToString:@"2G"]) {
returnValue = LLNetworkStatusReachableViaWWAN2G;
} else if ([originalText isEqualToString:@"3G"]) {
returnValue = LLNetworkStatusReachableViaWWAN3G;
} else if ([originalText isEqualToString:@"4G"]) {
returnValue = LLNetworkStatusReachableViaWWAN4G;
} else {
returnValue = LLNetworkStatusReachableViaWWAN;
}
break;
}
}
}
}
} else {
// For others iPhone
NSArray *children = [[_statusBar valueForKeyPath:@"foregroundView"] subviews];
int type = -1;
for (id child in children) {
if ([child isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]]) {
type = [[child valueForKeyPath:@"dataNetworkType"] intValue];
}
}
switch (type) {
case 0:
returnValue = LLNetworkStatusNotReachable;
break;
case 1:
returnValue = LLNetworkStatusReachableViaWWAN2G;
break;
case 2:
returnValue = LLNetworkStatusReachableViaWWAN3G;
break;
case 3:
returnValue = LLNetworkStatusReachableViaWWAN4G;
break;
case 4:
returnValue = LLNetworkStatusReachableViaWWAN;
break;
case 5:
returnValue = LLNetworkStatusReachableViaWiFi;
break;
default:
break;
}
}
}
return returnValue;
}
複製代碼