2014-04-03 skylonely + 關注獻花(0)數組
以前寫過runtime的一些東西,此次經過runtime獲取一些蘋果官方不想讓你拿到的東西,好比,狀態欄內部的控件屬性。本文將經過runtime帶你一步步拿到狀態欄中顯示網絡狀態的控件,而後經過監測該控件的屬性來獲取當前精確網絡狀態,好比2G/3G/4G/WIFI。首先,咱們須要拿到狀態欄,而後經過runtime去探討狀態欄內部的組成結構。網絡
一、導入運行時頭文件#import <objc/message.h>app
二、編寫運行時代碼,獲取到當前應用程序的全部成員變量複製代碼1 #import "ViewController.h"2 #import <objc/message.h>34 @interface ViewController ()56 @end78 @implementation ViewController910 - (void)viewDidAppear:(BOOL)animated11 {12 // 狀態欄是由當前app控制的,首先獲取當前app13 UIApplication *app = [UIApplication sharedApplication];1415 // 遍歷當前app的全部屬性,找到關於狀態欄的16 unsigned int outCount = 0;1718 Ivar *ivars = class_copyIvarList(app.class, &outCount);1920 for (int i = 0; i < outCount; i++) {21 Ivar ivar = ivars[i];22 printf("|%s", ivar_getName(ivar));23 }24 }2526 @end複製代碼直接運行,能夠看到打印結果爲:ide
|_delegate|_exclusiveTouchWindows|_event|_touchesEvent|_motionEvent|_remoteControlEvent|_remoteControlEventObservers|_topLevelNibObjects|_networkResourcesCurrentlyLoadingCount|_hideNetworkActivityIndicatorTimer|_editAlertView|_statusBar|_statusBarRequestedStyle|_statusBarWindow|_observerBlocks|_postCommitActions|_mainStoryboardName|_tintViewDurationStack|_statusBarTintColorLockingControllers|_statusBarTintColorLockingCount|_preferredContentSizeCategory|_applicationFlags|_defaultTopNavBarTintColor|_undoButtonIndex|_redoButtonIndex|_moveEvent|_physicalButtonsEvent|_wheelEvent|_physicalButtonMap|_physicalKeyboardEvent|_alwaysHitTestsForMainScreen|_backgroundHitTestWindow|_eventQueue|_childEventMap|_disableTouchCoalescingCount|_classicMode|_actionsPendingInitialization|_idleTimerDisabledReasons|_currentTimestampWhenFirstTouchCameDown|_currentLocationWhereFirstTouchCameDown|_currentActivityUUID|_currentActivityType|_sceneSettingsDiffInspector|_saveStateRestorationArchiveWithFileProtectionCompleteUntilFirstUserAuthentication|_simulatorShakeNotificationToken|_virtualHorizontalSizeClass|_virtualVerticalSizeClass|__expectedViewOrientation|_preferredContentSizeCategoryName|_lastTimestampWhenFirstTouchCameDown|_lastTimestampWhenAllTouchesLifted|_lastLocationWhereFirstTouchCameDown|_lastLocationWhereAllTouchesLifted|_virtualWindowSizeInSceneReferenceSpacepost
三、能夠看app裏確實有個關於狀態欄的成員變量,咱們經過KVC取出它複製代碼1 - (void)viewDidAppear:(BOOL)animated2 {3 // 狀態欄是由當前app控制的,首先獲取當前app4 UIApplication *app = [UIApplication sharedApplication];56 id statusBar = [app valueForKeyPath:@"statusBar"];78 // 遍歷狀態欄的全部成員9 unsigned int outCount = 0;10 Ivar *ivars = class_copyIvarList([statusBar class], &outCount);1112 for (int i = 0; i < outCount; i++) {13 Ivar ivar = ivars[i];14 printf("|%s", ivar_getName(ivar));15 }16 }複製代碼運行後能夠看到打印結果爲測試
|_statusBarWindow|_statusBarServer|_inProcessProvider|_showsForeground|_backgroundView|_foregroundView|_doubleHeightLabel|_doubleHeightLabelContainer|_currentDoubleHeightText|_currentRawData|_interruptedAnimationCompositeViews|_newStyleBackgroundView|_newStyleForegroundView|_slidingStatusBar|_requestedStyle|_styleOverrides|_styleAttributes|_orientation|_hidden|_suppressesHiddenSideEffects|_foreground|_registered|_waitingOnCallbackAfterChangingStyleOverridesLocally|_suppressGlow|_translucentBackgroundAlpha|_showOnlyCenterItems|_localDataOverrides|_tintColor|_lastUsedBackgroundColor|_nextTintTransition|_overrideHeight|_disableRasterizationReasons|_persistentAnimationsEnabled|_simulatesLegacyAppearance|_serverUpdatesDisabled|_homeItemsDisabled|_styleDelegate|_foregroundColor|_legibilityStylespa
四、狀態欄裏有foregroundView這個成員,應該表明着全部當前顯示的視圖,經過KVC取出它裏面的全部子視圖複製代碼1 // 狀態欄是由當前app控制的,首先獲取當前app2 UIApplication *app = [UIApplication sharedApplication];34 NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];56 for (id child in children) {7 NSLog(@"--%@", [child class]);8 }複製代碼打印結果爲.net
--UIStatusBarServiceItemVieworm
2016-02-29 14:51:56.538 runtime[16977:130523] --UIStatusBarDataNetworkItemViewserver
2016-02-29 14:51:56.538 runtime[16977:130523] --UIStatusBarBatteryItemView
2016-02-29 14:51:56.538 runtime[16977:130523] --UIStatusBarTimeItemView
五、遍歷數組,取出用於顯示網絡狀態的視圖,並遍歷其內部的全部成員變量複製代碼1 // 狀態欄是由當前app控制的,首先獲取當前app2 UIApplication *app = [UIApplication sharedApplication];34 NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];56 for (id child in children) {7 if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {8 // 遍歷當前狀態欄的全部屬性,找到關於狀態欄的9 unsigned int outCount = 0;10 Ivar *ivars = class_copyIvarList([child class], &outCount);1112 for (int i = 0; i < outCount; i++) {13 Ivar ivar = ivars[i];14 printf("|%s", ivar_getName(ivar));15 }16 }17 }複製代碼打印結果爲
|_dataNetworkType|_wifiStrengthRaw|_wifiStrengthBars|_enableRSSI|_showRSSI
六、下面經過KVC,取出dataNetworkType1 if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {2 id type = [child valueForKeyPath:@"dataNetworkType"];3 NSLog(@"_dataNetworkType class is %@, value is %@", [type class], type);4 }打印結果爲:
_dataNetworkType class is __NSCFNumber, value is 5
可見,dataNetworkType類型是NSNumber,值是5。
【以上均爲模擬器測試】通過測試,發現,可能的值爲 1,2,3,5 分別對應的網絡狀態是2G、3G、4G及WIFI。 當沒有網絡時,隱藏UIStatusBarDataNetworkItemView,沒法獲取dataNetworkType值總結:如下是完整的代碼,並通過真機測試:複製代碼1 - (void)viewDidAppear:(BOOL)animated2 {3 // 狀態欄是由當前app控制的,首先獲取當前app4 UIApplication *app = [UIApplication sharedApplication];56 NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];78 int type = 0;9 for (id child in children) {10 if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {11 type = [[child valueForKeyPath:@"dataNetworkType"] intValue];12 }13 }14 NSLog(@"----%d", type);15 }複製代碼打印出的type數字對應的網絡狀態依次是:0 - 無網絡; 1 - 2G; 2 - 3G; 3 - 4G; 5 - WIFI