2019年07月30日11:03:30
首先我們需要用到這幾個文件
# import <ifaddrs.h>
# import <arpa/inet.h>
# import <net/if.h>
然后獲取到硬件設(shè)備的各個IP
+(NSDictionary *)getIPAddresses{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP)) {
continue;
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
}
}
if(type) {
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
把拿到的地址打印一下 耳高,會看到各種不同的地址扎瓶。類似這種pdp_ip0/ipv4, en0/ipv4, en0/ipv6 等等,顧名思義這就是不同網(wǎng)絡(luò)環(huán)境下的IPv4 IPv6地址泌枪,我們需要用到這幾個:
#define IOS_CELLULAR @"pdp_ip0" // 蜂窩網(wǎng)絡(luò)
#define IOS_WIFI @"en0" // WiFi網(wǎng)絡(luò)
#define IOS_VPN @"utun0" // VPN
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
其他的當(dāng)然也有用概荷,比如打開熱點之后的也能在里面找到。
拿到地址之后判斷我們常用的這三個地址中有沒有IPv6的可用地址碌燕,因為用不同設(shè)備多獲取幾次地址误证,你就會發(fā)現(xiàn)其中有不可用地址,比如IPv4中 127.0.0.1修壕、169.254.120.241愈捅,IPv6中 以fe80開頭的地址。
所以 慈鸠,如果地址中有可用的IPv6那就說明當(dāng)前設(shè)備的網(wǎng)絡(luò)環(huán)境有支持IPv6改鲫,接下來我們就按照這個規(guī)則來判斷是否支持IPv6:
+(BOOL)isSupportIpv6{
NSArray *searchArray =
@[ IOS_VPN @"/" IP_ADDR_IPv6,
IOS_VPN @"/" IP_ADDR_IPv4,
IOS_WIFI @"/" IP_ADDR_IPv6,
IOS_WIFI @"/" IP_ADDR_IPv4,
IOS_CELLULAR @"/" IP_ADDR_IPv6,
IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getIPAddresses];
NSLog(@"addresses: %@", addresses);
__block BOOL isSupportIpv6 = NO;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
NSLog(@"---%@---%@---",key, addresses[key] );
if ([key rangeOfString:@"ipv6"].length > 0 && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] ) {
if (![addresses[key] hasPrefix:@"fe80"]) {
isSupportIpv6 = YES;
}
}
}];
return isSupportIpv6;
}
已經(jīng)判斷好IPv6了,然后該取IP地址了,這個時候呢因為有IPv6存在的原因像棘,所以取地址也需要區(qū)分一下稽亏,下面是取IP的代碼:
+ (NSString *)getIPAddress{
NSArray *searchArray = [self isSupportIpv6] ?
@[ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] :
@[ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] ;
NSDictionary *addresses = [self getIPAddresses];
__block NSString *address;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
address = addresses[key];
if([self isValidatIP:address]) *stop = YES;
} ];
NSLog(@"%@", address);
return address ? address : @"0.0.0.0";
}
看到這個地方,肯定有人有疑問了缕题,為啥這個取值的數(shù)組searchArray里面還是包含有IPv4和IPv6截歉?,這個嘛 我也沒有去查資料分析具體原因烟零,但是你可以看到IPv4和IPv6的順序是不一樣的瘪松, 我的理解是這個地方的設(shè)備訪問肯定是有順序的,比如我蜂窩網(wǎng)絡(luò)雖然支持IPv6锨阿,但是我現(xiàn)在連上了WiFi 宵睦,這個WiFi是IPv4的,那設(shè)備肯定是優(yōu)先使用WiFi進行網(wǎng)絡(luò)訪問墅诡。
以上參考資料來自CSDN https://blog.csdn.net/qq_15509071/article/details/54632811
好了 接下來我們來取網(wǎng)絡(luò)運營商壳嚎!這個網(wǎng)上很多其他說法,說iOS多少多少版本之后就不支持取了末早,然后他們都是通過statesBar里面去拿的數(shù)據(jù)烟馅。
但是實際上我拿到設(shè)備返回的CTCarrier時看到里面就有.carrierName屬性的啊然磷!那就直接拿來用就可以了郑趁,當(dāng)然還是有人會問SIM卡......暫時先不管,取運營商就夠了姿搜! 下面是代碼:
首先引入需要用的頭文件:
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
+ (NSString *)getMobileNetworkModel{
CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [info subscriberCellularProvider];
if (carrier == nil) {
return @"未識別";
}
NSString *code = [carrier mobileNetworkCode];
if (code == nil) {
return @"未識別";
}
if (carrier.carrierName != nil)
{
return carrier.carrierName;
}
return @"未識別";
}
接下來就是最爽的地方寡润,取信號強度。網(wǎng)上查了很多資料舅柜,都是說這東西不能直接取了梭纹,現(xiàn)在需要通過statesBar去拿到這個數(shù)據(jù),狀態(tài)欄中展示的數(shù)據(jù)也是有效的业踢。
然后這里面需要注意的就是取狀態(tài)欄的時候劉海屏和普通屏的取法不一致栗柒,貼上代碼:
#import <UIKit/UIKit.h>
+(NSArray *)getForegroundSubviews{
if (DeviceUtil.isIphoneX){
id statusBar = [[UIApplication sharedApplication] valueForKeyPath:@"statusBar"];
id statusBarView = [statusBar valueForKeyPath:@"statusBar"];
UIView *foregroundView = [statusBarView valueForKeyPath:@"foregroundView"];
// 0 是背景 1是運營商字段 2是信號欄
NSArray *subviews = [[foregroundView subviews][2] subviews];
return subviews;
} else {
UIApplication *app = [UIApplication sharedApplication];
NSArray *subviews = [[[app valueForKey:@"statusBar"] valueForKey:@"foregroundView"] subviews];
return subviews;
}
}
拿到狀態(tài)欄了,就可以去找里面關(guān)于信號的部分知举。蜂窩信號是4格滿格瞬沦,WiFi是3格滿格,需要考慮的問題就是現(xiàn)在iPhone現(xiàn)在也支持雙卡了雇锡,所以還需要處理單卡雙卡逛钻。貼上代碼:
+(NSDictionary *)getMobileNetworkStates{
NSMutableDictionary * result = [[NSMutableDictionary alloc]init];
NSArray *children = [self getForegroundSubviews];
NSString *state = [[NSString alloc]init];
int netType = 0;
for (id child in children) {
if(DeviceUtil.isIphoneX){
// 初始無網(wǎng)絡(luò)
[result setObject:@"無網(wǎng)絡(luò)" forKey:@"netStates_type"];
// 非WiFi狀態(tài)下
// 單卡
if([child isKindOfClass:NSClassFromString(@"_UIStatusBarCellularSignalView")]){
// 信號強度
[result setObject:[[child valueForKey:@"numberOfActiveBars"] stringValue] forKey:@"netStates_RSSI_0"];
}
// 雙卡
if([child isKindOfClass:NSClassFromString(@"_UIStatusBarDualCellularSignalView")]){
// 主卡
id topSignal = [child valueForKey:@"topSignalView"];
// 信號強度
[result setObject:[topSignal valueForKey:@"numberOfActiveBars"] forKey:@"netStates_RSSI_0"];
// 副卡
id bottomSignal = [child valueForKey:@"bottomSignalView"];
// 信號強度
[result setObject:[bottomSignal valueForKey:@"numberOfActiveBars"] forKey:@"netStates_RSSI_1"];
}
if([child isKindOfClass:NSClassFromString(@"_UIStatusBarStringView")]) {
// 蜂窩網(wǎng)絡(luò)狀態(tài)
// 網(wǎng)絡(luò)狀態(tài)
[result setObject:[child valueForKey:@"originalText"] forKey:@"netStates_type"];
break;
}else if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarWifiSignalView")]) {
// WiFi狀態(tài)
// WiFi信號強度
[result setObject:[child valueForKey:@"numberOfActiveBars"] forKey:@"netStates_RSSI_wifi"];
// 網(wǎng)絡(luò)狀態(tài)
[result setObject:@"WIFI" forKey:@"netStates_type"];
break;
}
}else{
if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
// 信號強度
[result setObject:[child valueForKeyPath:@"wifiStrengthBars"] forKey:@"netStates_RSSI_0"];
// 網(wǎng)絡(luò)狀態(tài)
[result setObject:[child valueForKeyPath:@"dataNetworkType"] forKey:@"netStates_states"];
// 增加一條轉(zhuǎn)義
netType = [[child valueForKeyPath:@"dataNetworkType"]intValue];
switch (netType) {
case 0:
state = @"無網(wǎng)絡(luò)"; // 0
break;
case 1:
state = @"2G"; // 1
break;
case 2:
state = @"3G"; // 2
break;
case 3:
state = @"4G"; // 3
break;
case 5:
state = @"WIFI"; // 5
// 信號強度
[result setObject:[child valueForKeyPath:@"wifiStrengthBars"] forKey:@"netStates_RSSI_wifi"];
[result removeObjectForKey:@"netStates_RSSI_0"];
break;
default:
break;
}
[result setObject:state forKey:@"netStates_type"];
}
}
}
return result;
}
思路來源于另一個帖子 http://www.reibang.com/p/a5bcdbdbfbf8 ,但這個帖子發(fā)的時候可能還沒有出雙卡雙待的iPhone, 所以在上面題主就給完善了一下锰提。這個狀態(tài)欄里面能用的信息還有點多曙痘,除了雙卡芳悲、運營商、還能拿到充電狀態(tài)边坤,以及電池剩余百分之多少電量名扛,有待進一步開發(fā)。