App启动广告页的实现和封装

欢迎访问我的博客muhlenXi,该文章出自我的博客。

版权声明:本文为muhlenXi原创文章,转载请注明出处,未经允许不得转载.

导语:

也许你也注意到了,现在很多App在启动页加载完毕后,还会出现一个n秒的广告页面,页面中有一个倒计时的按钮,我们可以通过点击跳过那个按钮来跳过,我们点击广告的时候,会进入广告的详情页面。如果我们不做任何操作的话,当倒计时为0秒是会自动进入主页面。

接下来我们就研究研究这个是如何实现的。

解决方案

我们仔细想想:不妨也有两种思路来解决。

  • 1、一种是App初次运行时,将广告页面的图片URL和要点击广告要跳转的URL数据通过服务器下载下来,然后再异步下载图片数据,最后将图片的数据和跳转URL保存到本地沙盒中。第二次运行App的时候会显示广告界面,这时候再通过服务器更新本地沙盒中的数据。第一次由于本地沙盒中没有数据则不会显示。目前大部分App采用此方式

  • 2、第二种是每次启动时就通过服务器异步下载图片数据和跳转URL,然后将其显示出来。这样做的优点时,可以实时更新广告页面数据。缺点是当网络出现阻塞时或无网络时,会出现一个空白的广告界面。

    针对该缺点,有一个解决思路,就是当网络不好时,用一个固定的图片和跳转URL来替换或者只用一个固定的图片来替换,点击广告则不跳转。但是当跳转URL失效时,需要通过迭代版本,重新上架来更新,周期比较长。

第一种方案。

请点击这里查看演示视频

XYJAdvertisementView的实现

Xcode中通过File -> New ->File... 新建一个继承与UIVIewXYJLaunchAdvertisingView

编写程序的核心在于要有一个完整的思路!其次为了高效率的编程,我们需要学会一些偷懒的方法,比如宏定义的使用、类型常量的使用、变量的高效命名...

【1】在XYJAdvertisementView.h文件中,我们需要几个宏定义和类型常量。代码如下:

#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
#define kScreenBounds [UIScreen mainScreen].bounds
static NSString * const adImageName = @"adImageName";
static NSString * const adDownloadUrl = @"adDownloadUrl";
static NSInteger  const adTime = 3;
static NSString * const pushToADNotiName = @"pushToADNotiName";
static NSString * const pushToADUrl = @"pushToADUrl";

我们还需声明一个图片路径和一个显示广告的show方法。

@property (nonatomic,copy) NSString * filePath; //!<  �图片路径 用于属性传值
- (void) showAD;  //显示广告页面方法

【2】在XYJAdvertisementView.m文件中,在匿名类中声明我们需要的控件。

声明如下:

@property (nonatomic,strong) UIImageView * adImageView;
@property (nonatomic,strong) UIButton * countBtn;  //倒计时
@property (nonatomic,strong) NSTimer  * countTimer;
@property (nonatomic,assign) NSInteger  count;  //记录当前的秒数

接下来我们依次实现相应的方法,我们要记住高内聚低耦合的原则

通过重写initWithFrame方法来搭建我们的UI界面

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //其他控件的初始化写在这里
        //1.广告图片
        _adImageView = [[UIImageView alloc] initWithFrame:frame];
        _adImageView.userInteractionEnabled = YES;
        _adImageView.backgroundColor =  [UIColor yellowColor];
        _adImageView.contentMode = UIViewContentModeScaleAspectFill;
        _adImageView.clipsToBounds = YES;
        UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesHandle:)];
        [_adImageView addGestureRecognizer:tap];
        //2.跳过按钮
        CGFloat btnW = 60.0f;
        CGFloat btnH = 30.0f;
        _countBtn = [[UIButton alloc] initWithFrame:CGRectMake(kScreenWidth-btnW-24, btnH, btnW, btnH)];
        [_countBtn addTarget:self action:@selector(dismissAD) forControlEvents:UIControlEventTouchUpInside];
        _countBtn.titleLabel.font = [UIFont systemFontOfSize:15];
        [_countBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [_countBtn setTitle:[NSString stringWithFormat:@"跳过%ld",adTime] forState:UIControlStateNormal];
        _countBtn.backgroundColor = [UIColor colorWithRed:38/255.0 green:38/255.0 blue:38/255.0 alpha:0.6];
        _countBtn.layer.cornerRadius = 4;
        [self addSubview:_adImageView];
        [self addSubview:_countBtn];
    }
    return self;
}
  • 通过懒加载的方式实现我们的定时器countTimer*

    所谓的懒加载也就是延时加载,即当对象需要用到的时候再去加载,简单理解就是,重写对象的get方法。当我们重写get方法时,一定要注意,先要判断当前对象是否为空,为空的话再去实例化对象。

    懒加载的优点如下:
    1、对象的实例化在get方法中实现,可以降低耦合度。
    2、不需要在viewDidLoad中再实例化对象,可以简化代码,同时增强代码的可读性。
    3、有效减少内存的占用率。

代码如下:

- (NSTimer *)countTimer
{
    if (_countTimer == nil) {
       _countTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(countDownEventHandle) userInfo:nil repeats:YES];
    }
    return _countTimer;
}

实现我们的事件响应方法

//广告界面点击
- (void) tapGesHandle:(UITapGestureRecognizer *) tap
{
   [self dismissAD];
   [[NSNotificationCenter defaultCenter] postNotificationName:pushToADNotiName object:nil userInfo:nil];
}
//定时器响应
- (void) countDownEventHandle
{
    _count--;
    [_countBtn setTitle:[NSString stringWithFormat:@"跳过%ld",_count] forState:UIControlStateNormal];
    if (_count == 0) {
        [self dismissAD];
    }
}
//跳过按钮触发
- (void) dismissAD
{
    [self.countTimer invalidate];
    self.countTimer = nil;
    [UIView animateWithDuration:0.3 animations:^{
    self.alpha = 0;
    } completion:^(BOOL finished) {
       [self removeFromSuperview];
    }];
}

实现我们前面声明的Show方法

//启动定时器
- (void) startTimer
{
    _count = adTime;
    [[NSRunLoop mainRunLoop] addTimer:self.countTimer forMode:NSRunLoopCommonModes];
}
//显示广告页面
- (void)showAD
{
    [self startTimer];
    UIWindow * window = [UIApplication sharedApplication].keyWindow;
    [window addSubview:self];
}
//图片赋值
- (void)setFilePath:(NSString *)filePath
{
    _filePath = filePath;
    _adImageView.image = [UIImage imageWithContentsOfFile:filePath];
}

到这里,我们的View就定制完成,我们还需要一个数据管理类来管理沙盒中的广告数据!

数据管理类XYJADDataManager的实现

通过File -> New ->File... 新建一个继承与NSObjectXYJADDataManager

【1】在XYJADDataManager.h文件中,声明一个添加广告的方法声明。代码如下:

导入头文件:

#import "XYJAdvertisementView.h"

方法声明:

+ (void) addXYJAdvertisementView;

【2】在XYJADDataManager.m文件中,实现相应的方法。代码如下:

在实现show方法之前,我们要先实现一些帮助方法

通过图片的名字获取该图片在沙盒中的绝对路径

+ (NSString *)getFilePathWithImageName:(NSString *)imageName
{
    if (imageName) {
        NSArray * paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        //图片默认存储在Cache目录下
        return [paths[0] stringByAppendingPathComponent:imageName];
    }
    return nil;
}

判断该路径下是否存在文件

+ (BOOL)isFileExistWithFilePath:(NSString *) filePath
{
    NSFileManager * fileManager = [NSFileManager defaultManager];
    BOOL isDirectory = NO;
    return [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];   
}

删除旧的图片

+ (void) deleteOldImage
{
    NSString * imageName = [[NSUserDefaults standardUserDefaults] objectForKey:adImageName];
    if (imageName) {
        NSString * filePath = [self getFilePathWithImageName:imageName];
        NSFileManager * fileManager = [NSFileManager defaultManager];
        if ([self isFileExistWithFilePath:filePath]) {
            [fileManager removeItemAtPath:filePath error:nil];
        }
    }
}

向服务器请求广告数据

该方法中要根据实际项目需求做相应调整

+ (void) UpdateAdvertisementDataFromServer
{
    //TODO 在这里请求广告的数据,包含图片的图片路径和点击图片要跳转的URL
    //我们这里假设从服务器中获得的 图片的下载URl和跳转URl如下所示
    NSString * imageurl = @"http://pic.paopaoche.net/up/2012-2/20122220201612322865.png";
    NSString * pushtoURl = @"http://www.jianshu.com";
    //获取图片名
   NSString * imageName = [[imageurl componentsSeparatedByString:@"/"] lastObject];
   //将图片名与沙盒中的数据比较
    NSString * oldImageName =[[NSUserDefaults standardUserDefaults] objectForKey:adImageName];
    if ((oldImageName == nil) || (![oldImageName isEqualToString:imageName]) ) {
        //异步下载广告数据
        [self downloadADImageWithUrl:imageurl iamgeName:imageName];
        //保存跳转路径到沙盒中
        [[NSUserDefaults standardUserDefaults] setObject:pushtoURl forKey:pushToADUrl];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
 }

异步下载图片数据

+ (void) downloadADImageWithUrl:imageUrl iamgeName:(NSString *) imageName
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //TODO 异步操作
        //1、下载数据
        NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
        UIImage * image = [UIImage imageWithData:data];
        //2、获取保存文件的路径
        NSString * filePath = [self getFilePathWithImageName:imageName];
        //3、写入文件到沙盒中
        BOOL ret = [UIImagePNGRepresentation(image) writeToFile:filePath atomically:YES];
        if (ret) {
            NSLog(@"广告图片保存成功");
            [self deleteOldImage];
            //保存�图片名和下载路径
            [[NSUserDefaults standardUserDefaults] setObject:imageName forKey:adImageName];
            [[NSUserDefaults standardUserDefaults] setObject:imageUrl forKey:adDownloadUrl];
            [[NSUserDefaults standardUserDefaults] synchronize];
        } else {
            NSLog(@"广告图片保存失败");
        }
    });
}

实现addXYJAdvertisementView方法

+ (void) addXYJAdvertisementView;
{
    //1.判断沙盒中是否存在广告的图片名字和图片数据,如果有则显示
    NSString * imageName = [[NSUserDefaults standardUserDefaults] objectForKey:adImageName];
    if (imageName != nil)
    {
        NSString * filePath = [self getFilePathWithImageName:imageName];
        BOOL isExist = [self isFileExistWithFilePath:filePath];
        //本地存在图片
        if (isExist) {
            NSLog(@"本地存在图片");
            XYJAdvertisementView * adView = [[XYJAdvertisementView alloc] initWithFrame:kScreenBounds];
            adView.filePath = filePath;
            [adView showAD];
        }
}
    //更新本地广告数据
    [self UpdateAdvertisementDataFromServer];
}

最后一步,在AppDelegate文件中添加广告

  • 1、在AppDelegate.h文件中导入XYJADDataManager.h文件 。
  • 2、在didFinishLaunchingWithOptions方法中加入如下代码即可。
//添加启动广告
[XYJADDataManager addXYJAdvertisementView];
  • 3、如果你需要获取广告点击事件,则需要进行如下操作。

在主界面ViewController的viewDidLoad方法中添加:

//添加广告点击的监听
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pushToADVC) name:pushToADNotiName object:nil];

同时实现事件响应方法:

- (void) pushToADVC
{
    //TODO 在这里处理广告事件响应
    NSLog(@"广告点击了");
    XYJADWebViewController * webVC = [[XYJADWebViewController alloc] init];
    webVC.url = [[NSUserDefaults standardUserDefaults] objectForKey:pushToADUrl];
    [self.navigationController pushViewController:webVC animated:YES];
}

编译运行后,会发现跟我们前面的效果展示是一样的。

点这里下载完整Demo

目前暂无实现第二种方案

感谢阅读,有什么建议可以给我留言

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

推荐阅读更多精彩内容