前言
小編最近在項目中遇到了一個問題,除劉海屏以外的iOS設備可以正常的搜索到硬件設備,但是劉海屏就不行。因此,小編花了一點時間研究了一下iOS設備獲取當前設備的網絡狀態。
實現
因為iOS的系統是封閉的,所以是沒有直接的APi去獲取當前的網絡狀態。但是道高一尺,魔高一尺。開發者總會有辦法獲取自己想要的東西。
1.網絡狀態獲取
獲取當前的網絡類型
獲取當前的網絡類型是通過獲取狀態欄,然后遍歷狀態欄的視圖完成的。
先導入頭文件,如下:
#import "AppDelegate.h"
實現方法如下:
+ (NSString *)getNetworkType
{
UIApplication *app = [UIApplication sharedApplication];
id statusBar = nil;
// 判斷是否是iOS 13
NSString *network = @"";
if (@available(iOS 13.0, *)) {
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
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.
network = @"WIFI";
} else if (_cellularEntry && [[_cellularEntry valueForKeyPath:@"isEnabled"] boolValue]) {
NSNumber *type = [_cellularEntry valueForKeyPath:@"type"];
if (type) {
switch (type.integerValue) {
case 0:
// 無sim卡
network = @"NONE";
break;
case 1:
network = @"1G";
break;
case 4:
network = @"3G";
break;
case 5:
network = @"4G";
break;
default:
// 默認WWAN類型
network = @"WWAN";
break;
}
}
}
}
}else {
statusBar = [app valueForKeyPath:@"statusBar"];
if ([[[self alloc]init]isLiuHaiScreen]) {
// 劉海屏
id statusBarView = [statusBar valueForKeyPath:@"statusBar"];
UIView *foregroundView = [statusBarView valueForKeyPath:@"foregroundView"];
NSArray *subviews = [[foregroundView subviews][2] subviews];
if (subviews.count == 0) {
// iOS 12
id currentData = [statusBarView valueForKeyPath:@"currentData"];
id wifiEntry = [currentData valueForKey:@"wifiEntry"];
if ([[wifiEntry valueForKey:@"_enabled"] boolValue]) {
network = @"WIFI";
}else {
// 卡1:
id cellularEntry = [currentData valueForKey:@"cellularEntry"];
// 卡2:
id secondaryCellularEntry = [currentData valueForKey:@"secondaryCellularEntry"];
if (([[cellularEntry valueForKey:@"_enabled"] boolValue]|[[secondaryCellularEntry valueForKey:@"_enabled"] boolValue]) == NO) {
// 無卡情況
network = @"NONE";
}else {
// 判斷卡1還是卡2
BOOL isCardOne = [[cellularEntry valueForKey:@"_enabled"] boolValue];
int networkType = isCardOne ? [[cellularEntry valueForKey:@"type"] intValue] : [[secondaryCellularEntry valueForKey:@"type"] intValue];
switch (networkType) {
case 0://無服務
network = [NSString stringWithFormat:@"%@-%@", isCardOne ? @"Card 1" : @"Card 2", @"NONE"];
break;
case 3:
network = [NSString stringWithFormat:@"%@-%@", isCardOne ? @"Card 1" : @"Card 2", @"2G/E"];
break;
case 4:
network = [NSString stringWithFormat:@"%@-%@", isCardOne ? @"Card 1" : @"Card 2", @"3G"];
break;
case 5:
network = [NSString stringWithFormat:@"%@-%@", isCardOne ? @"Card 1" : @"Card 2", @"4G"];
break;
default:
break;
}
}
}
}else {
for (id subview in subviews) {
if ([subview isKindOfClass:NSClassFromString(@"_UIStatusBarWifiSignalView")]) {
network = @"WIFI";
}else if ([subview isKindOfClass:NSClassFromString(@"_UIStatusBarStringView")]) {
network = [subview valueForKeyPath:@"originalText"];
}
}
}
}else {
// 非劉海屏
UIView *foregroundView = [statusBar valueForKeyPath:@"foregroundView"];
NSArray *subviews = [foregroundView subviews];
for (id subview in subviews) {
if ([subview isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
int networkType = [[subview valueForKeyPath:@"dataNetworkType"] intValue];
switch (networkType) {
case 0:
network = @"NONE";
break;
case 1:
network = @"2G";
break;
case 2:
network = @"3G";
break;
case 3:
network = @"4G";
break;
case 5:
network = @"WIFI";
break;
default:
break;
}
}
}
}
}
if ([network isEqualToString:@""]) {
network = @"NO DISPLAY";
}
return network;
}
獲取當前的Wifi信息
獲取當前的Wifi信息需要借助系統的SystemConfiguration這個庫。
先導入頭文件,如下:
#import <SystemConfiguration/CaptiveNetwork.h>
實現方法如下:
#pragma mark 獲取Wifi信息
+ (id)fetchSSIDInfo
{
NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
id info = nil;
for (NSString *ifnam in ifs) {
info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
if (info && [info count]) {
break;
}
}
return info;
}
#pragma mark 獲取WIFI名字
+ (NSString *)getWifiSSID
{
return (NSString *)[self fetchSSIDInfo][@"SSID"];
}
#pragma mark 獲取WIFI的MAC地址
+ (NSString *)getWifiBSSID
{
return (NSString *)[self fetchSSIDInfo][@"BSSID"];
}
獲取當前的Wifi信號強度
獲取信號強度與獲取網絡狀態有點類似,通過遍歷狀態欄,從而獲取WIFI圖標的信號強度。在獲取前需注意當前狀態是否為WIFI。如下:
+ (int)getWifiSignalStrength{
int signalStrength = 0;
// 判斷類型是否為WIFI
if ([[self getNetworkType]isEqualToString:@"WIFI"]) {
// 判斷是否為iOS 13
if (@available(iOS 13.0, *)) {
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager;
id statusBar = nil;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
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) {
id currentData = [[statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"currentData"];
id wifiEntry = [currentData valueForKeyPath:@"wifiEntry"];
if ([wifiEntry isKindOfClass:NSClassFromString(@"_UIStatusBarDataIntegerEntry")]) {
// 層級:_UIStatusBarDataNetworkEntry、_UIStatusBarDataIntegerEntry、_UIStatusBarDataEntry
signalStrength = [[wifiEntry valueForKey:@"displayValue"] intValue];
}
}
}else {
UIApplication *app = [UIApplication sharedApplication];
id statusBar = [app valueForKey:@"statusBar"];
if ([[[self alloc]init]isLiuHaiScreen]) {
// 劉海屏
id statusBarView = [statusBar valueForKeyPath:@"statusBar"];
UIView *foregroundView = [statusBarView valueForKeyPath:@"foregroundView"];
NSArray *subviews = [[foregroundView subviews][2] subviews];
if (subviews.count == 0) {
// iOS 12
id currentData = [statusBarView valueForKeyPath:@"currentData"];
id wifiEntry = [currentData valueForKey:@"wifiEntry"];
signalStrength = [[wifiEntry valueForKey:@"displayValue"] intValue];
// dBm
// int rawValue = [[wifiEntry valueForKey:@"rawValue"] intValue];
}else {
for (id subview in subviews) {
if ([subview isKindOfClass:NSClassFromString(@"_UIStatusBarWifiSignalView")]) {
signalStrength = [[subview valueForKey:@"_numberOfActiveBars"] intValue];
}
}
}
}else {
// 非劉海屏
UIView *foregroundView = [statusBar valueForKey:@"foregroundView"];
NSArray *subviews = [foregroundView subviews];
NSString *dataNetworkItemView = nil;
for (id subview in subviews) {
if([subview isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]]) {
dataNetworkItemView = subview;
break;
}
}
signalStrength = [[dataNetworkItemView valueForKey:@"_wifiStrengthBars"] intValue];
return signalStrength;
}
}
}
return signalStrength;
}
2.Reachability的使用
下載開源類Reachability,然后根據文檔使用即可(該類把移動網絡統稱為WWAN):
+ (NSString *)getNetworkTypeByReachability
{
NSString *network = @"";
switch ([[Reachability reachabilityForInternetConnection]currentReachabilityStatus]) {
case NotReachable:
network = @"NONE";
break;
case ReachableViaWiFi:
network = @"WIFI";
break;
case ReachableViaWWAN:
network = @"WWAN";
break;
default:
break;
}
if ([network isEqualToString:@""]) {
network = @"NO DISPLAY";
}
return network;
}
上次發布了這篇文章之后,有人問我,怎么才能獲取設備的IP地址呢?在這里,小編附上獲取iP地址的方法。
先導入頭文件,如下:
#import <ifaddrs.h>
#import <arpa/inet.h>
實現方法,如下:
#pragma mark 獲取設備IP地址
+ (NSString *)getIPAddress
{
NSString *address = @"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 0;
// 檢索當前接口,在成功時,返回0
success = getifaddrs(&interfaces);
if (success == 0) {
// 循環鏈表的接口
temp_addr = interfaces;
while(temp_addr != NULL) {
// 開熱點時本機的IP地址
if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"bridge100"]
) {
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
if(temp_addr->ifa_addr->sa_family == AF_INET) {
// 檢查接口是否en0 wifi連接在iPhone上
if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
// 得到NSString從C字符串
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr = temp_addr->ifa_next;
}
}
// 釋放內存
freeifaddrs(interfaces);
return address;
}
3.iOS 12下的補充
在iOS 12下xcode需要打開權限才可以正常操作,如下:
4.iOS 13下的補充
在iOS 13下xcode需要打開權限才可以正常操作,如下:
并且,在iOS 13下,若要獲取SSID和BSSID,需要添加定位權限
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()
@property (strong, nonatomic) CLLocationManager *locationManager;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (@available(iOS 13.0, *)) {
// 如果是iOS13 未開啟地理位置權限 需要提示一下
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager requestWhenInUseAuthorization];
}
}
}
到這里為止,這篇文章就結束了。在這里提醒一下各位看官,橫屏時請注意不要把狀態欄去掉。有說明不足的地方歡迎評論,這里附上Demo下載地址:Demo。最后,希望這篇文章對各位看官們有所幫助。對支持小編的看官們表示感謝。