最近在CocoaChina上看到蠻多小伙伴分享了自己的開屏廣告經驗和代碼,如:分分鐘解決iOS開發中App啟動廣告的功能,App啟動加載廣告頁面思路
代碼還是不錯的,但是個人覺得,上訴代碼的耦合性還是太強了,需要對 AppDelegate 和 ViewController 等代碼進行入侵。如果按照模塊化方式來開發,後續廣告要擴展和維護都是很艱難的,因為你要擔心你埋入的那些代碼被其他人員改動了。
下面是我使用的一套方案。真正做到模塊化,即插即用!
實現原理
自啟動 & 監聽
///在load 方法中,啟動監聽,可以做到無注入+ (void)load { [self shareInstance]; } - (instancetype)init { self = [super init]; if (self) { ///如果是沒啥經驗的開發,請不要在初始化的代碼裡面做別的事,防止對主線程的卡頓,和 其他情況 ///應用啟動, 首次開屏廣告 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { ///要等DidFinished方法結束後才能初始化UIWindow,不然會檢測是否有rootViewController dispatch_async(dispatch_get_main_queue(), ^{ [self checkAD]; }); }]; ///進入後台 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { [self request]; }]; ///後台啟動,二次開屏廣告 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { [self checkAD]; }]; } return self; }
iOS的通知是一個神器,它會發出應用的啟動,退到後台等事件通知,有了通知我們就可以做到對AppDelegate的無入侵。
只有通知還是沒有用的,我們還需要顯示。
核心突破點:顯示
- (void)show { ///初始化一個Window, 做到對業務視圖無干擾。 UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; ///廣告布局 [self setupSubviews:window]; ///設置為最頂層,防止 AlertView 等彈窗的覆蓋 window.windowLevel = UIWindowLevelStatusBar + 1; ///默認為YES,當你設置為NO時,這個Window就會顯示了 window.hidden = NO; ///來個漸顯動畫 window.alpha = 0; [UIView animateWithDuration:0.3 animations:^{ window.alpha = 1; }]; ///防止釋放,顯示完後 要手動設置為 nil self.window = window; }
其實大家一般蓋視圖,習慣在 KeyWindow 上直接AddSubview, 其實這是不好的。首先KeyWindow 會被AlertView覆蓋, 還有可能別的業務代碼也進行了AddSubview 這樣就會把你的廣告給覆蓋了。
而使用我這種 UIWindow 的初始化,可以讓你的視圖出現在最頂層,不用怕亂七八糟
的業務邏輯覆蓋。
調用KeyWindow 還有個壞處。下面會說到。
跳轉
其實倒計時跟跳轉是個很普通的功能點,沒啥說的。有個關鍵點還是要說的 就是KeyWindow的調用
///不直接取KeyWindow 是因為當有AlertView 或者有鍵盤彈出時, 取到的KeyWindow是錯誤的。 UIViewController* rootVC = [[UIApplication sharedApplication].delegate window].rootViewController; ///push [[rootVC imy_navigationController] pushViewController:[IMYWebViewController new] animated:YES];
其實 [UIApplication sharedApplication].keyWindow
取到的Window 不一定是你想要的。 因為KeyWindow 是會變的,所以勁量使用 [Delegate Window]
來獲取顯示的Window。 做 OS X 的應該體會多點。
在送上一個擴展,獲取任意ViewController的navigationController
@implementation UIViewController (IMYPublic) - (UINavigationController*)imy_navigationController { UINavigationController* nav = nil; if ([self isKindOfClass:[UINavigationController class]]) { nav = (id)self; } else { if ([self isKindOfClass:[UITabBarController class]]) { nav = [((UITabBarController*)self).selectedViewController imy_navigationController]; } else { nav = self.navigationController; } } return nav; } @end
DEMO地址:https://github.com/li6185377/IMYADLaunchDemo
demo - gif圖,會動的
這個方案已經在美柚穩定使用半年多了,代碼的穩定性是可以放心的。有需求或者bug可以提issues,我會盡快回復。