iOS开发小贴士—无侵入广告模块设计

写在前面:项目一个新需求增加开屏广告,本着偷懒的打算(不想过多的改变之前的代码,毕竟bug是改出来的)而又恰巧之前在网上看到过一个不知名大神bestswifter写过关于无侵入广告设计思想,结合项目实际情况做了这个即插即用的开屏广告小功能。

需要实现的效果

显示场景我总结为:点击App初次启动、App进入后台再返回前台。当然深层次考虑用户友好度问题,就要分析切换前后台间隔时间问题、广告每日显示次数的上限问题等。在这个地方有一个难点,就是准确的抓住用户的进入后台行为,因为我们是调用苹果的API,这里面有一些小坑会导致我们抓取到了一些伪进入后台的操作,在下面具体实现我用代码解释。

核心点

这个涉及到NSObject的load方法的运行机制,凡是重写了load方法的类系统都会在didFinishLaunchingWithOptions运行之前自动调用一次该方法.有篇帖子就是解释这个的

//在load 方法中,启动监听,可以做到无侵入
+ (void)load {
    
    [self shareInstance];
}

+ (instancetype)shareInstance {
    
    static id instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
         // 写一些通知和核心逻辑代码
    }
    return self;
}

抓取进入后台的行为

类似我之前说的,我们是调用苹果的API监听UIApplicationDidFinishLaunchingNotificationUIApplicationDidEnterBackgroundNotificationUIApplicationWillEnterForegroundNotification这三个状态。
但是这里有个问题,就是当你应用在前台锁屏的情况下,它一样会发送UIApplicationDidEnterBackgroundNotification(进入后台)的通知。这就是我上面说的伪进入后台的情况。所以在这里我用CFNotificationCenterAddObserver监听锁屏和开屏的状态,并设立一个标志位self.enterBackground来判断是否是真的进入后台的操作以及是否需要显示广告,这个标志位的逻辑处理,我测试的好久没啥问题,理解上有点难理解,大概的意思就是检测出App处于前台而你锁屏了这一动作,将其从排除掉。

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        
        //应用启动, 首次开屏广告
        [[NSNotificationCenter defaultCenter]
         addObserverForName:UIApplicationDidFinishLaunchingNotification
                     object:nil
                      queue:nil
                 usingBlock:^(NSNotification * _Nonnull note) {
            //要等DidFinished方法结束后才能初始化UIWindow,不然会检测是否有rootViewController
            
            self.enterBackground = YES;// 标志位初始化
            
            //检查是否满足条件显示广告
            [self checkAD];
        }];
        //进入后台
        [[NSNotificationCenter defaultCenter]
         addObserverForName:UIApplicationDidEnterBackgroundNotification
                     object:nil
                      queue:nil
                 usingBlock:^(NSNotification * _Nonnull note) {
            
            if (self.enterBackground) {
                
                // 请求新的广告数据
                [self requestADData];
            }
        }];
        //后台启动,二次开屏广告
        [[NSNotificationCenter defaultCenter]
         addObserverForName:UIApplicationWillEnterForegroundNotification
                     object:nil
                      queue:nil
                 usingBlock:^(NSNotification * _Nonnull note) {
            
            if (self.enterBackground) {
                
                //检查是否满足条件显示广告
                [self checkAD];
            }
            self.enterBackground = YES; // 标志位
        }];
        
        // 检测锁屏和解锁
        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
             NULL, // observer
             displayStatusChanged,
             CFSTR("com.apple.springboard.lockstate"),
             NULL, // object
             CFNotificationSuspensionBehaviorDeliverImmediately);
    }
    return self;
}

// 接受通知后的处理
static void displayStatusChanged(CFNotificationCenterRef center,
                                 void *observer,
                                 CFStringRef name,
                                 const void *object,
                                 CFDictionaryRef userInfo) {
    
    // 每次锁屏和解锁都会发这个通知,第一次是锁屏,第二次是解锁,交替进行  注:只有应用在前台锁、开屏才会走该通知  程序处于后台并不会走该通知(坑)
    [LJAddShow shareInstance].enterBackground = NO; // 标志位
}

优化业务--广告显示次数上限以及进入后台间隔时间

假如存在用户快速的切换App前后台,或者用户一天内多次启动App。我们是否应该继续让用户将时间耗费在这个烦人的广告上。从用户友好度上来考虑,我们应对App做一些逻辑调整。所以这里我们可以单独抽出一个自然日缓存类#import "LJDailyCache.h"
这一段代码其实没什么难度,主要是个细心吧。将年月日作为key值存储App的在该日还可显示的广告次数,把握好真实进入后台和进入前台的动作记录这两个的时间点。里面的自然日显示次数上限以及进入后台的时间间隔都可以通过远程请求,万一对广告有什么别的业务调整会方便一点。

static NSString *showTimes = @"20";//  显示次数也可远程

static NSInteger  enterBackgroundTimeInterval = 10;// 进入后台时间间隔  时间太短也不可显示广告  这个可以远程请求

@property (nonatomic, assign)BOOL   isShow; // 是否显示广告 标志位

#pragma mark --判断是否符合条件显示广告  今天是否剩余显示次数  是否间隔足够时间
- (BOOL)judgeWhetherShowAd:(NSString *)time {
    
    // 获取今天的日期 格式“2017-08-12”作为存储广告显示次数的key
    NSString *key = [self timeToTranslate:[NSNumber numberWithFloat:[[NSDate date]timeIntervalSince1970]] Formatter:@"yyyy-MM-dd"];
    
    
    NSString *adTime = [[NSUserDefaults standardUserDefaults] stringForKey:key];
    
    NSString *intervalTime = [[NSUserDefaults standardUserDefaults] stringForKey:INTERVALTIME];
    
    // 查看是否存在今天的广告次数
    if (adTime) {
        
        if ([adTime integerValue] > 0) {
            
            [self writeWithKey:key value:[NSString stringWithFormat:@"%ld",[adTime integerValue] - 1]];

            self.isShow = YES;
        }else {
            
            self.isShow = NO;
        }
    }else {
        
        [self writeWithKey:key value:showTime];
        
        self.isShow = YES;
    }
    
    // 判断时间间隔是否足够长
    if (self.isShow&&[time integerValue] - [intervalTime integerValue] > appTimeInterval) {
        
        self.isShow = YES;
        [self writeWithKey:INTERVALTIME value:time];
    }else {
        
        self.isShow = NO;
        [self writeWithKey:INTERVALTIME value:time];
    }
    
    
    return self.isShow;
}

这个Demo里有一段关于广告VC显示的代码我感觉很好 这段代码跟我没关系
这样写的好处是,让视图出现在最顶层,避免了其他的业务代码的覆盖干扰。

- (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;
}

使用

①将Demo里的LJAddShow文件夹拖入到工程里;
#import "LJAddShow.h"文件里- (void)show方法处理需要显示的广告数据;
#import "LJDailyCache.h"文件里修改两个局部变量showTimes(每日显示上限次数)和enterBackgroundTimeInterval(进入后台间隔时间);
#import "LJAddWebViewController.h"广告页,一般是自己工程的web页面。

就这样不需要改变之前的代码,一个开屏广告功能完成。

总结

该功能主要涉及到NSObject的load方法运用、App状态变化的抓取以及一些小的逻辑梳理,细心一点就好。

注:该小功能是借用了一个不知道名字的大神和bestswifter的二者的思路做出来,不算百分之百的原创。两位大神有不满可联系我,我会删帖。

Demo地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,043评论 25 707
  • 从事移动广告商业变现多年,总是觉得产品与商务的同事是一对“欢喜冤家”。站在开发者的角度,关注用户体验最优是无可厚非...
    游社长阅读 8,633评论 2 78
  • 小A说她的妈妈看不到她的朋友圈。 因为:像家里停水来水这样的事就不用发朋友圈了,发一些有用的东西。 不发鸡汤就发鸭...
    余沅芮阅读 635评论 0 0
  • 好的文章一定是有具体内容的,有细节,可以细节到几点几分,可以具体到那个颜色,哪件衣服,可以生动到哪种声音,联想,...
    Jamsweetpot阅读 564评论 0 2
  • 我, 是个单纯善良的人 …… 却已不是个能被认可和接纳的人 …… 但我依然想对这个世界说:我是个好人!!!我还想着...
    真实的事儿阅读 125评论 0 0