前言

  由于项目中要用到启动页广告,所以做了简单的研究,同时借鉴网易新闻和蘑菇街的交互写了一个简单的demo,现在写出来供大家参考,可能由于个人局限会有一些bug和不完善的地方,也希望大家能够友善提醒和指正。

Github地址:https://github.com/Running2snail/LLFullScreenAd

效果图如下:

代码分析:
上面主要展示了广告图提过按钮显示的两种方式,一种是常见的计数倒计时+跳过的样式(大部分的广告启动页都是这种方式),一种是通过环形倒计时+跳过的样式(仿网易新闻)。下面我将分别介绍两种样式的简单原理。

思路分析:
启动页广告是在启动页消失后添加在window上显示,过程为获取广告图信息,然后下载广告图,其次显示并相应相应的点击跳转等事件。过程并不复杂,主要的问题在于启动时期比较短暂而且图片信息的获取和下载时间的不确定等问题。在这里主要通过设置一个等待计时器来在避免长时间的等待,同时利用SDWebImage来缓存图片数据用于下次的显示。另外圆形进度条的实现也比较简单,这里就不做过多的介绍,下面具体看主要的代码实现。

代码分析:

在AppDelegate.m文件中创建并添加到window上,同时下载广告图

AppDelegate.m

 @implementation AppDelegate

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[ViewController alloc] init];
[self.window makeKeyAndVisible]; [self addADView]; // 添加广告图
[self getADImageURL];
return YES;
} /** 添加广告图 */
- (void)addADView
{
LLFullScreenAdView *adView = [[LLFullScreenAdView alloc] init];
adView.tag = ;
adView.duration = ;
adView.waitTime = ;
adView.skipType = SkipButtonTypeCircleAnimationTest;
adView.adImageTapBlock = ^(NSString *content) {
NSLog(@"%@", content);
};
[self.window addSubview:adView];
} /** 获取广告图URL */
- (void)getADImageURL
{
// 此处推荐使用tag来获取adView,勿使用全局变量。因为在AppDelegate中将其设为全局变量时,不会被释放
LLFullScreenAdView *adView = (LLFullScreenAdView *)[self.window viewWithTag:]; // 模拟从服务器上获取广告图URL
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSString *urlString = @"http://s8.mogucdn.com/p2/170223/28n_4eb3la6b6b0h78c23d2kf65dj1a92_750x1334.jpg"; [adView reloadAdImageWithUrl:urlString]; // 加载广告图(如果没有设置广告图设置为空)
});
}

LLFullScreenAdView.h

#import <UIKit/UIKit.h>

    typedef NS_ENUM(NSUInteger, SkipButtonType) {
SkipButtonTypeNormalTimeAndText = , //普通的倒计时+跳过
SkipButtonTypeCircleAnimationTest, //圆形动画+跳过
SkipButtonTypeNormalText, //只有普通的跳过
SkipButtonTypeNormalTime, //只有普通的倒计时
SkipButtonTypeNone //无
}; typedef void(^adImageBlock)(NSString *content); //可以根据需要添加一些相应的参数 @interface LLFullScreenAdView : UIImageView /** 广告图的显示时间(默认5秒)*/
@property (nonatomic, assign) NSUInteger duration; /** 获取数据前,启动图的等待时间(若不设置则不启动等待机制)*/
@property (nonatomic, assign) NSUInteger waitTime; /** 右上角按钮的样式(默认倒计时+跳过)*/
@property (nonatomic, assign) SkipButtonType skipType; /** 广告图点击事件回调*/
@property (nonatomic, copy) adImageBlock adImageTapBlock; /** 加载广告图*/
- (void)reloadAdImageWithUrl:(NSString *)urlStr; @end

LLFullScreenAdView.m

  /** 获取启动图片 */
- (UIImage *)getLaunchImage
{
CGSize viewSize = [UIScreen mainScreen].bounds.size;
NSString *viewOrientation = @"Portrait"; // 横屏请设置成 @"Landscape"
UIImage *lauchImage = nil;
NSArray *imagesDictionary = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
for (NSDictionary *dict in imagesDictionary)
{
CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
{
lauchImage = [UIImage imageNamed:dict[@"UILaunchImageName"]];
}
}
return lauchImage;
} /** 广告图加载前等待计时器 */
- (void)scheduledWaitTimer
{
if (_timerWait) dispatch_source_cancel(_timerWait); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
_timerWait = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , , queue);
dispatch_source_set_timer(_timerWait, dispatch_walltime(NULL, ), 1.0 * NSEC_PER_SEC, ); dispatch_source_set_event_handler(_timerWait, ^{
if (_waitTime <= ) {
_flag = YES;
dispatch_source_cancel(_timerWait);
dispatch_async(dispatch_get_main_queue(), ^{
[self dismiss]; // 关闭界面
});
} else {
_waitTime--;
}
});
dispatch_resume(_timerWait);
} /** 获取广告图 */
- (void)reloadAdImageWithUrl:(NSString *)urlStr
{
if (urlStr.length <= ) {
if (_timerWait) dispatch_source_cancel(_timerWait);
[self removeFromSuperview];
return;
}
NSURL *imageUrl = [NSURL URLWithString:urlStr];
__weak typeof(self) weakSelf = self;
UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:urlStr];
if (cacheImage) { //查看是否有缓存
NSLog(@"cacheImage");
[weakSelf adImageShowWithImage:cacheImage];
} else {
NSLog(@"noCacheImage");
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageUrl options:SDWebImageLowPriority progress:^(NSInteger receivedSize, NSInteger expectedSize) { } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image && finished && error == nil) { [weakSelf adImageShowWithImage:image];
[[SDImageCache sharedImageCache] storeImage:image forKey:urlStr toDisk:YES];
}
}];
}
} /** 广告图显示倒计时 */
- (void)scheduledTimer
{
if (_timerWait) dispatch_source_cancel(_timerWait); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , ,queue);
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, ), 1.0 * NSEC_PER_SEC, ); // 每秒执行 dispatch_source_set_event_handler(_timer, ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (_duration <= ) {
dispatch_source_cancel(_timer);
[self dismiss]; // 关闭界面
} else {
[self showSkipBtnTitleTime:_duration];
_duration--;
}
});
});
dispatch_resume(_timer);
} /** 消失广告图 */
- (void)dismiss
{
NSLog(@"dismiss");
[UIView animateWithDuration:0.5 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.transform = CGAffineTransformMakeScale(1.2, 1.2);
self.alpha = 0.0;
} completion:^(BOOL finished) { [self removeFromSuperview];
}];
}

圆形进度条的简单实现

使用了CAShapeLayer的strokeStart和strokeEnd属性,实现起来十分的简单方便,通过设置Path的中心点、起始和终止的位置,并利用strokeStart和strokeEnd这两个属性支持动画的特点,我们通过以下代码就可以实现圆形进度条的效果

//懒加载一个计时器视图,并设置动画绘制的路径
- (UIView *)timerView
{
if (!_timerView) {
self.timerView = [[UIView alloc] initWithFrame:CGRectMake(MainScreenWidth - , , , )];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = [UIColor colorWithRed: green: blue: alpha:0.4].CGColor; // 填充颜色
layer.strokeColor = [UIColor redColor].CGColor; // 绘制颜色
layer.lineCap = kCALineCapRound;
layer.lineJoin = kCALineJoinRound;
layer.lineWidth = ;
layer.frame = self.bounds;
layer.path = [self getCirclePath].CGPath;
layer.strokeStart = ;
[_timerView.layer addSublayer:layer];
self.viewLayer = layer; UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(, , , )];
titleLabel.text = @"跳过";
titleLabel.textColor = [UIColor whiteColor];
[titleLabel setTextAlignment:NSTextAlignmentCenter];
titleLabel.font = [UIFont systemFontOfSize:];
[_timerView addSubview:titleLabel];
_remain = _duration * ;
_count = ;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(skipAction)];
[_timerView addGestureRecognizer:tap];
}
return _timerView;
} /* 绘制路径
* path这个属性必须需要设置,不然是没有效果的/
*/
- (UIBezierPath *)getCirclePath
{
return [UIBezierPath bezierPathWithArcCenter:CGPointMake(, ) radius: startAngle:-0.5*M_PI endAngle:1.5*M_PI clockwise:YES];
} /** 广告图显示倒计时 */
- (void)setCircleTimer
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , ,queue);
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, ), 0.05 * NSEC_PER_SEC, ); // 每秒执行 dispatch_source_set_event_handler(_timer, ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (_count >= _remain) {
dispatch_source_cancel(_timer);
self.viewLayer.strokeStart = ;
[self dismiss]; // 关闭界面
} else {
self.viewLayer.strokeStart += 0.01;
_count++; //剩余时间进行自加
}
});
});
dispatch_resume(_timer);
}

如果想对CAShapeLayer做更多了解可参考这篇文章上手CAShapeLayer,动画其实并不难

常用的计时器有两种一种是NSTimer,另一种是GCD,但NSTimer受runloop的影响,由于runloop需要处理很多任务,导致NSTimer的精度降低,在日常开发中,如果我们需要对定时器的精度要求很高的话,可以考虑dispatch_source_t去实现 。dispatch_source_t精度很高,系统自动触发,是系统级别的源。而且使用 NSTimer 时,如果使用不当就容易出现内存泄露,所以计时器建议使用GCD来实现。

上面的实现比较简单,所以也没有用太多的解释,如果感兴趣可以看下源代码,如果有好的建议或者疑问可以留言探讨。另外如果喜欢或者感觉对你有帮助最好能给个Star,非常感谢。

Github地址:https://github.com/Running2snail/LLFullScreenAd

iOS 快速集成启动页广告的更多相关文章

  1. iOS开发 关于启动页和停留时间的设置

    引言: 在开发一款商业App时,我们大都会为我们的App设置一个启动页. 苹果官方对于iOS启动页的设计说明: 为了增强应用程序启动时的用户体验,您应该提供一个启动图像.启动图像与应用程序的首屏幕看起 ...

  2. iOS仿写有妖气漫画、视频捕获框架、启动页广告页demo、多种动画效果等源码

    iOS精选源码 以tableview的section为整体添加阴影效果/ta'b'le'vi'e'w顶部悬浮.... 一个可以轻松应用自定义过滤器的视频捕获框架. 基于UITableView的组件,旨 ...

  3. iOS快速集成友盟社会化分享功能(v6.1.1)

    1.  U-Share SDK集成 1.1 下载U-Share SDK 通过iOS社会化组件选择所需的社交平台后进行下载,下载链接http://dev.umeng.com/social/ios/sdk ...

  4. iOS 快速集成ijkplayer视频直播与录播框架

    最近由于需求的变动,项目内把最初最简单的原生直播框架变成了B站开源的ijkplayer框架,下面把具体的过程总结一下整个过程都比较简单,重要的是理解的过程,集成完毕之后,视频的用户体验比苹果原生好了很 ...

  5. IOS快速集成下拉上拉刷新

    http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5% ...

  6. 混合开发之iOS快速集成DSBridge

    DSBridge-IOS github:https://github.com/wendux/DSBridge-IOS 使用 Native 实现API 代理类 //JsApiTest.m @implem ...

  7. iOS快速集成检查更新

    一直以为Appstore有了检查版本是否更新的机制,我们在APP上做这个更新功能会被拒,但是也有看到一些APP也是做了这个更新功能的.因为在网上没有找到完全正确的方法能获取到iTunes里的数据的,于 ...

  8. iOS 启动页后广告Demo

    重点! 对于启动页后的广告,相信大家也都看到过很多很多的,比如我自己常看到的有 QQ音乐,爱奇艺了.你点击了APP,它会启动就会随之启动..其实这些APP的启动页是没有消失的,你去认真的观察一下!所以 ...

  9. iOS swift 启动页加载广告(图片广告+视频广告)

    一般app在启动的时候都会有广告页,广告页用来加载自己的或者第三方的广告,广告的展示形式也多种多样,最近在看swift相关的东西,这里将提供支持加载图片广告和视频广告的解决方案 思路: 我们知道在加载 ...

随机推荐

  1. redhat在线安装chrome浏览器

    开始的时候是参考吹尽黄沙始到金的文章http://www.cnblogs.com/effective/archive/2012/03/18/2405189.html 1.创建一个文件/etc/yum. ...

  2. IM 之 融云

    官方网站:http://www.rongcloud.cn 官方开发文档:http://www.rongcloud.cn/docs/ IM 融云 之 开发基础概念 IM 融云 之 通讯能力库API IM ...

  3. 利用JS做到隐藏div和显示div

    div的visibility可以控制div的显示和隐藏,但是隐藏后页面显示空白 style="visibility: none;" document.getElementById( ...

  4. pipe管道

    回顾: 进程间通信方式: 信号,管道 消息队列,共享内存,信号量 sokcet 信号: 本质就是软中断 signal(信号,函数指针); void func(int); kill(pid,signo) ...

  5. 利用DIV+CSS制作网页过程中常用的基本概念及标签使用细节

    CSS主要用于对HTML文件功能的补充及扩展,其作用就是对HTML文件中各种排版进行设置,达到对网页中字体.颜色.背景.图片等的控制,使网页能够完全依照设计者的想法来显示. CSS可以控制网页的显示, ...

  6. [ Android 五种数据存储方式之二 ] —— 文件存储数据

    关于文件存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的. 文件可用来存放大量数据,如文本.图片.音 ...

  7. overflow:hidden 你所不知道的事

    overflow:hidden 你所不知道的事 overflow:hidden这个CSS样式是大家常用到的CSS样式,但是大多数人对这个样式的理解仅仅局限于隐藏溢出,而对于清除浮动这个含义不是很了解. ...

  8. Google中Gson的使用解析json数据-------学习篇

    之前写过一篇Gson解析json数据的基本应用,这里不多说,直接上例子. 有兴趣的可以先阅读下之前那篇,这里附上链接: http://www.cnblogs.com/Ant-soldier/p/632 ...

  9. HDU1173

    采矿 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  10. Java 异步 IO

         新的异步功能的关键点,它们是Channel 类的一些子集,Channel 在处理IO操作的时候需要被切换成一个后台进程.一些需要访问较大,耗时的操作,或是其它的类似实例,可以考虑应用此功能. ...