最近在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;
}
@endDEMO地址:https://github.com/li6185377/IMYADLaunchDemo
demo - gif圖,會動的

這個方案已經在美柚穩定使用半年多了,代碼的穩定性是可以放心的。有需求或者bug可以提issues,我會盡快回復。