[iOS][OC] 開發利器:控制器傳送門VCPicker(附demo)

席萍萍BO發表於2019-03-04

0 背景

在專案早期開發階段,還不需要接入後臺網路資料,主要工作的介面的開發。
隨著業務頁面的深入,要檢視二級、三級頁面的效果就需要編譯後跳轉兩三次甚至更多,不斷地重複這個過程相對來說,就沒有可以直接啟動就檢視那個頁面來得那麼高效。

1 解決方案

1.1 簡單粗暴的方式

常見的做法是在application:didFinishLaunchWithOptions:時直接push到想要到達的頁面,如下:

 //  獲取到可以展示的容器控制器
UINavigationViewController *naviVC = [self getNavigationVC];
// 建立想要跳轉的控制器
TargetViewController *targetVC = [[TargetViewController alloc] init]; 
// 跳轉到目標頁面
[naviVC pushViewController:targetVC animated:YES]; 
複製程式碼

這樣可以在程式啟動時便捷地跳轉了,但在多個工程師協同合作時有一個潛在的問題

  • 是會汙染其他同事的程式碼
  • 而多個同事都寫了這樣的便捷頁面跳轉而不小心提交到公共程式碼庫就會出程式碼衝突

1.2 傳送門方案VCPicker

為了方便每個協同開發的工程師跳轉任意頁面,一個更效率的方式有一個入口可以隨時找到某一個ViewController頁面的類,跳轉過去即可,順著這個思路需要做兩個事情:

  • 找到工程裡所有的ViewController控制器類
  • 設定一個統一的入口

實現的途徑:

  • 利用objc-runtime機制獲取專案中所有的類,再篩選出所有UIViewController的子類,以字串的形式儲存類名,以列表的形式展現,當點選某一個類時,通過類初始化一個例項即可實現目標頁面的跳轉
    Class *classes = NULL;
    int numClasses = objc_getClassList(NULL, 0);
複製程式碼
  • 傳送門入口的設計,最初是想通過搖一搖來實現從而不影響原有UI的效果,但是不便於模擬器上的使用,所以借鑑了蘋果的輔助手勢Assist touch懸浮球設計,在程式的keyWindow上懸浮一個可以挪動的小球從而在編譯進入程式後可以第一時間點選檢視控制器列表選擇想要跳轉的控制器。
// 建立懸浮效果
[[UIDynamicAnimator alloc] initWithReferenceView:self.superview];
複製程式碼

2 優化

在使用的過程中逐步衍生並優化了一些有用的功能

2.1 使用class-prefix

由於通過runtime獲取到的類有很多是系統的私有類,甚至不響應NSObject協議的,在操作這些類時則會非常危險,此外一些UI的控制器類(比如圖片選擇、通訊錄獲取)是需要許可權才能訪問和建立例項的,而我們實際的專案中一般都有類字首class-prefix(外包的同學不服….),通過類字首可以快速地篩選出實際專案中的業務頁面

2.2 展示方式

根據具體的業務場景基本上分為帶導航和不帶導航兩種,因此使用presentViewController的方法,一種會建立導航控制器後present,另一種則是直接present;

2.3 獲取title

有的小夥伴提出,看到的是茫茫的一片類名而不知道業務title不方便同事之間學習,通過分析大部分都會在viewDidLoad方法設定title,或者navigationItem.title或者tabbarItem.title,因此需要例項化一個控制器物件,嘗試呼叫viewDidLoad方法,實踐證明如此是不安全的,一方面是viewDidLoad是控制器的生命週期方法原則上是父類呼叫的,再者很多同學還在viewDidLoad進行了KVO和通知的監聽的監聽,手動呼叫viewDidLoad會導致重複監聽的問題,而呼叫[controller view]方法則可以完美地解決這個問題,呼叫[controller view]方法會在內部依次觸發[controller loadView]和[controller viewDidLoad]方法,之後就能獲取到想要的title資訊;此外,在例項化物件呼叫view屬性觸發viewDidLoad時可能因為初始化引數不足的問題丟擲異常,因此需要在此處程式碼塊進行@try-catch保護,並儲存異常資訊提醒當前頁面存在潛在異常。

// 建立例項,獲取title
            UIViewController *controller = nil;
            NSMutableDictionary *dic = [NSMutableDictionary dictionary];
            
            @try {
                if (_needTitle) {
                    controller = [[NSClassFromString(className) alloc] init]; // nil
                    [controller view]; // to active viewDidLoad so we can get conroller.title
                }
                
            } @catch (NSException *exception) {
                NSLog(@"[VCPicker <%@> exception: %@]", className, exception);
                dic[kErrorKey] = exception.description;
                
            } @finally {
                dic[kNameKey] = className;
                dic[kTitleKey] = controller.title ?: (controller.navigationItem.title ?: (controller.tabBarItem.title ?: className));
                [self refreshHistoryForControllerInfo:dic];
                [array addObject:dic];
            }
複製程式碼

2.4 增加歷史記錄和搜尋功能

從眾多的類列表中,經過排序可以方便查詢,更方便的方法是提供搜尋功能,下次再進入時如果儲存了歷史記錄就更好了,所以這些都要有,所以都有了

感謝開發過程中Zoro和Ace同學的極好的建議

在實際開發專案中進行應用,內部實現使用DEBUG巨集進行預編譯的判斷,確保上線時不會出問題,使用VCPicker不用移除也可以正常稽核上線App store。

3 原始碼和demo

demo

相關文章