├── .gitignore
├── .swift-version
├── LICENSE
├── LaunchImageSet
├── LaunchImageSet-01.png
├── LaunchImageSet-02.png
└── LaunchImageSet.md
├── Logo
├── header.png
└── qqgroup.png
├── README.md
├── ScreenShot
├── ScreenShot00.gif
├── ScreenShot01.gif
├── ScreenShot02.gif
├── ScreenShot03.gif
├── ScreenShot04.gif
├── ScreenShot05.gif
└── ScreenShot06.gif
├── XHLaunchAd.podspec
├── XHLaunchAd
├── Lib
│ └── FLAnimatedImage
│ │ ├── FLAnimatedImage.h
│ │ ├── FLAnimatedImage.m
│ │ ├── FLAnimatedImageView.h
│ │ └── FLAnimatedImageView.m
└── XHLaunchAd
│ ├── XHLaunchAd.h
│ ├── XHLaunchAd.m
│ ├── XHLaunchAdButton.h
│ ├── XHLaunchAdButton.m
│ ├── XHLaunchAdCache.h
│ ├── XHLaunchAdCache.m
│ ├── XHLaunchAdConfiguration.h
│ ├── XHLaunchAdConfiguration.m
│ ├── XHLaunchAdConst.h
│ ├── XHLaunchAdConst.m
│ ├── XHLaunchAdController.h
│ ├── XHLaunchAdController.m
│ ├── XHLaunchAdDownloader.h
│ ├── XHLaunchAdDownloader.m
│ ├── XHLaunchAdImageManager.h
│ ├── XHLaunchAdImageManager.m
│ ├── XHLaunchAdImageView+XHLaunchAdCache.h
│ ├── XHLaunchAdImageView+XHLaunchAdCache.m
│ ├── XHLaunchAdView.h
│ ├── XHLaunchAdView.m
│ ├── XHLaunchImageView.h
│ └── XHLaunchImageView.m
└── XHLaunchAdExample
├── Class
├── Model
│ ├── LaunchAdModel.h
│ └── LaunchAdModel.m
├── Tools
│ ├── Network
│ │ ├── Network.h
│ │ └── Network.m
│ └── UIViewController+
│ │ ├── UIViewController+Nav.h
│ │ └── UIViewController+Nav.m
├── ViewController.h
├── ViewController.m
├── ViewController.xib
├── WebViewController.h
└── WebViewController.m
├── Source
├── LaunchImageAd.json
├── LaunchVideoAd.json
├── image0.jpg
├── image1.jpg
├── image12.gif
├── image2.jpg
├── image3.jpg
├── image4.jpg
├── video0.mp4
└── video1.mp4
├── XHLaunchAdExample.xcodeproj
└── project.pbxproj
└── XHLaunchAdExample
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
├── AppIcon.appiconset
│ └── Contents.json
├── Contents.json
└── LaunchImage.launchimage
│ ├── Contents.json
│ ├── LaunchImage-1125x2436@3x.png
│ ├── LaunchImage-1242x2208@3x.png
│ ├── LaunchImage-1242x2688@3x.png
│ ├── LaunchImage-640x1136@2x.png
│ ├── LaunchImage-640x960@2x.png
│ ├── LaunchImage-750x1334@2x.png
│ ├── LaunchImage-828x1792@2x.png
│ ├── LaunchImage-ipad-1024x768.png
│ ├── LaunchImage-ipad-1536x2048@2x.png
│ ├── LaunchImage-ipad-2048x1536@2x.png
│ └── LaunchImage-ipad-768x1024.png
├── Base.lproj
└── LaunchScreen.storyboard
├── Info.plist
├── XHLaunchAdManager.h
├── XHLaunchAdManager.m
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Xcode
3 | #
4 | build/
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | project.xcworkspace
14 | !default.xcworkspace
15 | xcuserdata
16 | *.xccheckout
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 | *.xcuserstate
22 | *.xcscmblueprint
23 |
24 | # CocoaPods
25 | #
26 | # We recommend against adding the Pods directory to your .gitignore. However
27 | # you should judge for yourself, the pros and cons are mentioned at:
28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
29 | #
30 | Pods/
31 | Podfile.lock
32 |
33 | .DS_Store
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 XHLaunchAd (https://github.com/CoderZhuXH/XHLaunchAd)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LaunchImageSet/LaunchImageSet-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/LaunchImageSet/LaunchImageSet-01.png
--------------------------------------------------------------------------------
/LaunchImageSet/LaunchImageSet-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/LaunchImageSet/LaunchImageSet-02.png
--------------------------------------------------------------------------------
/LaunchImageSet/LaunchImageSet.md:
--------------------------------------------------------------------------------
1 | ## 设置启动页为LaunchImage
2 |
3 | #### 1.设置启动页为LaunchImage,方法如下:
4 | * 1.在Assets.xcassets中新建LaunchImage
5 | 2.在项目`TARGETS->General->App Icons and Launch Images`中设置 `Launch Images Source` 为LaunchImage,并将`Launch Screen File` 设为空(如图)
6 | 
7 |
8 | #### 2.在LaunchImage 添加相应启动图片
9 | * 1.如图
10 | 
11 |
--------------------------------------------------------------------------------
/Logo/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/Logo/header.png
--------------------------------------------------------------------------------
/Logo/qqgroup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/Logo/qqgroup.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ### Github下载不了/下载慢 可以访问国内下载地址: [https://gitee.com/CoderZhuXH/XHLaunchAd](https://gitee.com/CoderZhuXH/XHLaunchAd)
4 |
5 | ### 开屏广告、启动广告解决方案-支持静态/动态图片广告/mp4视频广告
6 |
7 | [](https://github.com/CoderZhuXH/XHLaunchAd)
8 | [](http://cocoadocs.org/docsets/XHLaunchAd)
9 | [](https://github.com/CoderZhuXH/XHLaunchAd)
10 | [](http://cocoadocs.org/docsets/XHLaunchAd/)
11 | [](https://github.com/CoderZhuXH/XHLaunchAd/blob/master/LICENSE)
12 |
13 | ### 特性:
14 |
15 | * 1.支持静态/动态图片广告.
16 | * 2.支持mp4视频广告.
17 | * 3.支持全屏/半屏广告.
18 | * 4.支持网络及本地资源.
19 | * 5.兼容iPhone和iPad.
20 | * 6.支持广告点击事件.
21 | * 7.支持自定义跳过按钮,添加子视图.
22 | * 8.支持设置数据等待时间.
23 | * 9.自带图片/视频下载,缓存功能.
24 | * 10.支持预缓存图片及视频.
25 | * 11.支持设置完成动画.
26 | * 12.支持清除指定资源缓存.
27 | * 13.支持LaunchImage 和 LaunchScreen.storyboard.
28 | * 14.等等等...
29 |
30 | ### 技术交流群(群号:537476189)
31 | 
32 |
33 |
34 | ### 常见问题
35 | #### 1.为什么设置了本地图片广告,却提示找不到图片资源?
36 | * 请将本地广告图片,直接放在工程目录,不要放在Assets里面,XHLaunchAd不是通过imageName:读取图片,而是是通过[NSBundle mainBundle] path....的方式读取本地图片的(此处涉及到内存优化)
37 |
38 | #### 2.为什么我启动的时候会先进入根控制器后,再显示广告页面?
39 | * 请确认下,你在请求广告数据之前,是否有调用`[XHLaunchAd setWaitDataDuration:2];`方法设置数据等待时间
40 |
41 | #### 3.为什么有时候启动广告倒计时出现跳秒现象或者出现卡顿现象?
42 | * 此问题是你APP启动时主线程阻塞造成的.
43 | * XHLaunchAd采用GCD定时器,倒计时不受主线程阻塞影响,但更新秒数/UI是在主线程中进行的.主线程阻塞影响UI更新.
44 | * 请检查你程序启动时,有没有掉用同步方法或同步请求,(例如:环信SDK同步登录等).
45 | * 建议打开手机-设置-开发者-弱网环境 进行调试比较容易找到阻塞原因.
46 |
47 | #### 4.为什么设置了缓存策略,关闭网络后,开屏广告却不显示?
48 | * XHLaunchAd 图片广告设置configuration.imageOption为XHLaunchAdImageDefault 或者XHLaunchAdImageRefreshCached 或者XHLaunchAdImageCacheInBackground后,都会缓存广告image,视频广告会默认缓存视频文件,断网后广告数据请求会失败,你需要在广告数据请求失败的回调里初始化XHLaunchAd,没有初始化XHLaunchAd,开屏广告是不会显示的.
49 | [XHLaunchAd cacheImageURLString]和 [XHLaunchAd cacheVideoURLString]可以获取上一次广告显示时的ImageURLString和videoUrlString.
50 |
51 | #### 5.如何用一张广告图,适配所有机型?
52 | * 图片广告:设置imageAdconfiguration.contentMode = UIViewContentModeScaleAspectFill; 视频广告:设置videoAdconfiguration.videoGravity = AVLayerVideoGravityResizeAspectFill;.
53 | * 广告图片/视频内容到四周保留适当安全距离,多余的会裁剪掉,例如:用8P尺寸1242*2208的广告图,在iphonex,iphonexr,iphonexs,iphonexs max上显示时,左右两侧会多出一部分,多出的的部分会裁剪掉,这一区域不要放广告内容.
54 |
55 |
56 | ### 更新记录:
57 |
58 | * 2020.01.09 -- v3.9.10 -->1.广告点击事件代理支持返回bool值决定是否移除广告(- (BOOL)xhLaunchAd:(XHLaunchAd *)launchAd clickAtOpenModel:(id)openModel clickPoint:(CGPoint)clickPoint) 2.LaunchScreen.storyboard safeAreaInsets问题修复 3.其他bug fix
59 | * 2018.01.31 -- v3.9.6 -->1.添加视频广告静音属性(muted),2.显示视频广告时暂停其它APP音频播放显示完恢复,3.增加视频播放失败通知
60 | * 2018.01.11 -- v3.9.5 -->1.修复videoGravity修饰符错误bug...
61 | * 2017.12.29 -- v3.9.4 -->1.替换视频播放控制器为AVPlayerViewController 2.解决/优化在线程阻塞的情况下视频播放加载慢的问题 3.注意:此次更新后XHLaunchAd支持iOS8及以上版本,不在支持iOS7...
62 | * 2017.11.22 -- v3.9.0 -->1.新增部分代理方法,部分旧的代理方法做过期处理,2.几处优化...
63 | * 2017.11.14 -- v3.8.4 -->1.版本优化,2.bug fix...
64 | * 2017.10.18 -- v3.8.0 -->1.增加对LaunchScreen.storyboard支持,2.修复pod导入编译报错问题...
65 | * 2017.10.11 -- v3.7.1 -->1.批量缓存接口增加结果回调...
66 | * 2017.10.09 -- v3.7.0 -->1.增加几种清除缓存的接口,2.增加几种倒计时按钮类型,3.已知问题优化与修复...
67 | * 2017.09.29 -- v3.6.1 -->1.增加清除指定图片/视频缓存接口,2.优化在iPhoneX上显示效果,3.已知问题优化与修复...
68 | * 2017.09.18 -- v3.6.0 -->1.优化图片解码方案,2.支持设置GIF动图是否循环播放...
69 | * 2017.09.13 -- v3.5.8 -->增加几种显示完成的动画...
70 | * 2017.08.20 -- v3.5.6 -->已知问题修复及内存优化...
71 | * 2017.05.26 -- v3.5.4 -->修复横屏启动造成的界面问题...
72 | * 2017.05.02 -- v3.5.0 -->Gif动图占用内存优化...
73 | * 2017.04.26 -- v3.4.6 -->版本优化,bug fix...
74 | * 2017.02.17 -- v3.4.0 -->修复部分出现Crash Bug...
75 | * 2016.12.14 -- v3.2.0 -->添加subViews属性2.添加缓存检测...
76 | * 2016.12.03 -- v3.0.0 -->1.增加mp4视频开屏广告2.增加对本地资源支持3.增加预缓存接口4.增加更多属性及接口,具有更强的自定义性5.可设置显示完成动画类型6.可自定义跳过按钮7.拥有更优雅的接入接口8.优化缓存机制,bug fix等等...
77 | * 2016.11.05 -- v2.2.0 -->增加一种缓存方案:先缓存,下次显示.
78 | * 2016.09.13 -- v2.1.8 -->修复在Swift中使用异常.
79 | * 2016.09.10 -- v2.1.7 -->适配iPad,增加应用内跳转到广告详情,优化.
80 | * 2016.09.07 -- v2.1.5 -->修复跳过按钮类型设为None无效问题.
81 | * 2016.09.01 -- v2.1.4 -->广告url传nil或不合法时,按无数据处理.
82 | * 2016.08.22 -- v2.1.2 -->增加未检测到广告数据,设置启动页停留时间属性.
83 | * 2016.08.19 -- v2.1.1 -->跳过按钮bug修复.
84 | * 2016.08.18 -- v2.1.0 -->API微调,增加设置跳过按钮类型选项.
85 | * 2016.08.16 -- v2.0 -->1.修复显示广告前RootViewController闪现bug; 2.API重构,增强实用性.
86 | * 2016.07.18 -- v1.2 -->增加对GIF动态广告的支持.
87 | * 2016.07.02 -- v1.1.2 -->增加设置缓存机制选项.
88 | * 2016.06.17 -- v1.1 -->增加倒计时/跳过按钮.
89 | * 2016.06.13 -- v1.0
90 |
91 | ## 效果
92 |
93 | ### 静态/动态广告-图片/视频广告
94 |
95 |   
96 |   
97 |
98 | ## 使用方法
99 |
100 | ### 1.在didFinishLaunchingWithOptions中或UIApplicationDidFinishLaunching时初始化开屏广告
101 |
102 | ### -1.1 添加图片开屏广告-使用本地数据
103 | #### -1.1.1 使用默认配置快速初始化
104 | ```objc
105 | //1.使用默认配置初始化
106 |
107 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
108 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
109 |
110 | //配置广告数据
111 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration defaultConfiguration];
112 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
113 | imageAdconfiguration.imageNameOrURLString = @"image0.jpg";
114 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
115 | imageAdconfiguration.openModel = @"http://www.it7090.com";
116 | //显示图片开屏广告
117 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
118 | ```
119 | #### -1.1.2自定义配置初始化
120 |
121 | ```objc
122 | //2.自定义配置初始化
123 |
124 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
125 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
126 |
127 | //配置广告数据
128 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration new];
129 | //广告停留时间
130 | imageAdconfiguration.duration = 5;
131 | //广告frame
132 | imageAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-150);
133 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
134 | imageAdconfiguration.imageNameOrURLString = @"image0.jpg";
135 | //设置GIF动图是否只循环播放一次(仅对动图设置有效)
136 | imageAdconfiguration.GIFImageCycleOnce = NO;
137 | //网络图片缓存机制(只对网络图片有效)
138 | imageAdconfiguration.imageOption = XHLaunchAdImageRefreshCached;
139 | //图片填充模式
140 | imageAdconfiguration.contentMode = UIViewContentModeScaleToFill;
141 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
142 | imageAdconfiguration.openModel = @"http://www.it7090.com";
143 | //广告显示完成动画
144 | imageAdconfiguration.showFinishAnimate =ShowFinishAnimateFadein;
145 | //广告显示完成动画时间
146 | imageAdconfiguration.showFinishAnimateTime = 0.8;
147 | //跳过按钮类型
148 | imageAdconfiguration.skipButtonType = SkipTypeTimeText;
149 | //后台返回时,是否显示广告
150 | imageAdconfiguration.showEnterForeground = NO;
151 |
152 | //设置要添加的子视图(可选)
153 | //imageAdconfiguration.subViews = ...
154 |
155 | //显示图片开屏广告
156 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
157 |
158 | ```
159 | ### -1.2 添加图片开屏广告-使用网络数据
160 | #### -1.2.1 使用默认配置快速初始化
161 |
162 | ```objc
163 |
164 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
165 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
166 |
167 | //1.因为数据请求是异步的,请在数据请求前,调用下面方法配置数据等待时间.
168 | //2.设为2即表示:启动页将停留2s等待服务器返回广告数据,2s内等到广告数据,将正常显示广告,否则将不显示
169 | //3.数据获取成功,配置广告数据后,自动结束等待,显示广告
170 | //注意:请求广告数据前,必须设置此属性,否则会先进入window的的根控制器
171 | [XHLaunchAd setWaitDataDuration:2];
172 |
173 | //广告数据请求
174 | [Network getLaunchAdImageDataSuccess:^(NSDictionary * response) {
175 |
176 | NSLog(@"广告数据 = %@",response);
177 |
178 | //广告数据转模型
179 | LaunchAdModel *model = [[LaunchAdModel alloc] initWithDict:response[@"data"]];
180 | //配置广告数据
181 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration defaultConfiguration];
182 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
183 | imageAdconfiguration.imageNameOrURLString = model.content;
184 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
185 | imageAdconfiguration.openModel = model.openUrl;
186 | //显示开屏广告
187 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
188 |
189 | } failure:^(NSError *error) {
190 | }];
191 |
192 |
193 | ```
194 | #### -1.2.2 自定义配置初始化
195 |
196 | ```objc
197 |
198 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
199 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
200 |
201 | //1.因为数据请求是异步的,请在数据请求前,调用下面方法配置数据等待时间.
202 | //2.设为2即表示:启动页将停留2s等待服务器返回广告数据,2s内等到广告数据,将正常显示广告,否则将不显示
203 | //3.数据获取成功,配置广告数据后,自动结束等待,显示广告
204 | //注意:请求广告数据前,必须设置此属性,否则会先进入window的的根控制器
205 | [XHLaunchAd setWaitDataDuration:2];
206 |
207 | //广告数据请求
208 | [Network getLaunchAdImageDataSuccess:^(NSDictionary * response) {
209 |
210 | NSLog(@"广告数据 = %@",response);
211 |
212 | //广告数据转模型
213 | LaunchAdModel *model = [[LaunchAdModel alloc] initWithDict:response[@"data"]];
214 | //配置广告数据
215 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration new];
216 | //广告停留时间
217 | imageAdconfiguration.duration = model.duration;
218 | //广告frame
219 | imageAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width/model.width*model.height);
220 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
221 | imageAdconfiguration.imageNameOrURLString = model.content;
222 | //设置GIF动图是否只循环播放一次(仅对动图设置有效)
223 | imageAdconfiguration.GIFImageCycleOnce = NO;
224 | //缓存机制(仅对网络图片有效)
225 | //为告展示效果更好,可设置为XHLaunchAdImageCacheInBackground,先缓存,下次显示
226 | imageAdconfiguration.imageOption = XHLaunchAdImageDefault;
227 | //图片填充模式
228 | imageAdconfiguration.contentMode = UIViewContentModeScaleToFill;
229 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
230 | imageAdconfiguration.openModel = model.openUrl;
231 | //广告显示完成动画
232 | imageAdconfiguration.showFinishAnimate =ShowFinishAnimateLite;
233 | //广告显示完成动画时间
234 | imageAdconfiguration.showFinishAnimateTime = 0.8;
235 | //跳过按钮类型
236 | imageAdconfiguration.skipButtonType = SkipTypeTimeText;
237 | //后台返回时,是否显示广告
238 | imageAdconfiguration.showEnterForeground = NO;
239 |
240 | //设置要添加的自定义视图(可选)
241 | //imageAdconfiguration.subViews = ...
242 |
243 | //显示开屏广告
244 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
245 |
246 | } failure:^(NSError *error) {
247 | }];
248 |
249 | ```
250 |
251 | ### -1.3添加视频开屏广告-使用本地数据
252 | #### -1.3.1 使用默认配置快速初始化
253 |
254 | ```objc
255 |
256 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
257 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
258 |
259 | //1.使用默认配置初始化
260 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration defaultConfiguration];
261 | //广告视频URLString/或本地视频名(请带上后缀)
262 | videoAdconfiguration.videoNameOrURLString = @"video0.mp4";
263 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
264 | videoAdconfiguration.openModel = @"http://www.it7090.com";
265 | //显示视频开屏广告
266 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
267 | ```
268 |
269 | #### -1.3.2 自定义配置初始化
270 |
271 | ```objc
272 |
273 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
274 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
275 |
276 | //2.自定义配置
277 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration new];
278 | //广告停留时间
279 | videoAdconfiguration.duration = 5;
280 | //广告frame
281 | videoAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
282 | //广告视频URLString/或本地视频名(请带上后缀)
283 | videoAdconfiguration.videoNameOrURLString = @"video1.mp4";
284 | //是否关闭音频
285 | videoAdconfiguration.muted = NO;
286 | //视频填充模式
287 | videoAdconfiguration.videoGravity = AVLayerVideoGravityResizeAspectFill;
288 | //是否只循环播放一次
289 | videoAdconfiguration.videoCycleOnce = NO;
290 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
291 | videoAdconfiguration.openModel = @"http://www.it7090.com";
292 | //广告显示完成动画
293 | videoAdconfiguration.showFinishAnimate =ShowFinishAnimateFadein;
294 | //广告显示完成动画时间
295 | videoAdconfiguration.showFinishAnimateTime = 0.8;
296 | //跳过按钮类型
297 | videoAdconfiguration.skipButtonType = SkipTypeTimeText;
298 | //后台返回时,是否显示广告
299 | videoAdconfiguration.showEnterForeground = NO;
300 |
301 | //设置要添加的子视图(可选)
302 | //videoAdconfiguration.subViews = ...
303 |
304 | //显示视频开屏广告
305 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
306 |
307 | ```
308 | ### -1.4添加视频开屏广告-使用网络数据
309 | #### -1.4.1 使用默认配置快速初始化
310 |
311 | ```objc
312 |
313 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
314 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
315 |
316 | //1.因为数据请求是异步的,请在数据请求前,调用下面方法配置数据等待时间.
317 | //2.设为2即表示:启动页将停留2s等待服务器返回广告数据,2s内等到广告数据,将正常显示广告,否则将不显示
318 | //3.数据获取成功,配置广告数据后,自动结束等待,显示广告
319 | //注意:请求广告数据前,必须设置此属性,否则会先进入window的的根控制器
320 | [XHLaunchAd setWaitDataDuration:2];
321 |
322 | //广告数据请求
323 | [Network getLaunchAdVideoDataSuccess:^(NSDictionary * response) {
324 |
325 | NSLog(@"广告数据 = %@",response);
326 |
327 | //广告数据转模型
328 | LaunchAdModel *model = [[LaunchAdModel alloc] initWithDict:response[@"data"]];
329 |
330 | //配置广告数据
331 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration defaultConfiguration];
332 | //注意:视频广告只支持先缓存,下次显示(看效果请二次运行)
333 | videoAdconfiguration.videoNameOrURLString = model.content;
334 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
335 | videoAdconfiguration.openModel = model.openUrl;
336 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
337 |
338 | } failure:^(NSError *error) {
339 | }];
340 |
341 |
342 | ```
343 | #### -1.4.2 自定义配置初始化
344 |
345 | ```objc
346 |
347 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
348 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
349 |
350 | //1.因为数据请求是异步的,请在数据请求前,调用下面方法配置数据等待时间.
351 | //2.设为2即表示:启动页将停留2s等待服务器返回广告数据,2s内等到广告数据,将正常显示广告,否则将不显示
352 | //3.数据获取成功,配置广告数据后,自动结束等待,显示广告
353 | //注意:请求广告数据前,必须设置此属性,否则会先进入window的的根控制器
354 | [XHLaunchAd setWaitDataDuration:2];
355 |
356 | //广告数据请求
357 | [Network getLaunchAdVideoDataSuccess:^(NSDictionary * response) {
358 |
359 | NSLog(@"广告数据 = %@",response);
360 |
361 | //广告数据转模型
362 | LaunchAdModel *model = [[LaunchAdModel alloc] initWithDict:response[@"data"]];
363 |
364 | //配置广告数据
365 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration new];
366 | //广告停留时间
367 | videoAdconfiguration.duration = model.duration;
368 | //广告frame
369 | videoAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width/model.width*model.height);
370 | //广告视频URLString/或本地视频名(请带上后缀)
371 | //注意:视频广告只支持先缓存,下次显示(看效果请二次运行)
372 | videoAdconfiguration.videoNameOrURLString = model.content;
373 | //是否关闭音频
374 | videoAdconfiguration.muted = NO;
375 | //视频填充模式
376 | videoAdconfiguration.videoGravity = AVLayerVideoGravityResizeAspectFill;
377 | //是否只循环播放一次
378 | videoAdconfiguration.videoCycleOnce = NO;
379 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
380 | videoAdconfiguration.openModel = model.openUrl;
381 | //广告显示完成动画
382 | videoAdconfiguration.showFinishAnimate =ShowFinishAnimateFadein;
383 | //广告显示完成动画时间
384 | videoAdconfiguration.showFinishAnimateTime = 0.8;
385 | //后台返回时,是否显示广告
386 | videoAdconfiguration.showEnterForeground = NO;
387 | //跳过按钮类型
388 | videoAdconfiguration.skipButtonType = SkipTypeTimeText;
389 |
390 | //设置要添加的自定义视图(可选)
391 | //videoAdconfiguration.subViews = ...
392 |
393 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
394 |
395 | } failure:^(NSError *error) {
396 | }];
397 |
398 |
399 | ```
400 |
401 | #### -1.5.0 显示完成动画支持以下效果
402 |
403 | ```objc
404 |
405 | /** 显示完成动画类型 */
406 | typedef NS_ENUM(NSInteger , ShowFinishAnimate) {
407 | /** 无动画 */
408 | ShowFinishAnimateNone = 1,
409 | /** 普通淡入(default) */
410 | ShowFinishAnimateFadein = 2,
411 | /** 放大淡入 */
412 | ShowFinishAnimateLite = 3,
413 | /** 左右翻转(类似网易云音乐) */
414 | ShowFinishAnimateFlipFromLeft = 4,
415 | /** 下上翻转 */
416 | ShowFinishAnimateFlipFromBottom = 5,
417 | /** 向上翻页 */
418 | ShowFinishAnimateCurlUp = 6,
419 | };
420 |
421 | ```
422 |
423 | #### -1.6.0 跳过按钮支持以下类型
424 |
425 | ```objc
426 |
427 | /** 跳过按钮类型 */
428 | typedef NS_ENUM(NSInteger,SkipType) {
429 | SkipTypeNone = 1,//无
430 | /** 方形 */
431 | SkipTypeTime = 2,//方形:倒计时
432 | SkipTypeText = 3,//方形:跳过
433 | SkipTypeTimeText = 4,//方形:倒计时+跳过 (default)
434 | /** 圆形 */
435 | SkipTypeRoundTime = 5,//圆形:倒计时
436 | SkipTypeRoundText = 6,//圆形:跳过
437 | SkipTypeRoundProgressTime = 7,//圆形:进度圈+倒计时
438 | SkipTypeRoundProgressText = 8,//圆形:进度圈+跳过
439 | };
440 |
441 | ```
442 |
443 |
444 | ### 2.点击事件
445 | ```objc
446 | /**
447 | 广告点击事件回调(return YES移除广告,NO不移除广告)
448 | */
449 | -(BOOL)xhLaunchAd:(XHLaunchAd *)launchAd clickAtOpenModel:(id)openModel clickPoint:(CGPoint)clickPoint{
450 |
451 | NSLog(@"广告点击事件");
452 |
453 | //openModel即配置广告数据设置的点击广告时打开页面参数(configuration.openModel)
454 |
455 | if(openModel == nil) return NO;
456 |
457 | WebViewController *VC = [[WebViewController alloc] init];
458 | NSString *urlString = (NSString *)openModel;
459 | VC.URLString = urlString;
460 | //此处不要直接取keyWindow
461 | UIViewController* rootVC = [[UIApplication sharedApplication].delegate window].rootViewController;
462 | [rootVC.myNavigationController pushViewController:VC animated:YES];
463 |
464 | return YES;//YES移除广告,NO不移除广告
465 | }
466 |
467 |
468 | ```
469 | ### 3.自定义跳过按钮
470 | ```objc
471 | //1.XHLaunchImageAdConfiguration 和XHLaunchVideoAdConfiguration 均有一个configuration.customSkipView 属性
472 | //2.自定义一个skipView 赋值给configuration.customSkipView属性 便可替换默认跳过按钮 如下:
473 | configuration.customSkipView = [self customSkipView];
474 |
475 | -(UIView *)customSkipView
476 | {
477 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
478 | button.backgroundColor =[UIColor orangeColor];
479 | button.layer.cornerRadius = 5.0;
480 | button.layer.borderWidth = 1.5;
481 | button.layer.borderColor = [UIColor lightGrayColor].CGColor;
482 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
483 | button.titleLabel.font = [UIFont systemFontOfSize:14];
484 | CGFloat y = XH_IPHONEX ? 54 : 30;
485 | button.frame = CGRectMake([UIScreen mainScreen].bounds.size.width-100,y, 85, 30);
486 | [button addTarget:self action:@selector(skipAction) forControlEvents:UIControlEventTouchUpInside];
487 | return button;
488 | }
489 |
490 | -(void)skipAction
491 | {
492 | //移除广告
493 | [XHLaunchAd removeAndAnimated:YES];
494 | }
495 |
496 | /**
497 | * 代理方法 - 倒计时回调
498 | *
499 | * @param launchAd XHLaunchAd
500 | * @param duration 倒计时时间
501 | */
502 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd customSkipView:(UIView *)customSkipView duration:(NSInteger)duration
503 | {
504 | UIButton *button = (UIButton *)customSkipView;//此处转换为你之前的类型
505 | //设置自定义跳过按钮倒计时
506 | [button setTitle:[NSString stringWithFormat:@"自定义%lds",duration] forState:UIControlStateNormal];
507 | }
508 | ```
509 |
510 | ### 4.批量下载缓存接口(如果你需要提前批量下载并缓存广告图片或视频请调用下面方法)
511 | ```objc
512 |
513 | /**
514 | * 批量下载并缓存image(异步) - 已缓存的image不会再次下载缓存
515 | *
516 | * @param urlArray image URL Array
517 | */
518 | +(void)downLoadImageAndCacheWithURLArray:(NSArray * )urlArray;
519 |
520 | /**
521 | 批量下载并缓存image,并回调结果(异步)- 已缓存的image不会再次下载缓存
522 |
523 | @param urlArray image URL Array
524 | @param completedBlock 回调结果为一个字典数组,url:图片的url字符串,result:0表示该图片下载缓存失败,1表示该图片下载并缓存完成或本地缓存中已有该图片
525 | */
526 | +(void)downLoadImageAndCacheWithURLArray:(nonnull NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock;
527 |
528 | /**
529 | * 批量下载并缓存视频(异步) - 已缓存的视频不会再次下载缓存
530 | *
531 | * @param urlArray 视频URL Array
532 | */
533 | +(void)downLoadVideoAndCacheWithURLArray:(NSArray * )urlArray;
534 |
535 | /**
536 | 批量下载并缓存视频,并回调结果(异步) - 已缓存的视频不会再次下载缓存
537 |
538 | @param urlArray 视频URL Array
539 | @param completedBlock 回调结果为一个字典数组,url:视频的url字符串,result:0表示该视频下载缓存失败,1表示该视频下载并缓存完成或本地缓存中已有该视频
540 | */
541 | +(void)downLoadVideoAndCacheWithURLArray:(nonnull NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock;
542 |
543 | ```
544 |
545 | ### 5.检测是否已缓存
546 |
547 | ```objc
548 |
549 | /**
550 | * 是否已缓存在该图片
551 | *
552 | * @param url image url
553 | *
554 | * @return BOOL
555 | */
556 | +(BOOL)checkImageInCacheWithURL:(NSURL *)url;
557 |
558 | /**
559 | * 是否已缓存该视频
560 | *
561 | * @param url video url
562 | *
563 | * @return BOOL
564 | */
565 | +(BOOL)checkVideoInCacheWithURL:(NSURL *)url;
566 |
567 | ```
568 |
569 | ### 6.缓存/清理相关
570 | ```objc
571 |
572 | /**
573 | * 清除XHLaunch本地所有缓存
574 | */
575 | +(void)clearDiskCache;
576 |
577 | /**
578 | 清除指定Url的图片本地缓存(异步)
579 |
580 | @param imageUrlArray 需要清除缓存的图片Url数组
581 | */
582 | +(void)clearDiskCacheWithImageUrlArray:(NSArray *)imageUrlArray;
583 |
584 | /**
585 | 清除指定Url除外的图片本地缓存(异步)
586 |
587 | @param exceptImageUrlArray 此url数组的图片缓存将被保留,不会被清理
588 | */
589 | +(void)clearDiskCacheExceptImageUrlArray:(NSArray *)exceptImageUrlArray;
590 |
591 | /**
592 | 清除指定Url的视频本地缓存(异步)
593 |
594 | @param videoUrlArray 需要清除缓存的视频url数组
595 | */
596 | +(void)clearDiskCacheWithVideoUrlArray:(NSArray *)videoUrlArray;
597 |
598 | /**
599 | 清除指定Url除外的视频本地缓存(异步)
600 |
601 | @param exceptVideoUrlArray 此url数组的视频缓存将被保留,不会被清理
602 | */
603 | +(void)clearDiskCacheExceptVideoUrlArray:(NSArray *)exceptVideoUrlArray;
604 |
605 |
606 | /**
607 | * 获取XHLaunch本地缓存大小(M)
608 | */
609 | +(float)diskCacheSize;
610 |
611 | /**
612 | * 缓存路径
613 | */
614 | +(NSString *)xhLaunchAdCachePath;
615 |
616 | ```
617 |
618 | ### 7.其它代理方法
619 | ```objc
620 | /**
621 | * 图片本地读取/或下载完成回调
622 | *
623 | * @param launchAd XHLaunchAd
624 | * @param image 读取/下载的image
625 | * @param imageData 读取/下载的imageData
626 | */
627 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd imageDownLoadFinish:(UIImage *)image imageData:(NSData *)imageData;
628 | {
629 | NSLog(@"图片下载完成/或本地图片读取完成回调");
630 | }
631 | /**
632 | * 视频下载完成回调
633 | *
634 | * @param launchAd XHLaunchAd
635 | * @param pathURL 视频保存在本地的path
636 | */
637 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd videoDownLoadFinish:(NSURL *)pathURL
638 | {
639 | NSLog(@"video下载/加载完成/保存path = %@",pathURL.absoluteString);
640 | }
641 |
642 | /**
643 | * 视频下载进度回调
644 | */
645 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd videoDownLoadProgress:(float)progress total:(unsigned long long)total current:(unsigned long long)current
646 | {
647 | NSLog(@"总大小=%lld,已下载大小=%lld,下载进度=%f",total,current,progress);
648 |
649 | }
650 | /**
651 | * 广告显示完成
652 | */
653 | -(void)xhLaunchAdShowFinish:(XHLaunchAd *)launchAd
654 | {
655 | NSLog(@"广告显示完成");
656 | }
657 |
658 | /**
659 | 如果你想用SDWebImage等框架加载网络广告图片,请实现此代理(注意:实现此方法后,图片缓存将不受XHLaunchAd管理)
660 |
661 | @param launchAd XHLaunchAd
662 | @param launchAdImageView launchAdImageView
663 | @param url 图片url
664 | */
665 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd launchAdImageView:(UIImageView *)launchAdImageView URL:(NSURL *)url
666 | {
667 | [launchAdImageView sd_setImageWithURL:url];
668 |
669 | }
670 |
671 | ```
672 |
673 |
674 | ## 依赖
675 | #### 1.本库依赖于:FLAnimatedImage
676 |
677 | ## 安装
678 | ### 1.手动添加:
679 | * 1.将 XHLaunchAd 文件夹添加到工程目录中
680 | * 2.导入 XHLaunchAd.h
681 |
682 | ### 2.CocoaPods:
683 | * 1.在 Podfile 中添加 pod 'XHLaunchAd'
684 | * 2.执行 pod install 或 pod update
685 | * 3.导入 XHLaunchAd.h
686 |
687 | ### 3.Tips
688 | * 1.如果发现pod search XHLaunchAd 搜索出来的不是最新版本,需要在终端执行pod repo update命令更新本地spec缓存(需要几分钟),然后再搜索就可以了
689 | * 2.如果你发现你执行pod install后,导入的不是最新版本,请删除Podfile.lock文件,在执行一次 pod install
690 | * 3.如果在使用过程中遇到BUG,希望你能Issues我,谢谢(或者尝试下载最新的代码看看BUG修复没有)
691 |
692 | ## 系统要求
693 | * 该项目最低支持 iOS 8.0 和 Xcode 8.0
694 |
695 | ## 许可证
696 | XHLaunchAd 使用 MIT 许可证,详情见 LICENSE 文件
697 |
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot00.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot00.gif
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot01.gif
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot02.gif
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot03.gif
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot04.gif
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot05.gif
--------------------------------------------------------------------------------
/ScreenShot/ScreenShot06.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/ScreenShot/ScreenShot06.gif
--------------------------------------------------------------------------------
/XHLaunchAd.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'XHLaunchAd'
3 | s.version = '3.9.12'
4 | s.summary = 'The screen opening advertising solutions - 开屏广告、启动广告解决方案,支持图片/视频、静态/动态、全屏/半屏广告,支持iPhone/iPad,自带图片下载、缓存功能,等等.'
5 | s.homepage = 'https://github.com/CoderZhuXH/XHLaunchAd'
6 | s.license = 'MIT'
7 | s.authors = { 'Zhu Xiaohui' => '977950862@qq.com'}
8 | s.platform = :ios, '8.0'
9 | s.source = { :git => 'https://github.com/CoderZhuXH/XHLaunchAd.git', :tag => s.version }
10 | s.source_files = 'XHLaunchAd/XHLaunchAd/**/*.{h,m}'
11 | s.requires_arc = true
12 | s.dependency 'FLAnimatedImage', '~> 1.0.12'
13 | end
14 |
--------------------------------------------------------------------------------
/XHLaunchAd/Lib/FLAnimatedImage/FLAnimatedImage.h:
--------------------------------------------------------------------------------
1 | //
2 | // FLAnimatedImage.h
3 | // Flipboard
4 | //
5 | // Created by Raphael Schaad on 7/8/13.
6 | // Copyright (c) 2013-2015 Flipboard. All rights reserved.
7 | //
8 |
9 |
10 | #import
11 |
12 | // Allow user classes conveniently just importing one header.
13 | #import "FLAnimatedImageView.h"
14 |
15 |
16 | #ifndef NS_DESIGNATED_INITIALIZER
17 | #if __has_attribute(objc_designated_initializer)
18 | #define NS_DESIGNATED_INITIALIZER __attribute((objc_designated_initializer))
19 | #else
20 | #define NS_DESIGNATED_INITIALIZER
21 | #endif
22 | #endif
23 |
24 | extern const NSTimeInterval kFLAnimatedImageDelayTimeIntervalMinimum;
25 |
26 | //
27 | // An `FLAnimatedImage`'s job is to deliver frames in a highly performant way and works in conjunction with `FLAnimatedImageView`.
28 | // It subclasses `NSObject` and not `UIImage` because it's only an "image" in the sense that a sea lion is a lion.
29 | // It tries to intelligently choose the frame cache size depending on the image and memory situation with the goal to lower CPU usage for smaller ones, lower memory usage for larger ones and always deliver frames for high performant play-back.
30 | // Note: `posterImage`, `size`, `loopCount`, `delayTimes` and `frameCount` don't change after successful initialization.
31 | //
32 | @interface FLAnimatedImage : NSObject
33 |
34 | @property (nonatomic, strong, readonly) UIImage *posterImage; // Guaranteed to be loaded; usually equivalent to `-imageLazilyCachedAtIndex:0`
35 | @property (nonatomic, assign, readonly) CGSize size; // The `.posterImage`'s `.size`
36 |
37 | @property (nonatomic, assign, readonly) NSUInteger loopCount; // 0 means repeating the animation indefinitely
38 | @property (nonatomic, strong, readonly) NSDictionary *delayTimesForIndexes; // Of type `NSTimeInterval` boxed in `NSNumber`s
39 | @property (nonatomic, assign, readonly) NSUInteger frameCount; // Number of valid frames; equal to `[.delayTimes count]`
40 |
41 | @property (nonatomic, assign, readonly) NSUInteger frameCacheSizeCurrent; // Current size of intelligently chosen buffer window; can range in the interval [1..frameCount]
42 | @property (nonatomic, assign) NSUInteger frameCacheSizeMax; // Allow to cap the cache size; 0 means no specific limit (default)
43 |
44 | // Intended to be called from main thread synchronously; will return immediately.
45 | // If the result isn't cached, will return `nil`; the caller should then pause playback, not increment frame counter and keep polling.
46 | // After an initial loading time, depending on `frameCacheSize`, frames should be available immediately from the cache.
47 | - (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index;
48 |
49 | // Pass either a `UIImage` or an `FLAnimatedImage` and get back its size
50 | + (CGSize)sizeForImage:(id)image;
51 |
52 | // On success, the initializers return an `FLAnimatedImage` with all fields initialized, on failure they return `nil` and an error will be logged.
53 | - (instancetype)initWithAnimatedGIFData:(NSData *)data;
54 | // Pass 0 for optimalFrameCacheSize to get the default, predrawing is enabled by default.
55 | - (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled NS_DESIGNATED_INITIALIZER;
56 | + (instancetype)animatedImageWithGIFData:(NSData *)data;
57 |
58 | @property (nonatomic, strong, readonly) NSData *data; // The data the receiver was initialized with; read-only
59 |
60 | @end
61 |
62 | typedef NS_ENUM(NSUInteger, FLLogLevel) {
63 | FLLogLevelNone = 0,
64 | FLLogLevelError,
65 | FLLogLevelWarn,
66 | FLLogLevelInfo,
67 | FLLogLevelDebug,
68 | FLLogLevelVerbose
69 | };
70 |
71 | @interface FLAnimatedImage (Logging)
72 |
73 | + (void)setLogBlock:(void (^)(NSString *logString, FLLogLevel logLevel))logBlock logLevel:(FLLogLevel)logLevel;
74 | + (void)logStringFromBlock:(NSString *(^)(void))stringBlock withLevel:(FLLogLevel)level;
75 |
76 | @end
77 |
78 | #define FLLog(logLevel, format, ...) [FLAnimatedImage logStringFromBlock:^NSString *{ return [NSString stringWithFormat:(format), ## __VA_ARGS__]; } withLevel:(logLevel)]
79 |
80 | @interface FLWeakProxy : NSProxy
81 |
82 | + (instancetype)weakProxyForObject:(id)targetObject;
83 |
84 | @end
85 |
--------------------------------------------------------------------------------
/XHLaunchAd/Lib/FLAnimatedImage/FLAnimatedImageView.h:
--------------------------------------------------------------------------------
1 | //
2 | // FLAnimatedImageView.h
3 | // Flipboard
4 | //
5 | // Created by Raphael Schaad on 7/8/13.
6 | // Copyright (c) 2013-2015 Flipboard. All rights reserved.
7 | //
8 |
9 |
10 | #import
11 |
12 | @class FLAnimatedImage;
13 | @protocol FLAnimatedImageViewDebugDelegate;
14 |
15 |
16 | //
17 | // An `FLAnimatedImageView` can take an `FLAnimatedImage` and plays it automatically when in view hierarchy and stops when removed.
18 | // The animation can also be controlled with the `UIImageView` methods `-start/stop/isAnimating`.
19 | // It is a fully compatible `UIImageView` subclass and can be used as a drop-in component to work with existing code paths expecting to display a `UIImage`.
20 | // Under the hood it uses a `CADisplayLink` for playback, which can be inspected with `currentFrame` & `currentFrameIndex`.
21 | //
22 | @interface FLAnimatedImageView : UIImageView
23 |
24 | // Setting `[UIImageView.image]` to a non-`nil` value clears out existing `animatedImage`.
25 | // And vice versa, setting `animatedImage` will initially populate the `[UIImageView.image]` to its `posterImage` and then start animating and hold `currentFrame`.
26 | @property (nonatomic, strong) FLAnimatedImage *animatedImage;
27 | @property (nonatomic, copy) void(^loopCompletionBlock)(NSUInteger loopCountRemaining);
28 |
29 | @property (nonatomic, strong, readonly) UIImage *currentFrame;
30 | @property (nonatomic, assign, readonly) NSUInteger currentFrameIndex;
31 |
32 | // The animation runloop mode. Enables playback during scrolling by allowing timer events (i.e. animation) with NSRunLoopCommonModes.
33 | // To keep scrolling smooth on single-core devices such as iPhone 3GS/4 and iPod Touch 4th gen, the default run loop mode is NSDefaultRunLoopMode. Otherwise, the default is NSDefaultRunLoopMode.
34 | @property (nonatomic, copy) NSString *runLoopMode;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/XHLaunchAd/Lib/FLAnimatedImage/FLAnimatedImageView.m:
--------------------------------------------------------------------------------
1 | //
2 | // FLAnimatedImageView.h
3 | // Flipboard
4 | //
5 | // Created by Raphael Schaad on 7/8/13.
6 | // Copyright (c) 2013-2015 Flipboard. All rights reserved.
7 | //
8 |
9 |
10 | #import "FLAnimatedImageView.h"
11 | #import "FLAnimatedImage.h"
12 | #import
13 |
14 |
15 | #if defined(DEBUG) && DEBUG
16 | @protocol FLAnimatedImageViewDebugDelegate
17 | @optional
18 | - (void)debug_animatedImageView:(FLAnimatedImageView *)animatedImageView waitingForFrame:(NSUInteger)index duration:(NSTimeInterval)duration;
19 | @end
20 | #endif
21 |
22 |
23 | @interface FLAnimatedImageView ()
24 |
25 | // Override of public `readonly` properties as private `readwrite`
26 | @property (nonatomic, strong, readwrite) UIImage *currentFrame;
27 | @property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
28 |
29 | @property (nonatomic, assign) NSUInteger loopCountdown;
30 | @property (nonatomic, assign) NSTimeInterval accumulator;
31 | @property (nonatomic, strong) CADisplayLink *displayLink;
32 |
33 | @property (nonatomic, assign) BOOL shouldAnimate; // Before checking this value, call `-updateShouldAnimate` whenever the animated image or visibility (window, superview, hidden, alpha) has changed.
34 | @property (nonatomic, assign) BOOL needsDisplayWhenImageBecomesAvailable;
35 |
36 | #if defined(DEBUG) && DEBUG
37 | @property (nonatomic, weak) id debug_delegate;
38 | #endif
39 |
40 | @end
41 |
42 |
43 | @implementation FLAnimatedImageView
44 | @synthesize runLoopMode = _runLoopMode;
45 |
46 | #pragma mark - Initializers
47 |
48 | // -initWithImage: isn't documented as a designated initializer of UIImageView, but it actually seems to be.
49 | // Using -initWithImage: doesn't call any of the other designated initializers.
50 | - (instancetype)initWithImage:(UIImage *)image
51 | {
52 | self = [super initWithImage:image];
53 | if (self) {
54 | [self commonInit];
55 | }
56 | return self;
57 | }
58 |
59 | // -initWithImage:highlightedImage: also isn't documented as a designated initializer of UIImageView, but it doesn't call any other designated initializers.
60 | - (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage
61 | {
62 | self = [super initWithImage:image highlightedImage:highlightedImage];
63 | if (self) {
64 | [self commonInit];
65 | }
66 | return self;
67 | }
68 |
69 | - (instancetype)initWithFrame:(CGRect)frame
70 | {
71 | self = [super initWithFrame:frame];
72 | if (self) {
73 | [self commonInit];
74 | }
75 | return self;
76 | }
77 |
78 | - (instancetype)initWithCoder:(NSCoder *)aDecoder
79 | {
80 | self = [super initWithCoder:aDecoder];
81 | if (self) {
82 | [self commonInit];
83 | }
84 | return self;
85 | }
86 |
87 | - (void)commonInit
88 | {
89 | self.runLoopMode = [[self class] defaultRunLoopMode];
90 | }
91 |
92 |
93 | #pragma mark - Accessors
94 | #pragma mark Public
95 |
96 | - (void)setAnimatedImage:(FLAnimatedImage *)animatedImage
97 | {
98 | if (![_animatedImage isEqual:animatedImage]) {
99 | if (animatedImage) {
100 | // Clear out the image.
101 | super.image = nil;
102 | // Ensure disabled highlighting; it's not supported (see `-setHighlighted:`).
103 | super.highlighted = NO;
104 | // UIImageView seems to bypass some accessors when calculating its intrinsic content size, so this ensures its intrinsic content size comes from the animated image.
105 | [self invalidateIntrinsicContentSize];
106 | } else {
107 | // Stop animating before the animated image gets cleared out.
108 | [self stopAnimating];
109 | }
110 |
111 | _animatedImage = animatedImage;
112 |
113 | self.currentFrame = animatedImage.posterImage;
114 | self.currentFrameIndex = 0;
115 | if (animatedImage.loopCount > 0) {
116 | self.loopCountdown = animatedImage.loopCount;
117 | } else {
118 | self.loopCountdown = NSUIntegerMax;
119 | }
120 | self.accumulator = 0.0;
121 |
122 | // Start animating after the new animated image has been set.
123 | [self updateShouldAnimate];
124 | if (self.shouldAnimate) {
125 | [self startAnimating];
126 | }
127 |
128 | [self.layer setNeedsDisplay];
129 | }
130 | }
131 |
132 |
133 | #pragma mark - Life Cycle
134 |
135 | - (void)dealloc
136 | {
137 | // Removes the display link from all run loop modes.
138 | [_displayLink invalidate];
139 | }
140 |
141 |
142 | #pragma mark - UIView Method Overrides
143 | #pragma mark Observing View-Related Changes
144 |
145 | - (void)didMoveToSuperview
146 | {
147 | [super didMoveToSuperview];
148 |
149 | [self updateShouldAnimate];
150 | if (self.shouldAnimate) {
151 | [self startAnimating];
152 | } else {
153 | [self stopAnimating];
154 | }
155 | }
156 |
157 |
158 | - (void)didMoveToWindow
159 | {
160 | [super didMoveToWindow];
161 |
162 | [self updateShouldAnimate];
163 | if (self.shouldAnimate) {
164 | [self startAnimating];
165 | } else {
166 | [self stopAnimating];
167 | }
168 | }
169 |
170 | - (void)setAlpha:(CGFloat)alpha
171 | {
172 | [super setAlpha:alpha];
173 |
174 | [self updateShouldAnimate];
175 | if (self.shouldAnimate) {
176 | [self startAnimating];
177 | } else {
178 | [self stopAnimating];
179 | }
180 | }
181 |
182 | - (void)setHidden:(BOOL)hidden
183 | {
184 | [super setHidden:hidden];
185 |
186 | [self updateShouldAnimate];
187 | if (self.shouldAnimate) {
188 | [self startAnimating];
189 | } else {
190 | [self stopAnimating];
191 | }
192 | }
193 |
194 |
195 | #pragma mark Auto Layout
196 |
197 | - (CGSize)intrinsicContentSize
198 | {
199 | // Default to let UIImageView handle the sizing of its image, and anything else it might consider.
200 | CGSize intrinsicContentSize = [super intrinsicContentSize];
201 |
202 | // If we have have an animated image, use its image size.
203 | // UIImageView's intrinsic content size seems to be the size of its image. The obvious approach, simply calling `-invalidateIntrinsicContentSize` when setting an animated image, results in UIImageView steadfastly returning `{UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric}` for its intrinsicContentSize.
204 | // (Perhaps UIImageView bypasses its `-image` getter in its implementation of `-intrinsicContentSize`, as `-image` is not called after calling `-invalidateIntrinsicContentSize`.)
205 | if (self.animatedImage) {
206 | intrinsicContentSize = self.image.size;
207 | }
208 |
209 | return intrinsicContentSize;
210 | }
211 |
212 | #pragma mark Smart Invert Colors
213 |
214 | - (BOOL)accessibilityIgnoresInvertColors
215 | {
216 | return YES;
217 | }
218 |
219 | #pragma mark - UIImageView Method Overrides
220 | #pragma mark Image Data
221 |
222 | - (UIImage *)image
223 | {
224 | UIImage *image = nil;
225 | if (self.animatedImage) {
226 | // Initially set to the poster image.
227 | image = self.currentFrame;
228 | } else {
229 | image = super.image;
230 | }
231 | return image;
232 | }
233 |
234 |
235 | - (void)setImage:(UIImage *)image
236 | {
237 | if (image) {
238 | // Clear out the animated image and implicitly pause animation playback.
239 | self.animatedImage = nil;
240 | }
241 |
242 | super.image = image;
243 | }
244 |
245 |
246 | #pragma mark Animating Images
247 |
248 | - (NSTimeInterval)frameDelayGreatestCommonDivisor
249 | {
250 | // Presision is set to half of the `kFLAnimatedImageDelayTimeIntervalMinimum` in order to minimize frame dropping.
251 | const NSTimeInterval kGreatestCommonDivisorPrecision = 2.0 / kFLAnimatedImageDelayTimeIntervalMinimum;
252 |
253 | NSArray *delays = self.animatedImage.delayTimesForIndexes.allValues;
254 |
255 | // Scales the frame delays by `kGreatestCommonDivisorPrecision`
256 | // then converts it to an UInteger for in order to calculate the GCD.
257 | NSUInteger scaledGCD = lrint([delays.firstObject floatValue] * kGreatestCommonDivisorPrecision);
258 | for (NSNumber *value in delays) {
259 | scaledGCD = gcd(lrint([value floatValue] * kGreatestCommonDivisorPrecision), scaledGCD);
260 | }
261 |
262 | // Reverse to scale to get the value back into seconds.
263 | return scaledGCD / kGreatestCommonDivisorPrecision;
264 | }
265 |
266 |
267 | static NSUInteger gcd(NSUInteger a, NSUInteger b)
268 | {
269 | // http://en.wikipedia.org/wiki/Greatest_common_divisor
270 | if (a < b) {
271 | return gcd(b, a);
272 | } else if (a == b) {
273 | return b;
274 | }
275 |
276 | while (true) {
277 | NSUInteger remainder = a % b;
278 | if (remainder == 0) {
279 | return b;
280 | }
281 | a = b;
282 | b = remainder;
283 | }
284 | }
285 |
286 |
287 | - (void)startAnimating
288 | {
289 | if (self.animatedImage) {
290 | // Lazily create the display link.
291 | if (!self.displayLink) {
292 | // It is important to note the use of a weak proxy here to avoid a retain cycle. `-displayLinkWithTarget:selector:`
293 | // will retain its target until it is invalidated. We use a weak proxy so that the image view will get deallocated
294 | // independent of the display link's lifetime. Upon image view deallocation, we invalidate the display
295 | // link which will lead to the deallocation of both the display link and the weak proxy.
296 | FLWeakProxy *weakProxy = [FLWeakProxy weakProxyForObject:self];
297 | self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];
298 |
299 | [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.runLoopMode];
300 | }
301 |
302 | // Note: The display link's `.frameInterval` value of 1 (default) means getting callbacks at the refresh rate of the display (~60Hz).
303 | // Setting it to 2 divides the frame rate by 2 and hence calls back at every other display refresh.
304 | const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz
305 | self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1);
306 |
307 | self.displayLink.paused = NO;
308 | } else {
309 | [super startAnimating];
310 | }
311 | }
312 |
313 | - (void)setRunLoopMode:(NSString *)runLoopMode
314 | {
315 | if (![@[NSDefaultRunLoopMode, NSRunLoopCommonModes] containsObject:runLoopMode]) {
316 | NSAssert(NO, @"Invalid run loop mode: %@", runLoopMode);
317 | _runLoopMode = [[self class] defaultRunLoopMode];
318 | } else {
319 | _runLoopMode = runLoopMode;
320 | }
321 | }
322 |
323 | - (void)stopAnimating
324 | {
325 | if (self.animatedImage) {
326 | self.displayLink.paused = YES;
327 | } else {
328 | [super stopAnimating];
329 | }
330 | }
331 |
332 |
333 | - (BOOL)isAnimating
334 | {
335 | BOOL isAnimating = NO;
336 | if (self.animatedImage) {
337 | isAnimating = self.displayLink && !self.displayLink.isPaused;
338 | } else {
339 | isAnimating = [super isAnimating];
340 | }
341 | return isAnimating;
342 | }
343 |
344 |
345 | #pragma mark Highlighted Image Unsupport
346 |
347 | - (void)setHighlighted:(BOOL)highlighted
348 | {
349 | // Highlighted image is unsupported for animated images, but implementing it breaks the image view when embedded in a UICollectionViewCell.
350 | if (!self.animatedImage) {
351 | [super setHighlighted:highlighted];
352 | }
353 | }
354 |
355 |
356 | #pragma mark - Private Methods
357 | #pragma mark Animation
358 |
359 | // Don't repeatedly check our window & superview in `-displayDidRefresh:` for performance reasons.
360 | // Just update our cached value whenever the animated image or visibility (window, superview, hidden, alpha) is changed.
361 | - (void)updateShouldAnimate
362 | {
363 | BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alpha > 0.0;
364 | self.shouldAnimate = self.animatedImage && isVisible;
365 | }
366 |
367 |
368 | - (void)displayDidRefresh:(CADisplayLink *)displayLink
369 | {
370 | // If for some reason a wild call makes it through when we shouldn't be animating, bail.
371 | // Early return!
372 | if (!self.shouldAnimate) {
373 | FLLog(FLLogLevelWarn, @"Trying to animate image when we shouldn't: %@", self);
374 | return;
375 | }
376 |
377 | NSNumber *delayTimeNumber = [self.animatedImage.delayTimesForIndexes objectForKey:@(self.currentFrameIndex)];
378 | // If we don't have a frame delay (e.g. corrupt frame), don't update the view but skip the playhead to the next frame (in else-block).
379 | if (delayTimeNumber) {
380 | NSTimeInterval delayTime = [delayTimeNumber floatValue];
381 | // If we have a nil image (e.g. waiting for frame), don't update the view nor playhead.
382 | UIImage *image = [self.animatedImage imageLazilyCachedAtIndex:self.currentFrameIndex];
383 | if (image) {
384 | FLLog(FLLogLevelVerbose, @"Showing frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage);
385 | self.currentFrame = image;
386 | if (self.needsDisplayWhenImageBecomesAvailable) {
387 | [self.layer setNeedsDisplay];
388 | self.needsDisplayWhenImageBecomesAvailable = NO;
389 | }
390 |
391 | self.accumulator += displayLink.duration * displayLink.frameInterval;
392 |
393 | // While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m
394 | while (self.accumulator >= delayTime) {
395 | self.accumulator -= delayTime;
396 | self.currentFrameIndex++;
397 | if (self.currentFrameIndex >= self.animatedImage.frameCount) {
398 | // If we've looped the number of times that this animated image describes, stop looping.
399 | self.loopCountdown--;
400 | if (self.loopCompletionBlock) {
401 | self.loopCompletionBlock(self.loopCountdown);
402 | }
403 |
404 | if (self.loopCountdown == 0) {
405 | [self stopAnimating];
406 | return;
407 | }
408 | self.currentFrameIndex = 0;
409 | }
410 | // Calling `-setNeedsDisplay` will just paint the current frame, not the new frame that we may have moved to.
411 | // Instead, set `needsDisplayWhenImageBecomesAvailable` to `YES` -- this will paint the new image once loaded.
412 | self.needsDisplayWhenImageBecomesAvailable = YES;
413 | }
414 | } else {
415 | FLLog(FLLogLevelDebug, @"Waiting for frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage);
416 | #if defined(DEBUG) && DEBUG
417 | if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImageView:waitingForFrame:duration:)]) {
418 | [self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:(NSTimeInterval)displayLink.duration * displayLink.frameInterval];
419 | }
420 | #endif
421 | }
422 | } else {
423 | self.currentFrameIndex++;
424 | }
425 | }
426 |
427 | + (NSString *)defaultRunLoopMode
428 | {
429 | // Key off `activeProcessorCount` (as opposed to `processorCount`) since the system could shut down cores in certain situations.
430 | return [NSProcessInfo processInfo].activeProcessorCount > 1 ? NSRunLoopCommonModes : NSDefaultRunLoopMode;
431 | }
432 |
433 |
434 | #pragma mark - CALayerDelegate (Informal)
435 | #pragma mark Providing the Layer's Content
436 |
437 | - (void)displayLayer:(CALayer *)layer
438 | {
439 | layer.contents = (__bridge id)self.image.CGImage;
440 | }
441 |
442 |
443 | @end
444 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAd.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAd.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | // 版本: 3.9.12
10 | // 发布: 2020.05.06
11 |
12 | // 如果你在使用过程中出现bug,请及时以下面任意一种方式联系我,我会及时修复bug并帮您解决问题。
13 | // QQ交流群:537476189
14 | // Email:it7090@163.com
15 | // 新浪微博:朱晓辉Allen
16 | // GitHub:https://github.com/CoderZhuXH
17 | // 简书:https://www.jianshu.com/u/acf1a1f12e0f
18 | // 掘金:https://juejin.im/user/59b50d3cf265da066d331a06
19 |
20 | // 使用说明:https://github.com/CoderZhuXH/XHLaunchAd/blob/master/README.md
21 |
22 | #import
23 | #import
24 | #import "XHLaunchAdConfiguration.h"
25 | #import "XHLaunchAdConst.h"
26 | #import "XHLaunchImageView.h"
27 |
28 | NS_ASSUME_NONNULL_BEGIN
29 | @class XHLaunchAd;
30 | @protocol XHLaunchAdDelegate
31 | @optional
32 |
33 | /**
34 | 广告点击回调
35 |
36 | @param launchAd launchAd
37 | @param openModel 打开页面参数(此参数即你配置广告数据设置的configuration.openModel)
38 | @param clickPoint 点击位置
39 | @param return YES移除广告,NO不移除
40 | */
41 | - (BOOL)xhLaunchAd:(XHLaunchAd *)launchAd clickAtOpenModel:(id)openModel clickPoint:(CGPoint)clickPoint;
42 |
43 | /**
44 | 跳过按钮点击回调(注意:自定义跳过按钮不会走此回调)
45 |
46 | @param launchAd launchAd
47 | @param skipButton 跳过按钮
48 | */
49 | - (void)xhLaunchAd:(XHLaunchAd *)launchAd clickSkipButton:(UIButton *)skipButton;
50 |
51 | /**
52 | * 图片本地读取/或下载完成回调
53 | *
54 | * @param launchAd XHLaunchAd
55 | * @param image 读取/下载的image
56 | * @param imageData 读取/下载的imageData
57 | */
58 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd imageDownLoadFinish:(UIImage *)image imageData:(NSData *)imageData;
59 |
60 | /**
61 | * video本地读取/或下载完成回调
62 | *
63 | * @param launchAd XHLaunchAd
64 | * @param pathURL 本地保存路径
65 | */
66 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd videoDownLoadFinish:(NSURL *)pathURL;
67 |
68 | /**
69 | 视频下载进度回调
70 |
71 | @param launchAd XHLaunchAd
72 | @param progress 下载进度
73 | @param total 总大小
74 | @param current 当前已下载大小
75 | */
76 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd videoDownLoadProgress:(float)progress total:(unsigned long long)total current:(unsigned long long)current;
77 |
78 | /**
79 | * 倒计时回调
80 | *
81 | * @param launchAd XHLaunchAd
82 | * @param duration 倒计时时间
83 | */
84 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd customSkipView:(UIView *)customSkipView duration:(NSInteger)duration;
85 |
86 | /**
87 | 广告显示完成
88 |
89 | @param launchAd XHLaunchAd
90 | */
91 | -(void)xhLaunchAdShowFinish:(XHLaunchAd *)launchAd;
92 |
93 | /**
94 | 如果你想用SDWebImage等框架加载网络广告图片,请实现此代理,注意:实现此方法后,图片缓存将不受XHLaunchAd管理
95 |
96 | @param launchAd XHLaunchAd
97 | @param launchAdImageView launchAdImageView
98 | @param url 图片url
99 | */
100 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd launchAdImageView:(UIImageView *)launchAdImageView URL:(NSURL *)url;
101 |
102 |
103 | #pragma mark - 过期-XHLaunchAdDelegate
104 | - (void)xhLaunchAd:(XHLaunchAd *)launchAd clickAndOpenModel:(id)openModel clickPoint:(CGPoint)clickPoint XHLaunchAdDeprecated("请使用xhLaunchAd:clickAtOpenModel:clickPoint:");
105 | - (void)xhLaunchAd:(XHLaunchAd *)launchAd clickAndOpenURLString:(NSString *)openURLString XHLaunchAdDeprecated("请使用xhLaunchAd:clickAtOpenModel:clickPoint:");
106 | - (void)xhLaunchAd:(XHLaunchAd *)launchAd clickAndOpenURLString:(NSString *)openURLString clickPoint:(CGPoint)clickPoint XHLaunchAdDeprecated("请使用xhLaunchAd:clickAtOpenModel:clickPoint:");
107 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd imageDownLoadFinish:(UIImage *)image XHLaunchAdDeprecated("请使用xhLaunchAd:imageDownLoadFinish:imageData:");
108 | -(void)xhLaunchShowFinish:(XHLaunchAd *)launchAd XHLaunchAdDeprecated("请使用xhLaunchAdShowFinish:");
109 |
110 | @end
111 |
112 | @interface XHLaunchAd : NSObject
113 |
114 | @property(nonatomic,assign) id delegate;
115 |
116 | /**
117 | 设置你工程的启动页使用的是LaunchImage还是LaunchScreen(default:SourceTypeLaunchImage)
118 | 注意:请在设置等待数据及配置广告数据前调用此方法
119 | @param sourceType sourceType
120 | */
121 | +(void)setLaunchSourceType:(SourceType)sourceType;
122 |
123 | /**
124 | * 设置等待数据源时间(建议值:2)
125 | *
126 | * @param waitDataDuration waitDataDuration
127 | */
128 | +(void)setWaitDataDuration:(NSInteger )waitDataDuration;
129 |
130 | /**
131 | * 图片开屏广告数据配置
132 | *
133 | * @param imageAdconfiguration 数据配置
134 | *
135 | * @return XHLaunchAd
136 | */
137 | +(XHLaunchAd *)imageAdWithImageAdConfiguration:(XHLaunchImageAdConfiguration *)imageAdconfiguration;
138 |
139 | /**
140 | * 图片开屏广告数据配置
141 | *
142 | * @param imageAdconfiguration 数据配置
143 | * @param delegate delegate
144 | *
145 | * @return XHLaunchAd
146 | */
147 | +(XHLaunchAd *)imageAdWithImageAdConfiguration:(XHLaunchImageAdConfiguration *)imageAdconfiguration delegate:(nullable id)delegate;
148 |
149 | /**
150 | * 视频开屏广告数据配置
151 | *
152 | * @param videoAdconfiguration 数据配置
153 | *
154 | * @return XHLaunchAd
155 | */
156 | +(XHLaunchAd *)videoAdWithVideoAdConfiguration:(XHLaunchVideoAdConfiguration *)videoAdconfiguration;
157 |
158 | /**
159 | * 视频开屏广告数据配置
160 | *
161 | * @param videoAdconfiguration 数据配置
162 | * @param delegate delegate
163 | *
164 | * @return XHLaunchAd
165 | */
166 | +(XHLaunchAd *)videoAdWithVideoAdConfiguration:(XHLaunchVideoAdConfiguration *)videoAdconfiguration delegate:(nullable id)delegate;
167 |
168 | #pragma mark -批量下载并缓存
169 | /**
170 | * 批量下载并缓存image(异步) - 已缓存的image不会再次下载缓存
171 | *
172 | * @param urlArray image URL Array
173 | */
174 | +(void)downLoadImageAndCacheWithURLArray:(NSArray * )urlArray;
175 |
176 | /**
177 | 批量下载并缓存image,并回调结果(异步)- 已缓存的image不会再次下载缓存
178 |
179 | @param urlArray image URL Array
180 | @param completedBlock 回调结果为一个字典数组,url:图片的url字符串,result:0表示该图片下载缓存失败,1表示该图片下载并缓存完成或本地缓存中已有该图片
181 | */
182 | +(void)downLoadImageAndCacheWithURLArray:(NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock;
183 |
184 | /**
185 | * 批量下载并缓存视频(异步) - 已缓存的视频不会再次下载缓存
186 | *
187 | * @param urlArray 视频URL Array
188 | */
189 | +(void)downLoadVideoAndCacheWithURLArray:(NSArray * )urlArray;
190 |
191 | /**
192 | 批量下载并缓存视频,并回调结果(异步) - 已缓存的视频不会再次下载缓存
193 |
194 | @param urlArray 视频URL Array
195 | @param completedBlock 回调结果为一个字典数组,url:视频的url字符串,result:0表示该视频下载缓存失败,1表示该视频下载并缓存完成或本地缓存中已有该视频
196 | */
197 | +(void)downLoadVideoAndCacheWithURLArray:(NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock;
198 |
199 | #pragma mark - Action
200 |
201 | /**
202 | 手动移除广告
203 |
204 | @param animated 是否需要动画
205 | */
206 | +(void)removeAndAnimated:(BOOL)animated;
207 |
208 | #pragma mark - 是否已缓存
209 | /**
210 | * 是否已缓存在该图片
211 | *
212 | * @param url image url
213 | *
214 | * @return BOOL
215 | */
216 | +(BOOL)checkImageInCacheWithURL:(NSURL *)url;
217 |
218 | /**
219 | * 是否已缓存该视频
220 | *
221 | * @param url video url
222 | *
223 | * @return BOOL
224 | */
225 | +(BOOL)checkVideoInCacheWithURL:(NSURL *)url;
226 |
227 | #pragma mark - 获取缓存url
228 | /**
229 | 从缓存中获取上一次的ImageURLString(XHLaunchAd 会默认缓存imageURLString)
230 |
231 | @return imageUrlString
232 | */
233 | +(NSString *)cacheImageURLString;
234 |
235 | /**
236 | 从缓存中获取上一次的videoURLString(XHLaunchAd 会默认缓存VideoURLString)
237 |
238 | @return videoUrlString
239 | */
240 | +(NSString *)cacheVideoURLString;
241 |
242 | #pragma mark - 缓存/清理相关
243 | /**
244 | * 清除XHLaunchAd本地所有缓存(异步)
245 | */
246 | +(void)clearDiskCache;
247 |
248 | /**
249 | 清除指定Url的图片本地缓存(异步)
250 |
251 | @param imageUrlArray 需要清除缓存的图片Url数组
252 | */
253 | +(void)clearDiskCacheWithImageUrlArray:(NSArray *)imageUrlArray;
254 |
255 | /**
256 | 清除指定Url除外的图片本地缓存(异步)
257 |
258 | @param exceptImageUrlArray 此url数组的图片缓存将被保留
259 | */
260 | +(void)clearDiskCacheExceptImageUrlArray:(NSArray *)exceptImageUrlArray;
261 |
262 | /**
263 | 清除指定Url的视频本地缓存(异步)
264 |
265 | @param videoUrlArray 需要清除缓存的视频url数组
266 | */
267 | +(void)clearDiskCacheWithVideoUrlArray:(NSArray *)videoUrlArray;
268 |
269 | /**
270 | 清除指定Url除外的视频本地缓存(异步)
271 |
272 | @param exceptVideoUrlArray 此url数组的视频缓存将被保留
273 | */
274 | +(void)clearDiskCacheExceptVideoUrlArray:(NSArray *)exceptVideoUrlArray;
275 |
276 | /**
277 | * 获取XHLaunch本地缓存大小(M)
278 | */
279 | +(float)diskCacheSize;
280 |
281 | /**
282 | * 缓存路径
283 | */
284 | +(NSString *)xhLaunchAdCachePath;
285 |
286 | #pragma mark - 过期
287 | +(void)skipAction XHLaunchAdDeprecated("请使用removeAndAnimated:");
288 | +(void)setLaunchImagesSource:(LaunchImagesSource)launchImagesSource XHLaunchAdDeprecated("请使用setLaunchSourceType:");
289 |
290 | @end
291 | NS_ASSUME_NONNULL_END
292 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAd.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAd.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAd.h"
10 | #import "XHLaunchAdView.h"
11 | #import "XHLaunchAdImageView+XHLaunchAdCache.h"
12 | #import "XHLaunchAdDownloader.h"
13 | #import "XHLaunchAdCache.h"
14 | #import "XHLaunchAdController.h"
15 |
16 | #if __has_include()
17 | #import
18 | #else
19 | #import "FLAnimatedImage.h"
20 | #endif
21 |
22 | typedef NS_ENUM(NSInteger, XHLaunchAdType) {
23 | XHLaunchAdTypeImage,
24 | XHLaunchAdTypeVideo
25 | };
26 |
27 | static NSInteger defaultWaitDataDuration = 3;
28 | static SourceType _sourceType = SourceTypeLaunchImage;
29 | @interface XHLaunchAd()
30 |
31 | @property(nonatomic,assign)XHLaunchAdType launchAdType;
32 | @property(nonatomic,assign)NSInteger waitDataDuration;
33 | @property(nonatomic,strong)XHLaunchImageAdConfiguration * imageAdConfiguration;
34 | @property(nonatomic,strong)XHLaunchVideoAdConfiguration * videoAdConfiguration;
35 | @property(nonatomic,strong)XHLaunchAdButton * skipButton;
36 | @property(nonatomic,strong)XHLaunchAdVideoView * adVideoView;
37 | @property(nonatomic,strong)UIWindow * window;
38 | @property(nonatomic,copy)dispatch_source_t waitDataTimer;
39 | @property(nonatomic,copy)dispatch_source_t skipTimer;
40 | @property (nonatomic, assign) BOOL detailPageShowing;
41 | @property(nonatomic,assign) CGPoint clickPoint;
42 | @end
43 |
44 | @implementation XHLaunchAd
45 | +(void)setLaunchSourceType:(SourceType)sourceType{
46 | _sourceType = sourceType;
47 | }
48 | +(void)setWaitDataDuration:(NSInteger )waitDataDuration{
49 | XHLaunchAd *launchAd = [XHLaunchAd shareLaunchAd];
50 | launchAd.waitDataDuration = waitDataDuration;
51 | }
52 | +(XHLaunchAd *)imageAdWithImageAdConfiguration:(XHLaunchImageAdConfiguration *)imageAdconfiguration{
53 | return [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:nil];
54 | }
55 |
56 | +(XHLaunchAd *)imageAdWithImageAdConfiguration:(XHLaunchImageAdConfiguration *)imageAdconfiguration delegate:(id)delegate{
57 | XHLaunchAd *launchAd = [XHLaunchAd shareLaunchAd];
58 | if(delegate) launchAd.delegate = delegate;
59 | launchAd.imageAdConfiguration = imageAdconfiguration;
60 | return launchAd;
61 | }
62 |
63 | +(XHLaunchAd *)videoAdWithVideoAdConfiguration:(XHLaunchVideoAdConfiguration *)videoAdconfiguration{
64 | return [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:nil];
65 | }
66 |
67 | +(XHLaunchAd *)videoAdWithVideoAdConfiguration:(XHLaunchVideoAdConfiguration *)videoAdconfiguration delegate:(nullable id)delegate{
68 | XHLaunchAd *launchAd = [XHLaunchAd shareLaunchAd];
69 | if(delegate) launchAd.delegate = delegate;
70 | launchAd.videoAdConfiguration = videoAdconfiguration;
71 | return launchAd;
72 | }
73 |
74 | +(void)downLoadImageAndCacheWithURLArray:(NSArray * )urlArray{
75 | [self downLoadImageAndCacheWithURLArray:urlArray completed:nil];
76 | }
77 |
78 | + (void)downLoadImageAndCacheWithURLArray:(NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock{
79 | if(urlArray.count==0) return;
80 | [[XHLaunchAdDownloader sharedDownloader] downLoadImageAndCacheWithURLArray:urlArray completed:completedBlock];
81 | }
82 |
83 | +(void)downLoadVideoAndCacheWithURLArray:(NSArray * )urlArray{
84 | [self downLoadVideoAndCacheWithURLArray:urlArray completed:nil];
85 | }
86 |
87 | +(void)downLoadVideoAndCacheWithURLArray:(NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock{
88 | if(urlArray.count==0) return;
89 | [[XHLaunchAdDownloader sharedDownloader] downLoadVideoAndCacheWithURLArray:urlArray completed:completedBlock];
90 | }
91 | +(void)removeAndAnimated:(BOOL)animated{
92 | [[XHLaunchAd shareLaunchAd] removeAndAnimated:animated];
93 | }
94 |
95 | +(BOOL)checkImageInCacheWithURL:(NSURL *)url{
96 | return [XHLaunchAdCache checkImageInCacheWithURL:url];
97 | }
98 |
99 | +(BOOL)checkVideoInCacheWithURL:(NSURL *)url{
100 | return [XHLaunchAdCache checkVideoInCacheWithURL:url];
101 | }
102 | +(void)clearDiskCache{
103 | [XHLaunchAdCache clearDiskCache];
104 | }
105 |
106 | +(void)clearDiskCacheWithImageUrlArray:(NSArray *)imageUrlArray{
107 | [XHLaunchAdCache clearDiskCacheWithImageUrlArray:imageUrlArray];
108 | }
109 |
110 | +(void)clearDiskCacheExceptImageUrlArray:(NSArray *)exceptImageUrlArray{
111 | [XHLaunchAdCache clearDiskCacheExceptImageUrlArray:exceptImageUrlArray];
112 | }
113 |
114 | +(void)clearDiskCacheWithVideoUrlArray:(NSArray *)videoUrlArray{
115 | [XHLaunchAdCache clearDiskCacheWithVideoUrlArray:videoUrlArray];
116 | }
117 |
118 | +(void)clearDiskCacheExceptVideoUrlArray:(NSArray *)exceptVideoUrlArray{
119 | [XHLaunchAdCache clearDiskCacheExceptVideoUrlArray:exceptVideoUrlArray];
120 | }
121 |
122 | +(float)diskCacheSize{
123 | return [XHLaunchAdCache diskCacheSize];
124 | }
125 |
126 | +(NSString *)xhLaunchAdCachePath{
127 | return [XHLaunchAdCache xhLaunchAdCachePath];
128 | }
129 |
130 | +(NSString *)cacheImageURLString{
131 | return [XHLaunchAdCache getCacheImageUrl];
132 | }
133 |
134 | +(NSString *)cacheVideoURLString{
135 | return [XHLaunchAdCache getCacheVideoUrl];
136 | }
137 |
138 | #pragma mark - 过期
139 | /** 请使用removeAndAnimated: */
140 | +(void)skipAction{
141 | [[XHLaunchAd shareLaunchAd] removeAndAnimated:YES];
142 | }
143 | /** 请使用setLaunchSourceType: */
144 | +(void)setLaunchImagesSource:(LaunchImagesSource)launchImagesSource{
145 | switch (launchImagesSource) {
146 | case LaunchImagesSourceLaunchImage:
147 | _sourceType = SourceTypeLaunchImage;
148 | break;
149 | case LaunchImagesSourceLaunchScreen:
150 | _sourceType = SourceTypeLaunchScreen;
151 | break;
152 | default:
153 | break;
154 | }
155 | }
156 |
157 | #pragma mark - private
158 | +(XHLaunchAd *)shareLaunchAd{
159 | static XHLaunchAd *instance = nil;
160 | static dispatch_once_t oneToken;
161 | dispatch_once(&oneToken,^{
162 | instance = [[XHLaunchAd alloc] init];
163 | });
164 | return instance;
165 | }
166 |
167 | - (instancetype)init{
168 | self = [super init];
169 | if (self) {
170 | XHWeakSelf
171 | [self setupLaunchAd];
172 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
173 | [self setupLaunchAdEnterForeground];
174 | }];
175 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
176 | [self removeOnly];
177 | }];
178 | [[NSNotificationCenter defaultCenter] addObserverForName:XHLaunchAdDetailPageWillShowNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
179 | weakSelf.detailPageShowing = YES;
180 | }];
181 | [[NSNotificationCenter defaultCenter] addObserverForName:XHLaunchAdDetailPageShowFinishNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
182 | weakSelf.detailPageShowing = NO;
183 | }];
184 | }
185 | return self;
186 | }
187 |
188 | -(void)setupLaunchAdEnterForeground{
189 | switch (_launchAdType) {
190 | case XHLaunchAdTypeImage:{
191 | if(!_imageAdConfiguration.showEnterForeground || _detailPageShowing) return;
192 | [self setupLaunchAd];
193 | [self setupImageAdForConfiguration:_imageAdConfiguration];
194 | }
195 | break;
196 | case XHLaunchAdTypeVideo:{
197 | if(!_videoAdConfiguration.showEnterForeground || _detailPageShowing) return;
198 | [self setupLaunchAd];
199 | [self setupVideoAdForConfiguration:_videoAdConfiguration];
200 | }
201 | break;
202 | default:
203 | break;
204 | }
205 | }
206 |
207 | -(void)setupLaunchAd{
208 | UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
209 | window.rootViewController = [XHLaunchAdController new];
210 | window.rootViewController.view.backgroundColor = [UIColor clearColor];
211 | window.rootViewController.view.userInteractionEnabled = NO;
212 | window.windowLevel = UIWindowLevelStatusBar + 1;
213 | window.hidden = NO;
214 | window.alpha = 1;
215 | _window = window;
216 | /** 添加launchImageView */
217 | [_window addSubview:[[XHLaunchImageView alloc] initWithSourceType:_sourceType]];
218 | }
219 |
220 | /**图片*/
221 | -(void)setupImageAdForConfiguration:(XHLaunchImageAdConfiguration *)configuration{
222 | if(_window == nil) return;
223 | [self removeSubViewsExceptLaunchAdImageView];
224 | XHLaunchAdImageView *adImageView = [[XHLaunchAdImageView alloc] init];
225 | [_window addSubview:adImageView];
226 | /** frame */
227 | if(configuration.frame.size.width>0 && configuration.frame.size.height>0) adImageView.frame = configuration.frame;
228 | if(configuration.contentMode) adImageView.contentMode = configuration.contentMode;
229 | /** webImage */
230 | if(configuration.imageNameOrURLString.length && XHISURLString(configuration.imageNameOrURLString)){
231 | [XHLaunchAdCache async_saveImageUrl:configuration.imageNameOrURLString];
232 | /** 自设图片 */
233 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:launchAdImageView:URL:)]) {
234 | [self.delegate xhLaunchAd:self launchAdImageView:adImageView URL:[NSURL URLWithString:configuration.imageNameOrURLString]];
235 | }else{
236 | if(!configuration.imageOption) configuration.imageOption = XHLaunchAdImageDefault;
237 | XHWeakSelf
238 | [adImageView xh_setImageWithURL:[NSURL URLWithString:configuration.imageNameOrURLString] placeholderImage:nil GIFImageCycleOnce:configuration.GIFImageCycleOnce options:configuration.imageOption GIFImageCycleOnceFinish:^{
239 | //GIF不循环,播放完成
240 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdGIFImageCycleOnceFinishNotification object:nil userInfo:@{@"imageNameOrURLString":configuration.imageNameOrURLString}];
241 |
242 | } completed:^(UIImage *image,NSData *imageData,NSError *error,NSURL *url){
243 | if(!error){
244 | #pragma clang diagnostic push
245 | #pragma clang diagnostic ignored"-Wdeprecated-declarations"
246 | if ([weakSelf.delegate respondsToSelector:@selector(xhLaunchAd:imageDownLoadFinish:)]) {
247 | [weakSelf.delegate xhLaunchAd:self imageDownLoadFinish:image];
248 | }
249 | #pragma clang diagnostic pop
250 | if ([weakSelf.delegate respondsToSelector:@selector(xhLaunchAd:imageDownLoadFinish:imageData:)]) {
251 | [weakSelf.delegate xhLaunchAd:self imageDownLoadFinish:image imageData:imageData];
252 | }
253 | }
254 | }];
255 | if(configuration.imageOption == XHLaunchAdImageCacheInBackground){
256 | /** 缓存中未有 */
257 | if(![XHLaunchAdCache checkImageInCacheWithURL:[NSURL URLWithString:configuration.imageNameOrURLString]]){
258 | [self removeAndAnimateDefault]; return; /** 完成显示 */
259 | }
260 | }
261 | }
262 | }else{
263 | if(configuration.imageNameOrURLString.length){
264 | NSData *data = XHDataWithFileName(configuration.imageNameOrURLString);
265 | if(XHISGIFTypeWithData(data)){
266 | FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:data];
267 | adImageView.animatedImage = image;
268 | adImageView.image = nil;
269 | __weak typeof(adImageView) w_adImageView = adImageView;
270 | adImageView.loopCompletionBlock = ^(NSUInteger loopCountRemaining) {
271 | if(configuration.GIFImageCycleOnce){
272 | [w_adImageView stopAnimating];
273 | XHLaunchAdLog(@"GIF不循环,播放完成");
274 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdGIFImageCycleOnceFinishNotification object:@{@"imageNameOrURLString":configuration.imageNameOrURLString}];
275 | }
276 | };
277 | }else{
278 | adImageView.animatedImage = nil;
279 | adImageView.image = [UIImage imageWithData:data];
280 | }
281 | #pragma clang diagnostic push
282 | #pragma clang diagnostic ignored"-Wdeprecated-declarations"
283 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:imageDownLoadFinish:)]) {
284 | [self.delegate xhLaunchAd:self imageDownLoadFinish:[UIImage imageWithData:data]];
285 | }
286 | #pragma clang diagnostic pop
287 | }else{
288 | XHLaunchAdLog(@"未设置广告图片");
289 | }
290 | }
291 | /** skipButton */
292 | [self addSkipButtonForConfiguration:configuration];
293 | [self startSkipDispathTimer];
294 | /** customView */
295 | if(configuration.subViews.count>0) [self addSubViews:configuration.subViews];
296 | XHWeakSelf
297 | adImageView.click = ^(CGPoint point) {
298 | [weakSelf clickAndPoint:point];
299 | };
300 | }
301 |
302 | -(void)addSkipButtonForConfiguration:(XHLaunchAdConfiguration *)configuration{
303 | if(!configuration.duration) configuration.duration = 5;
304 | if(!configuration.skipButtonType) configuration.skipButtonType = SkipTypeTimeText;
305 | if(configuration.customSkipView){
306 | [_window addSubview:configuration.customSkipView];
307 | }else{
308 | if(_skipButton == nil){
309 | _skipButton = [[XHLaunchAdButton alloc] initWithSkipType:configuration.skipButtonType];
310 | _skipButton.hidden = YES;
311 | [_skipButton addTarget:self action:@selector(skipButtonClick:) forControlEvents:UIControlEventTouchUpInside];
312 | }
313 | [_window addSubview:_skipButton];
314 | [_skipButton setTitleWithSkipType:configuration.skipButtonType duration:configuration.duration];
315 | }
316 | }
317 |
318 | /**视频*/
319 | -(void)setupVideoAdForConfiguration:(XHLaunchVideoAdConfiguration *)configuration{
320 | if(_window ==nil) return;
321 | [self removeSubViewsExceptLaunchAdImageView];
322 | if(!_adVideoView){
323 | _adVideoView = [[XHLaunchAdVideoView alloc] init];
324 | }
325 | [_window addSubview:_adVideoView];
326 | /** frame */
327 | if(configuration.frame.size.width>0&&configuration.frame.size.height>0) _adVideoView.frame = configuration.frame;
328 | #pragma clang diagnostic push
329 | #pragma clang diagnostic ignored"-Wdeprecated-declarations"
330 | if(configuration.scalingMode) _adVideoView.videoScalingMode = configuration.scalingMode;
331 | #pragma clang diagnostic pop
332 | if(configuration.videoGravity) _adVideoView.videoGravity = configuration.videoGravity;
333 | _adVideoView.videoCycleOnce = configuration.videoCycleOnce;
334 | if(configuration.videoCycleOnce){
335 | [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
336 | XHLaunchAdLog(@"video不循环,播放完成");
337 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdVideoCycleOnceFinishNotification object:nil userInfo:@{@"videoNameOrURLString":configuration.videoNameOrURLString}];
338 | }];
339 | }
340 | /** video 数据源 */
341 | if(configuration.videoNameOrURLString.length && XHISURLString(configuration.videoNameOrURLString)){
342 | [XHLaunchAdCache async_saveVideoUrl:configuration.videoNameOrURLString];
343 | NSURL *pathURL = [XHLaunchAdCache getCacheVideoWithURL:[NSURL URLWithString:configuration.videoNameOrURLString]];
344 | if(pathURL){
345 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:videoDownLoadFinish:)]) {
346 | [self.delegate xhLaunchAd:self videoDownLoadFinish:pathURL];
347 | }
348 | _adVideoView.contentURL = pathURL;
349 | _adVideoView.muted = configuration.muted;
350 | [_adVideoView.videoPlayer.player play];
351 | }else{
352 | XHWeakSelf
353 | [[XHLaunchAdDownloader sharedDownloader] downloadVideoWithURL:[NSURL URLWithString:configuration.videoNameOrURLString] progress:^(unsigned long long total, unsigned long long current) {
354 | if ([weakSelf.delegate respondsToSelector:@selector(xhLaunchAd:videoDownLoadProgress:total:current:)]) {
355 | [weakSelf.delegate xhLaunchAd:self videoDownLoadProgress:current/(float)total total:total current:current];
356 | }
357 | } completed:^(NSURL * _Nullable location, NSError * _Nullable error){
358 | if(!error){
359 | if ([weakSelf.delegate respondsToSelector:@selector(xhLaunchAd:videoDownLoadFinish:)]){
360 | [weakSelf.delegate xhLaunchAd:self videoDownLoadFinish:location];
361 | }
362 | }
363 | }];
364 | /***视频缓存,提前显示完成 */
365 | [self removeAndAnimateDefault]; return;
366 | }
367 | }else{
368 | if(configuration.videoNameOrURLString.length){
369 | NSURL *pathURL = nil;
370 | NSURL *cachePathURL = [[NSURL alloc] initFileURLWithPath:[XHLaunchAdCache videoPathWithFileName:configuration.videoNameOrURLString]];
371 | //若本地视频未在沙盒缓存文件夹中
372 | if (![XHLaunchAdCache checkVideoInCacheWithFileName:configuration.videoNameOrURLString]) {
373 | /***如果不在沙盒文件夹中则将其复制一份到沙盒缓存文件夹中/下次直接取缓存文件夹文件,加快文件查找速度 */
374 | NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:configuration.videoNameOrURLString withExtension:nil];
375 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
376 | [[NSFileManager defaultManager] copyItemAtURL:bundleURL toURL:cachePathURL error:nil];
377 | });
378 | pathURL = bundleURL;
379 | }else{
380 | pathURL = cachePathURL;
381 | }
382 |
383 | if(pathURL){
384 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:videoDownLoadFinish:)]) {
385 | [self.delegate xhLaunchAd:self videoDownLoadFinish:pathURL];
386 | }
387 | _adVideoView.contentURL = pathURL;
388 | _adVideoView.muted = configuration.muted;
389 | [_adVideoView.videoPlayer.player play];
390 |
391 | }else{
392 | XHLaunchAdLog(@"Error:广告视频未找到,请检查名称是否有误!");
393 | }
394 | }else{
395 | XHLaunchAdLog(@"未设置广告视频");
396 | }
397 | }
398 | /** skipButton */
399 | [self addSkipButtonForConfiguration:configuration];
400 | [self startSkipDispathTimer];
401 | /** customView */
402 | if(configuration.subViews.count>0) [self addSubViews:configuration.subViews];
403 | XHWeakSelf
404 | _adVideoView.click = ^(CGPoint point) {
405 | [weakSelf clickAndPoint:point];
406 | };
407 | }
408 |
409 | #pragma mark - add subViews
410 | -(void)addSubViews:(NSArray *)subViews{
411 | [subViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
412 | [_window addSubview:view];
413 | }];
414 | }
415 |
416 | #pragma mark - set
417 | -(void)setImageAdConfiguration:(XHLaunchImageAdConfiguration *)imageAdConfiguration{
418 | _imageAdConfiguration = imageAdConfiguration;
419 | _launchAdType = XHLaunchAdTypeImage;
420 | [self setupImageAdForConfiguration:imageAdConfiguration];
421 | }
422 |
423 | -(void)setVideoAdConfiguration:(XHLaunchVideoAdConfiguration *)videoAdConfiguration{
424 | _videoAdConfiguration = videoAdConfiguration;
425 | _launchAdType = XHLaunchAdTypeVideo;
426 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(CGFLOAT_MIN * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
427 | [self setupVideoAdForConfiguration:videoAdConfiguration];
428 | });
429 | }
430 |
431 | -(void)setWaitDataDuration:(NSInteger)waitDataDuration{
432 | _waitDataDuration = waitDataDuration;
433 | /** 数据等待 */
434 | [self startWaitDataDispathTiemr];
435 | }
436 |
437 | #pragma mark - Action
438 | -(void)skipButtonClick:(XHLaunchAdButton *)button{
439 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:clickSkipButton:)]) {
440 | [self.delegate xhLaunchAd:self clickSkipButton:button];
441 | }
442 | [self removeAndAnimated:YES];
443 | }
444 |
445 | -(void)removeAndAnimated:(BOOL)animated{
446 | if(animated){
447 | [self removeAndAnimate];
448 | }else{
449 | [self remove];
450 | }
451 | }
452 |
453 | -(void)clickAndPoint:(CGPoint)point{
454 | self.clickPoint = point;
455 | XHLaunchAdConfiguration * configuration = [self commonConfiguration];
456 | #pragma clang diagnostic push
457 | #pragma clang diagnostic ignored"-Wdeprecated-declarations"
458 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:clickAndOpenURLString:)]) {
459 | [self.delegate xhLaunchAd:self clickAndOpenURLString:configuration.openURLString];
460 | [self removeAndAnimateDefault];
461 | }
462 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:clickAndOpenURLString:clickPoint:)]) {
463 | [self.delegate xhLaunchAd:self clickAndOpenURLString:configuration.openURLString clickPoint:point];
464 | [self removeAndAnimateDefault];
465 | }
466 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:clickAndOpenModel:clickPoint:)]) {
467 | [self.delegate xhLaunchAd:self clickAndOpenModel:configuration.openModel clickPoint:point];
468 | [self removeAndAnimateDefault];
469 | }
470 | #pragma clang diagnostic pop
471 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:clickAtOpenModel:clickPoint:)]) {
472 | BOOL status = [self.delegate xhLaunchAd:self clickAtOpenModel:configuration.openModel clickPoint:point];
473 | if(status) [self removeAndAnimateDefault];
474 | }
475 | }
476 |
477 | -(XHLaunchAdConfiguration *)commonConfiguration{
478 | XHLaunchAdConfiguration *configuration = nil;
479 | switch (_launchAdType) {
480 | case XHLaunchAdTypeVideo:
481 | configuration = _videoAdConfiguration;
482 | break;
483 | case XHLaunchAdTypeImage:
484 | configuration = _imageAdConfiguration;
485 | break;
486 | default:
487 | break;
488 | }
489 | return configuration;
490 | }
491 |
492 | -(void)startWaitDataDispathTiemr{
493 | __block NSInteger duration = defaultWaitDataDuration;
494 | if(_waitDataDuration) duration = _waitDataDuration;
495 | _waitDataTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
496 | NSTimeInterval period = 1.0;
497 | dispatch_source_set_timer(_waitDataTimer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0);
498 | dispatch_source_set_event_handler(_waitDataTimer, ^{
499 | if(duration==0){
500 | DISPATCH_SOURCE_CANCEL_SAFE(_waitDataTimer);
501 | dispatch_async(dispatch_get_main_queue(), ^{
502 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdWaitDataDurationArriveNotification object:nil];
503 | [self remove];
504 | return ;
505 | });
506 | }
507 | duration--;
508 | });
509 | dispatch_resume(_waitDataTimer);
510 | }
511 |
512 | -(void)startSkipDispathTimer{
513 | XHLaunchAdConfiguration * configuration = [self commonConfiguration];
514 | DISPATCH_SOURCE_CANCEL_SAFE(_waitDataTimer);
515 | if(!configuration.skipButtonType) configuration.skipButtonType = SkipTypeTimeText;//默认
516 | __block NSInteger duration = 5;//默认
517 | if(configuration.duration) duration = configuration.duration;
518 | if(configuration.skipButtonType == SkipTypeRoundProgressTime || configuration.skipButtonType == SkipTypeRoundProgressText){
519 | [_skipButton startRoundDispathTimerWithDuration:duration];
520 | }
521 | NSTimeInterval period = 1.0;
522 | _skipTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
523 | dispatch_source_set_timer(_skipTimer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0);
524 | dispatch_source_set_event_handler(_skipTimer, ^{
525 | dispatch_async(dispatch_get_main_queue(), ^{
526 | if ([self.delegate respondsToSelector:@selector(xhLaunchAd:customSkipView:duration:)]) {
527 | [self.delegate xhLaunchAd:self customSkipView:configuration.customSkipView duration:duration];
528 | }
529 | if(!configuration.customSkipView){
530 | [_skipButton setTitleWithSkipType:configuration.skipButtonType duration:duration];
531 | }
532 | if(duration==0){
533 | DISPATCH_SOURCE_CANCEL_SAFE(_skipTimer);
534 | [self removeAndAnimate]; return ;
535 | }
536 | duration--;
537 | });
538 | });
539 | dispatch_resume(_skipTimer);
540 | }
541 |
542 | -(void)removeAndAnimate{
543 |
544 | XHLaunchAdConfiguration * configuration = [self commonConfiguration];
545 | CGFloat duration = showFinishAnimateTimeDefault;
546 | if(configuration.showFinishAnimateTime>0) duration = configuration.showFinishAnimateTime;
547 | switch (configuration.showFinishAnimate) {
548 | case ShowFinishAnimateNone:{
549 | [self remove];
550 | }
551 | break;
552 | case ShowFinishAnimateFadein:{
553 | [self removeAndAnimateDefault];
554 | }
555 | break;
556 | case ShowFinishAnimateLite:{
557 | [UIView transitionWithView:_window duration:duration options:UIViewAnimationOptionCurveEaseOut animations:^{
558 | _window.transform = CGAffineTransformMakeScale(1.5, 1.5);
559 | _window.alpha = 0;
560 | } completion:^(BOOL finished) {
561 | [self remove];
562 | }];
563 | }
564 | break;
565 | case ShowFinishAnimateFlipFromLeft:{
566 | [UIView transitionWithView:_window duration:duration options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
567 | _window.alpha = 0;
568 | } completion:^(BOOL finished) {
569 | [self remove];
570 | }];
571 | }
572 | break;
573 | case ShowFinishAnimateFlipFromBottom:{
574 | [UIView transitionWithView:_window duration:duration options:UIViewAnimationOptionTransitionFlipFromBottom animations:^{
575 | _window.alpha = 0;
576 | } completion:^(BOOL finished) {
577 | [self remove];
578 | }];
579 | }
580 | break;
581 | case ShowFinishAnimateCurlUp:{
582 | [UIView transitionWithView:_window duration:duration options:UIViewAnimationOptionTransitionCurlUp animations:^{
583 | _window.alpha = 0;
584 | } completion:^(BOOL finished) {
585 | [self remove];
586 | }];
587 | }
588 | break;
589 | default:{
590 | [self removeAndAnimateDefault];
591 | }
592 | break;
593 | }
594 | }
595 |
596 | -(void)removeAndAnimateDefault{
597 | XHLaunchAdConfiguration * configuration = [self commonConfiguration];
598 | CGFloat duration = showFinishAnimateTimeDefault;
599 | if(configuration.showFinishAnimateTime>0) duration = configuration.showFinishAnimateTime;
600 | [UIView transitionWithView:_window duration:duration options:UIViewAnimationOptionTransitionNone animations:^{
601 | _window.alpha = 0;
602 | } completion:^(BOOL finished) {
603 | [self remove];
604 | }];
605 | }
606 | -(void)removeOnly{
607 | DISPATCH_SOURCE_CANCEL_SAFE(_waitDataTimer)
608 | DISPATCH_SOURCE_CANCEL_SAFE(_skipTimer)
609 | REMOVE_FROM_SUPERVIEW_SAFE(_skipButton)
610 | if(_launchAdType==XHLaunchAdTypeVideo){
611 | if(_adVideoView){
612 | [_adVideoView stopVideoPlayer];
613 | REMOVE_FROM_SUPERVIEW_SAFE(_adVideoView)
614 | }
615 | }
616 | if(_window){
617 | [_window.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
618 | REMOVE_FROM_SUPERVIEW_SAFE(obj)
619 | }];
620 | _window.hidden = YES;
621 | _window = nil;
622 | }
623 | }
624 |
625 | -(void)remove{
626 | [self removeOnly];
627 | #pragma clang diagnostic push
628 | #pragma clang diagnostic ignored"-Wdeprecated-declarations"
629 | if ([self.delegate respondsToSelector:@selector(xhLaunchShowFinish:)]) {
630 | [self.delegate xhLaunchShowFinish:self];
631 | }
632 | #pragma clang diagnostic pop
633 | if ([self.delegate respondsToSelector:@selector(xhLaunchAdShowFinish:)]) {
634 | [self.delegate xhLaunchAdShowFinish:self];
635 | }
636 | }
637 |
638 | -(void)removeSubViewsExceptLaunchAdImageView{
639 | [_window.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
640 | if(![obj isKindOfClass:[XHLaunchImageView class]]){
641 | REMOVE_FROM_SUPERVIEW_SAFE(obj)
642 | }
643 | }];
644 | }
645 | -(void)dealloc{
646 | [[NSNotificationCenter defaultCenter] removeObserver:self];
647 | }
648 | @end
649 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdSkipButton.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/9.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 |
11 | /**
12 | * 倒计时类型
13 | */
14 | typedef NS_ENUM(NSInteger,SkipType) {
15 | SkipTypeNone = 1,//无
16 | /** 方形 */
17 | SkipTypeTime = 2,//方形:倒计时
18 | SkipTypeText = 3,//方形:跳过
19 | SkipTypeTimeText = 4,//方形:倒计时+跳过 (default)
20 | /** 圆形 */
21 | SkipTypeRoundTime = 5,//圆形:倒计时
22 | SkipTypeRoundText = 6,//圆形:跳过
23 | SkipTypeRoundProgressTime = 7,//圆形:进度圈+倒计时
24 | SkipTypeRoundProgressText = 8,//圆形:进度圈+跳过
25 | };
26 |
27 | @interface XHLaunchAdButton : UIButton
28 |
29 | - (instancetype)initWithSkipType:(SkipType)skipType;
30 | - (void)startRoundDispathTimerWithDuration:(CGFloat )duration;
31 | - (void)setTitleWithSkipType:(SkipType)skipType duration:(NSInteger)duration;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdSkipButton.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/9.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdButton.h"
10 | #import "XHLaunchAdConst.h"
11 |
12 | /** Progress颜色 */
13 | #define RoundProgressColor [UIColor whiteColor]
14 | /** 背景色 */
15 | #define BackgroundColor [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4]
16 | /** 字体颜色 */
17 | #define FontColor [UIColor whiteColor]
18 |
19 | #define SkipTitle @"跳过"
20 | /** 倒计时单位 */
21 | #define DurationUnit @"S"
22 |
23 | @interface XHLaunchAdButton()
24 | @property(nonatomic,assign)SkipType skipType;
25 | @property(nonatomic,assign)CGFloat leftRightSpace;
26 | @property(nonatomic,assign)CGFloat topBottomSpace;
27 | @property(nonatomic,strong)UILabel *timeLab;
28 | @property (nonatomic,strong) CAShapeLayer *roundLayer;
29 | @property(nonatomic,copy)dispatch_source_t roundTimer;
30 | @end
31 |
32 | @implementation XHLaunchAdButton
33 |
34 | - (instancetype)initWithSkipType:(SkipType)skipType{
35 | self = [super init];
36 | if (self) {
37 |
38 | _skipType = skipType;
39 | CGFloat y = XH_FULLSCREEN ? 44 : 20;
40 | self.frame = CGRectMake(XH_ScreenW-80,y, 70, 35);//方形
41 | switch (skipType) {
42 | case SkipTypeRoundTime:
43 | case SkipTypeRoundText:
44 | case SkipTypeRoundProgressTime:
45 | case SkipTypeRoundProgressText:{//环形
46 | self.frame = CGRectMake(XH_ScreenW-55,y, 42, 42);
47 | }
48 | break;
49 | default:
50 | break;
51 | }
52 |
53 | switch (skipType) {
54 | case SkipTypeNone:{
55 | self.hidden = YES;
56 | }
57 | break;
58 | case SkipTypeTime:{
59 | [self addSubview:self.timeLab];
60 | self.leftRightSpace = 5;
61 | self.topBottomSpace = 2.5;
62 | }
63 | break;
64 | case SkipTypeText:{
65 | [self addSubview:self.timeLab];
66 | self.leftRightSpace = 5;
67 | self.topBottomSpace = 2.5;
68 | }
69 | break;
70 | case SkipTypeTimeText:{
71 | [self addSubview:self.timeLab];
72 | self.leftRightSpace = 5;
73 | self.topBottomSpace = 2.5;
74 | }
75 | break;
76 | case SkipTypeRoundTime:{
77 | [self addSubview:self.timeLab];
78 | }
79 | break;
80 | case SkipTypeRoundText:{
81 | [self addSubview:self.timeLab];
82 | }
83 | break;
84 | case SkipTypeRoundProgressTime:{
85 | [self addSubview:self.timeLab];
86 | [self.timeLab.layer addSublayer:self.roundLayer];
87 | }
88 | break;
89 | case SkipTypeRoundProgressText:{
90 | [self addSubview:self.timeLab];
91 | [self.timeLab.layer addSublayer:self.roundLayer];
92 | }
93 | break;
94 | default:
95 | break;
96 | }
97 | }
98 | return self;
99 | }
100 |
101 | -(UILabel *)timeLab{
102 | if(_timeLab == nil){
103 | _timeLab = [[UILabel alloc] initWithFrame:self.bounds];
104 | _timeLab.textColor = FontColor;
105 | _timeLab.backgroundColor = BackgroundColor;
106 | _timeLab.layer.masksToBounds = YES;
107 | _timeLab.textAlignment = NSTextAlignmentCenter;
108 | _timeLab.font = [UIFont systemFontOfSize:13.5];
109 | [self cornerRadiusWithView:_timeLab];
110 | }
111 | return _timeLab;
112 | }
113 |
114 | -(CAShapeLayer *)roundLayer{
115 | if(_roundLayer==nil){
116 | _roundLayer = [CAShapeLayer layer];
117 | _roundLayer.fillColor = BackgroundColor.CGColor;
118 | _roundLayer.strokeColor = RoundProgressColor.CGColor;
119 | _roundLayer.lineCap = kCALineCapRound;
120 | _roundLayer.lineJoin = kCALineJoinRound;
121 | _roundLayer.lineWidth = 2;
122 | _roundLayer.frame = self.bounds;
123 | _roundLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.timeLab.bounds.size.width/2.0, self.timeLab.bounds.size.width/2.0) radius:self.timeLab.bounds.size.width/2.0-1.0 startAngle:-0.5*M_PI endAngle:1.5*M_PI clockwise:YES].CGPath;
124 | _roundLayer.strokeStart = 0;
125 | }
126 | return _roundLayer;
127 | }
128 |
129 | - (void)setTitleWithSkipType:(SkipType)skipType duration:(NSInteger)duration{
130 |
131 | switch (skipType) {
132 | case SkipTypeNone:{
133 | self.hidden = YES;
134 | }
135 | break;
136 | case SkipTypeTime:{
137 | self.hidden = NO;
138 | self.timeLab.text = [NSString stringWithFormat:@"%ld %@",duration,DurationUnit];
139 | }
140 | break;
141 | case SkipTypeText:{
142 | self.hidden = NO;
143 | self.timeLab.text = SkipTitle;
144 | }
145 | break;
146 | case SkipTypeTimeText:{
147 | self.hidden = NO;
148 | self.timeLab.text = [NSString stringWithFormat:@"%ld %@",duration,SkipTitle];
149 | }
150 | break;
151 | case SkipTypeRoundTime:{
152 | self.hidden = NO;
153 | self.timeLab.text = [NSString stringWithFormat:@"%ld %@",duration,DurationUnit];
154 | }
155 | break;
156 | case SkipTypeRoundText:{
157 | self.hidden = NO;
158 | self.timeLab.text = SkipTitle;
159 | }
160 | break;
161 | case SkipTypeRoundProgressTime:{
162 | self.hidden = NO;
163 | self.timeLab.text = [NSString stringWithFormat:@"%ld %@",duration,DurationUnit];
164 | }
165 | break;
166 | case SkipTypeRoundProgressText:{
167 | self.hidden = NO;
168 | self.timeLab.text = SkipTitle;
169 | }
170 | break;
171 | default:
172 | break;
173 | }
174 | }
175 |
176 | -(void)startRoundDispathTimerWithDuration:(CGFloat )duration{
177 | NSTimeInterval period = 0.05;
178 | __block CGFloat roundDuration = duration;
179 | _roundTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
180 | dispatch_source_set_timer(_roundTimer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0);
181 | dispatch_source_set_event_handler(_roundTimer, ^{
182 | dispatch_async(dispatch_get_main_queue(), ^{
183 | if(roundDuration<=0){
184 | self.roundLayer.strokeStart = 1;
185 | DISPATCH_SOURCE_CANCEL_SAFE(self.roundTimer);
186 | }
187 | self.roundLayer.strokeStart += 1/(duration/period);
188 | roundDuration -= period;
189 | });
190 | });
191 | dispatch_resume(_roundTimer);
192 | }
193 |
194 | -(void)setLeftRightSpace:(CGFloat)leftRightSpace{
195 | _leftRightSpace = leftRightSpace;
196 | CGRect frame = self.timeLab.frame;
197 | CGFloat width = frame.size.width;
198 | if(leftRightSpace<=0 || leftRightSpace*2>= width) return;
199 | frame = CGRectMake(leftRightSpace, frame.origin.y, width-2*leftRightSpace, frame.size.height);
200 | self.timeLab.frame = frame;
201 | [self cornerRadiusWithView:self.timeLab];
202 | }
203 |
204 | -(void)setTopBottomSpace:(CGFloat)topBottomSpace{
205 | _topBottomSpace = topBottomSpace;
206 | CGRect frame = self.timeLab.frame;
207 | CGFloat height = frame.size.height;
208 | if(topBottomSpace<=0 || topBottomSpace*2>= height) return;
209 | frame = CGRectMake(frame.origin.x, topBottomSpace, frame.size.width, height-2*topBottomSpace);
210 | self.timeLab.frame = frame;
211 | [self cornerRadiusWithView:self.timeLab];
212 | }
213 |
214 | -(void)cornerRadiusWithView:(UIView *)view{
215 | CGFloat min = view.frame.size.height;
216 | if(view.frame.size.height > view.frame.size.width) {
217 | min = view.frame.size.width;
218 | }
219 | view.layer.cornerRadius = min/2.0;
220 | view.layer.masksToBounds = YES;
221 | }
222 |
223 | @end
224 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdCache.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | typedef void(^SaveCompletionBlock)(BOOL result , NSURL * URL);
15 |
16 | @interface XHLaunchAdCache : NSObject
17 |
18 | #pragma mark - 图片
19 | /**
20 | * 获取缓存图片
21 | *
22 | * @param url 图片url
23 | *
24 | * @return 图片
25 | */
26 | +(UIImage *)getCacheImageWithURL:(NSURL *)url;
27 |
28 | /**
29 | 获取缓存图片
30 |
31 | @param url 图片url
32 | @return imageData
33 | */
34 | +(NSData *)getCacheImageDataWithURL:(NSURL *)url;
35 |
36 | /**
37 | 缓存图片
38 |
39 | @param data imageData
40 | @param url 图片url
41 | @return 缓存结果
42 | */
43 | +(BOOL)saveImageData:(NSData *)data imageURL:(NSURL *)url;
44 |
45 | /**
46 | 缓存图片 - 异步
47 |
48 | @param data imageData
49 | @param url 图片url
50 | @param completedBlock 结果回调
51 | */
52 | +(void)async_saveImageData:(NSData *)data imageURL:(NSURL *)url completed:(nullable SaveCompletionBlock)completedBlock;
53 |
54 | /**
55 | * 检查是否已缓存在该图片
56 | *
57 | * @param url image url
58 | *
59 | * @return BOOL
60 | */
61 |
62 | #pragma mark - 视频
63 | +(BOOL)checkImageInCacheWithURL:(NSURL *)url;
64 |
65 | /**
66 | * 检查是否已缓存该视频
67 | *
68 | * @param url video url
69 | *
70 | * @return BOOL
71 | */
72 | +(BOOL)checkVideoInCacheWithURL:(NSURL *)url;
73 |
74 | /**
75 | * 检查是否已缓存该视频(仅限于本地视频读取使用)
76 | *
77 | * @param videoFileName 本地视频文件名称
78 | *
79 | * @return BOOL
80 | */
81 | +(BOOL)checkVideoInCacheWithFileName:(NSString *)videoFileName;
82 |
83 | /**
84 | * 获取缓存视频路径
85 | *
86 | * @param url 视频链接url
87 | * @return 视频本地路径
88 | */
89 | +(nullable NSURL *)getCacheVideoWithURL:(NSURL *)url;
90 |
91 | /**
92 | 保存视频到缓存目录
93 |
94 | @param location 视频路径
95 | @param url 视频url
96 |
97 | @return 缓存结果
98 | */
99 | +(BOOL)saveVideoAtLocation:(NSURL *)location URL:(NSURL *)url;
100 |
101 | /**
102 | 保存视频到缓存目录 - 异步
103 |
104 | @param location 视频路径
105 | @param url 视频url
106 | @param completedBlock 结果回调
107 | */
108 | +(void)async_saveVideoAtLocation:(NSURL *)location URL:(NSURL *)url completed:(nullable SaveCompletionBlock)completedBlock;;
109 |
110 | /**
111 | * 生成视频路径 for url
112 | */
113 | +(NSString *)videoPathWithURL:(NSURL *)url;
114 |
115 | /**
116 | * 生成视频路径 for videoFileName(仅限于本地视频读取使用)
117 | */
118 | +(NSString *)videoPathWithFileName:(NSString *)videoFileName;
119 |
120 | #pragma mark - url缓存
121 | /**
122 | 存储图片url - 异步
123 |
124 | @param url 图片url
125 | */
126 | +(void)async_saveImageUrl:(NSString *)url;
127 |
128 | /**
129 | 获取最后一次缓存的图片url
130 |
131 | @return url string
132 | */
133 | +(NSString *)getCacheImageUrl;
134 |
135 | /**
136 | 存储视频url - 异步
137 |
138 | @param url 视频url
139 | */
140 | +(void)async_saveVideoUrl:(NSString *)url;
141 |
142 | /**
143 | 获取最后一次缓存的视频url
144 |
145 | @return url string
146 | */
147 | +(NSString *)getCacheVideoUrl;
148 |
149 | #pragma mark - 其他
150 | /**
151 | * 缓存路径
152 | */
153 | + (NSString *)xhLaunchAdCachePath;
154 |
155 | /**
156 | * 清除XHLaunchAd本地所有缓存(异步)
157 | */
158 | +(void)clearDiskCache;
159 |
160 | /**
161 | 清除指定Url的图片本地缓存(异步)
162 |
163 | @param imageUrlArray 图片Url数组
164 | */
165 | +(void)clearDiskCacheWithImageUrlArray:(NSArray *)imageUrlArray;
166 |
167 | /**
168 | 清除指定Url除外的图片本地缓存(异步)
169 |
170 | @param exceptImageUrlArray 不需要清除缓存的图片Url数组,此url数组图片缓存将被保留
171 | */
172 | +(void)clearDiskCacheExceptImageUrlArray:(NSArray *)exceptImageUrlArray;
173 |
174 | /**
175 | 清除指定Url的视频本地缓存(异步)
176 |
177 | @param videoUrlArray 视频url数组
178 | */
179 | +(void)clearDiskCacheWithVideoUrlArray:(NSArray *)videoUrlArray;
180 |
181 | /**
182 | 清除指定Url除外的视频本地缓存(异步)
183 |
184 | @param exceptVideoUrlArray 不需要清除缓存的视频Url数组,此url数组视频缓存将被保留
185 | */
186 | +(void)clearDiskCacheExceptVideoUrlArray:(NSArray *)exceptVideoUrlArray;
187 |
188 | /**
189 | * 获取XHLaunch本地缓存大小(M)
190 | */
191 | +(float)diskCacheSize;
192 |
193 | #pragma mark - other
194 |
195 | +(NSString *)md5String:(NSString *)string;
196 |
197 | @end
198 |
199 | NS_ASSUME_NONNULL_END
200 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdCache.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdCache.h"
10 | #import
11 | #import "XHLaunchAdConst.h"
12 |
13 | @implementation XHLaunchAdCache
14 |
15 | +(UIImage *)getCacheImageWithURL:(NSURL *)url{
16 | if(url==nil) return nil;
17 | NSData *data = [NSData dataWithContentsOfFile:[self imagePathWithURL:url]];
18 | return [UIImage imageWithData:data];
19 | }
20 |
21 | +(NSData *)getCacheImageDataWithURL:(NSURL *)url{
22 | if(url==nil) return nil;
23 | return [NSData dataWithContentsOfFile:[self imagePathWithURL:url]];
24 | }
25 |
26 | +(BOOL)saveImageData:(NSData *)data imageURL:(NSURL *)url{
27 | NSString *path = [NSString stringWithFormat:@"%@/%@",[self xhLaunchAdCachePath],[self keyWithURL:url]];
28 | if (data) {
29 | BOOL result = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
30 | if (!result) XHLaunchAdLog(@"cache file error for URL: %@", url);
31 | return result;
32 | }
33 | return NO;
34 | }
35 |
36 | +(void)async_saveImageData:(NSData *)data imageURL:(NSURL *)url completed:(nullable SaveCompletionBlock)completedBlock{
37 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
38 | BOOL result = [self saveImageData:data imageURL:url];
39 | dispatch_async(dispatch_get_main_queue(), ^{
40 | if(completedBlock) completedBlock(result , url);
41 | });
42 | });
43 | }
44 |
45 | +(BOOL)saveVideoAtLocation:(NSURL *)location URL:(NSURL *)url{
46 | NSString *savePath = [[self xhLaunchAdCachePath] stringByAppendingPathComponent:[self videoNameWithURL:url]];
47 | NSURL *savePathUrl = [NSURL fileURLWithPath:savePath];
48 | BOOL result =[[NSFileManager defaultManager] moveItemAtURL:location toURL:savePathUrl error:nil];
49 | if(!result) XHLaunchAdLog(@"cache file error for URL: %@", url);
50 | return result;
51 | }
52 |
53 | +(void)async_saveVideoAtLocation:(NSURL *)location URL:(NSURL *)url completed:(nullable SaveCompletionBlock)completedBlock{
54 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
55 | BOOL result = [self saveVideoAtLocation:location URL:url];
56 | dispatch_async(dispatch_get_main_queue(), ^{
57 | if(completedBlock) completedBlock(result , url);
58 | });
59 | });
60 | }
61 |
62 | +(nullable NSURL *)getCacheVideoWithURL:(NSURL *)url{
63 | NSString *savePath = [[self xhLaunchAdCachePath] stringByAppendingPathComponent:[self videoNameWithURL:url]];
64 | //如果存在
65 | if([[NSFileManager defaultManager] fileExistsAtPath:savePath]){
66 | return [NSURL fileURLWithPath:savePath];
67 | }
68 | return nil;
69 | }
70 |
71 | + (NSString *)xhLaunchAdCachePath{
72 | NSString *path =[NSHomeDirectory() stringByAppendingPathComponent:@"Library/XHLaunchAdCache"];
73 | [self checkDirectory:path];
74 | return path;
75 | }
76 |
77 | +(NSString *)imagePathWithURL:(NSURL *)url{
78 | if(url==nil) return nil;
79 | return [[self xhLaunchAdCachePath] stringByAppendingPathComponent:[self keyWithURL:url]];
80 | }
81 |
82 | +(NSString *)videoPathWithURL:(NSURL *)url{
83 | if(url==nil) return nil;
84 | return [[self xhLaunchAdCachePath] stringByAppendingPathComponent:[self videoNameWithURL:url]];
85 | }
86 |
87 | +(NSString *)videoPathWithFileName:(NSString *)videoFileName{
88 | if(videoFileName.length==0) return nil;
89 | return [[self xhLaunchAdCachePath] stringByAppendingPathComponent:[self videoNameWithURL:[NSURL URLWithString:videoFileName]]];
90 | }
91 |
92 |
93 | +(BOOL)checkImageInCacheWithURL:(NSURL *)url{
94 | return [[NSFileManager defaultManager] fileExistsAtPath:[self imagePathWithURL:url]];
95 | }
96 |
97 | +(BOOL)checkVideoInCacheWithURL:(NSURL *)url{
98 | return [[NSFileManager defaultManager] fileExistsAtPath:[self videoPathWithURL:url]];
99 | }
100 |
101 | +(BOOL)checkVideoInCacheWithFileName:(NSString *)videoFileName{
102 | return [[NSFileManager defaultManager] fileExistsAtPath:[self videoPathWithFileName:videoFileName]];
103 | }
104 |
105 | +(void)checkDirectory:(NSString *)path {
106 | NSFileManager *fileManager = [NSFileManager defaultManager];
107 | BOOL isDir;
108 | if (![fileManager fileExistsAtPath:path isDirectory:&isDir]) {
109 | [self createBaseDirectoryAtPath:path];
110 | } else {
111 | if (!isDir) {
112 | NSError *error = nil;
113 | [fileManager removeItemAtPath:path error:&error];
114 | [self createBaseDirectoryAtPath:path];
115 | }
116 | }
117 | }
118 |
119 | #pragma mark - url缓存
120 | +(void)async_saveImageUrl:(NSString *)url{
121 | if(url==nil) return;
122 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
123 | [[NSUserDefaults standardUserDefaults] setObject:url forKey:XHCacheImageUrlStringKey];
124 | [[NSUserDefaults standardUserDefaults] synchronize];
125 | });
126 | }
127 |
128 | +(NSString *)getCacheImageUrl{
129 | return [[NSUserDefaults standardUserDefaults] objectForKey:XHCacheImageUrlStringKey];
130 | }
131 |
132 | +(void)async_saveVideoUrl:(NSString *)url{
133 | if(url==nil) return;
134 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
135 | [[NSUserDefaults standardUserDefaults] setObject:url forKey:XHCacheVideoUrlStringKey];
136 | [[NSUserDefaults standardUserDefaults] synchronize];
137 | });
138 | }
139 |
140 | +(NSString *)getCacheVideoUrl{
141 | return [[NSUserDefaults standardUserDefaults] objectForKey:XHCacheVideoUrlStringKey];
142 | }
143 |
144 | #pragma mark - 其他
145 | +(void)clearDiskCache{
146 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
147 | NSFileManager *fileManager = [NSFileManager defaultManager];
148 | NSString *path = [self xhLaunchAdCachePath];
149 | [fileManager removeItemAtPath:path error:nil];
150 | [self checkDirectory:[self xhLaunchAdCachePath]];
151 | });
152 | }
153 |
154 | +(void)clearDiskCacheWithImageUrlArray:(NSArray *)imageUrlArray{
155 | if(imageUrlArray.count==0) return;
156 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
157 | [imageUrlArray enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
158 | if([self checkImageInCacheWithURL:obj]){
159 | [[NSFileManager defaultManager] removeItemAtPath:[self imagePathWithURL:obj] error:nil];
160 | }
161 | }];
162 | });
163 | }
164 |
165 | +(void)clearDiskCacheExceptImageUrlArray:(NSArray *)exceptImageUrlArray{
166 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
167 | NSArray *allFilePaths = [self allFilePathWithDirectoryPath:[self xhLaunchAdCachePath]];
168 | NSArray *exceptImagePaths = [self filePathsWithFileUrlArray:exceptImageUrlArray videoType:NO];
169 | [allFilePaths enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
170 | if(![exceptImagePaths containsObject:obj] && !XHISVideoTypeWithPath(obj)){
171 | [[NSFileManager defaultManager] removeItemAtPath:obj error:nil];
172 | }
173 | }];
174 | XHLaunchAdLog(@"allFilePath = %@",allFilePaths);
175 | });
176 | }
177 |
178 | +(void)clearDiskCacheWithVideoUrlArray:(NSArray *)videoUrlArray{
179 | if(videoUrlArray.count==0) return;
180 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
181 | [videoUrlArray enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
182 | if([self checkVideoInCacheWithURL:obj]){
183 | [[NSFileManager defaultManager] removeItemAtPath:[self videoPathWithURL:obj] error:nil];
184 | }
185 | }];
186 | });
187 | }
188 |
189 | +(void)clearDiskCacheExceptVideoUrlArray:(NSArray *)exceptVideoUrlArray{
190 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
191 | NSArray *allFilePaths = [self allFilePathWithDirectoryPath:[self xhLaunchAdCachePath]];
192 | NSArray *exceptVideoPaths = [self filePathsWithFileUrlArray:exceptVideoUrlArray videoType:YES];
193 | [allFilePaths enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
194 | if(![exceptVideoPaths containsObject:obj] && XHISVideoTypeWithPath(obj)){
195 | [[NSFileManager defaultManager] removeItemAtPath:obj error:nil];
196 | }
197 | }];
198 | XHLaunchAdLog(@"allFilePath = %@",allFilePaths);
199 | });
200 | }
201 |
202 | +(float)diskCacheSize{
203 | NSString *directoryPath = [self xhLaunchAdCachePath];
204 | BOOL isDir = NO;
205 | unsigned long long total = 0;
206 | if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) {
207 | if (isDir) {
208 | NSError *error = nil;
209 | NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error];
210 | if (error == nil) {
211 | for (NSString *subpath in array) {
212 | NSString *path = [directoryPath stringByAppendingPathComponent:subpath];
213 | NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
214 | if (!error) {
215 | total += [dict[NSFileSize] unsignedIntegerValue];
216 | }
217 | }
218 | }
219 | }
220 | }
221 | return total/(1024.0*1024.0);
222 | }
223 |
224 | +(NSArray *)filePathsWithFileUrlArray:(NSArray *)fileUrlArray videoType:(BOOL)videoType{
225 | NSMutableArray *filePaths = [[NSMutableArray alloc] init];
226 | [fileUrlArray enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
227 | NSString *path;
228 | if(videoType){
229 | path = [self videoPathWithURL:obj];
230 | }else{
231 | path = [self imagePathWithURL:obj];
232 | }
233 | [filePaths addObject:path];
234 | }];
235 | return filePaths;
236 | }
237 |
238 | +(NSArray*)allFilePathWithDirectoryPath:(NSString*)directoryPath{
239 | NSMutableArray* array = [[NSMutableArray alloc] init];
240 | NSFileManager* fileManager = [NSFileManager defaultManager];
241 | NSArray* tempArray = [fileManager contentsOfDirectoryAtPath:directoryPath error:nil];
242 | for (NSString* fileName in tempArray) {
243 | BOOL flag = YES;
244 | NSString* fullPath = [directoryPath stringByAppendingPathComponent:fileName];
245 | if ([fileManager fileExistsAtPath:fullPath isDirectory:&flag]) {
246 | if (!flag) {
247 | [array addObject:fullPath];
248 | }
249 | }
250 | }
251 | return array;
252 | }
253 |
254 | + (void)createBaseDirectoryAtPath:(NSString *)path {
255 | __autoreleasing NSError *error = nil;
256 | [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
257 | if (error) {
258 | XHLaunchAdLog(@"create cache directory failed, error = %@", error);
259 | } else {
260 | [self addDoNotBackupAttribute:path];
261 | }
262 | XHLaunchAdLog(@"XHLaunchAdCachePath = %@",path);
263 | }
264 |
265 | + (void)addDoNotBackupAttribute:(NSString *)path {
266 | NSURL *url = [NSURL fileURLWithPath:path];
267 | NSError *error = nil;
268 | [url setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
269 | if (error) {
270 | XHLaunchAdLog(@"error to set do not backup attribute, error = %@", error);
271 | }
272 | }
273 |
274 | +(NSString *)md5String:(NSString *)string{
275 | const char *value = [string UTF8String];
276 | unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
277 | CC_MD5(value, (CC_LONG)strlen(value), outputBuffer);
278 | NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
279 | for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
280 | [outputString appendFormat:@"%02x",outputBuffer[count]];
281 | }
282 | return outputString;
283 | }
284 |
285 | +(NSString *)videoNameWithURL:(NSURL *)url{
286 | return [[self md5String:url.absoluteString] stringByAppendingString:@".mp4"];
287 | }
288 |
289 | +(NSString *)keyWithURL:(NSURL *)url{
290 | return [self md5String:url.absoluteString];
291 | }
292 |
293 | @end
294 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdConfiguration.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdConfiguration.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 | #import
11 | #import "XHLaunchAdButton.h"
12 | #import
13 | #import
14 | #import "XHLaunchAdImageManager.h"
15 | #import "XHLaunchAdConst.h"
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | /** 显示完成动画时间默认时间 */
20 | static CGFloat const showFinishAnimateTimeDefault = 0.8;
21 |
22 | /** 显示完成动画类型 */
23 | typedef NS_ENUM(NSInteger , ShowFinishAnimate) {
24 | /** 无动画 */
25 | ShowFinishAnimateNone = 1,
26 | /** 普通淡入(default) */
27 | ShowFinishAnimateFadein = 2,
28 | /** 放大淡入 */
29 | ShowFinishAnimateLite = 3,
30 | /** 左右翻转(类似网易云音乐) */
31 | ShowFinishAnimateFlipFromLeft = 4,
32 | /** 下上翻转 */
33 | ShowFinishAnimateFlipFromBottom = 5,
34 | /** 向上翻页 */
35 | ShowFinishAnimateCurlUp = 6,
36 | };
37 |
38 | #pragma mark - 公共属性
39 | @interface XHLaunchAdConfiguration : NSObject
40 |
41 | /** 停留时间(default 5 ,单位:秒) */
42 | @property(nonatomic,assign)NSInteger duration;
43 |
44 | /** 跳过按钮类型(default SkipTypeTimeText) */
45 | @property(nonatomic,assign)SkipType skipButtonType;
46 |
47 | /** 显示完成动画(default ShowFinishAnimateFadein) */
48 | @property(nonatomic,assign)ShowFinishAnimate showFinishAnimate;
49 |
50 | /** 显示完成动画时间(default 0.8 , 单位:秒) */
51 | @property(nonatomic,assign)CGFloat showFinishAnimateTime;
52 |
53 | /** 设置开屏广告的frame(default [UIScreen mainScreen].bounds) */
54 | @property (nonatomic,assign) CGRect frame;
55 |
56 | /** 程序从后台恢复时,是否需要展示广告(defailt NO) */
57 | @property (nonatomic,assign) BOOL showEnterForeground;
58 |
59 | /** 点击打开页面地址(请使用openModel,点击事件代理方法请对应使用xhLaunchAd:clickAndOpenModel:clickPoint:) */
60 | @property(nonatomic,copy)NSString *openURLString XHLaunchAdDeprecated("请使用openModel,点击事件代理方法请对应使用xhLaunchAd:clickAndOpenModel:clickPoint:");
61 |
62 | /** 点击打开页面参数 */
63 | @property (nonatomic, strong) id openModel;
64 |
65 | /** 自定义跳过按钮(若定义此视图,将会自定替换系统跳过按钮) */
66 | @property(nonatomic,strong) UIView *customSkipView;
67 |
68 | /** 子视图(若定义此属性,这些视图将会被自动添加在广告视图上,frame相对于window) */
69 | @property(nonatomic,copy,nullable) NSArray *subViews;
70 |
71 | @end
72 |
73 | #pragma mark - 图片广告相关
74 | @interface XHLaunchImageAdConfiguration : XHLaunchAdConfiguration
75 |
76 | /** image本地图片名(jpg/gif图片请带上扩展名)或网络图片URL string */
77 | @property(nonatomic,copy)NSString *imageNameOrURLString;
78 |
79 | /** 图片广告缩放模式(default UIViewContentModeScaleToFill) */
80 | @property(nonatomic,assign)UIViewContentMode contentMode;
81 |
82 | /** 缓存机制(default XHLaunchImageDefault) */
83 | @property(nonatomic,assign)XHLaunchAdImageOptions imageOption;
84 |
85 | /** 设置GIF动图是否只循环播放一次(YES:只播放一次,NO:循环播放,default NO,仅对动图设置有效) */
86 | @property (nonatomic, assign) BOOL GIFImageCycleOnce;
87 |
88 | +(XHLaunchImageAdConfiguration *)defaultConfiguration;
89 |
90 | @end
91 |
92 | #pragma mark - 视频广告相关
93 | @interface XHLaunchVideoAdConfiguration : XHLaunchAdConfiguration
94 |
95 | /** video本地名或网络链接URL string */
96 | @property(nonatomic,copy)NSString *videoNameOrURLString;
97 |
98 | /** 视频缩放模式(default MPMovieScalingModeAspectFill) */
99 | @property(nonatomic,assign)MPMovieScalingMode scalingMode XHLaunchAdDeprecated("请使用videoGravity");
100 |
101 | /** 视频缩放模式(default AVLayerVideoGravityResizeAspectFill) */
102 | @property (nonatomic, copy) AVLayerVideoGravity videoGravity;
103 |
104 | /** 设置视频是否只循环播放一次(YES:只播放一次,NO循环播放,default NO) */
105 | @property (nonatomic, assign) BOOL videoCycleOnce;
106 |
107 | /** 是否关闭音频(default NO) */
108 | @property (nonatomic, assign) BOOL muted;
109 |
110 | +(XHLaunchVideoAdConfiguration *)defaultConfiguration;
111 |
112 | @end
113 |
114 | NS_ASSUME_NONNULL_END
115 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdConfiguration.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdConfiguration.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdConfiguration.h"
10 |
11 | #pragma mark - 公共
12 | @implementation XHLaunchAdConfiguration
13 |
14 | @end
15 |
16 | #pragma mark - 图片广告相关
17 | @implementation XHLaunchImageAdConfiguration
18 |
19 | +(XHLaunchImageAdConfiguration *)defaultConfiguration{
20 | //配置广告数据
21 | XHLaunchImageAdConfiguration *configuration = [XHLaunchImageAdConfiguration new];
22 | //广告停留时间
23 | configuration.duration = 5;
24 | //广告frame
25 | configuration.frame = [UIScreen mainScreen].bounds;
26 | //设置GIF动图是否只循环播放一次(仅对动图设置有效)
27 | configuration.GIFImageCycleOnce = NO;
28 | //缓存机制
29 | configuration.imageOption = XHLaunchAdImageDefault;
30 | //图片填充模式
31 | configuration.contentMode = UIViewContentModeScaleToFill;
32 | //广告显示完成动画
33 | configuration.showFinishAnimate =ShowFinishAnimateFadein;
34 | //显示完成动画时间
35 | configuration.showFinishAnimateTime = showFinishAnimateTimeDefault;
36 | //跳过按钮类型
37 | configuration.skipButtonType = SkipTypeTimeText;
38 | //后台返回时,是否显示广告
39 | configuration.showEnterForeground = NO;
40 | return configuration;
41 | }
42 |
43 | @end
44 |
45 | #pragma mark - 视频广告相关
46 | @implementation XHLaunchVideoAdConfiguration
47 | +(XHLaunchVideoAdConfiguration *)defaultConfiguration{
48 | //配置广告数据
49 | XHLaunchVideoAdConfiguration *configuration = [XHLaunchVideoAdConfiguration new];
50 | //广告停留时间
51 | configuration.duration = 5;
52 | //广告frame
53 | configuration.frame = [UIScreen mainScreen].bounds;
54 | //视频填充模式
55 | configuration.videoGravity = AVLayerVideoGravityResizeAspectFill;
56 | //是否只循环播放一次
57 | configuration.videoCycleOnce = NO;
58 | //广告显示完成动画
59 | configuration.showFinishAnimate =ShowFinishAnimateFadein;
60 | //显示完成动画时间
61 | configuration.showFinishAnimateTime = showFinishAnimateTimeDefault;
62 | //跳过按钮类型
63 | configuration.skipButtonType = SkipTypeTimeText;
64 | //后台返回时,是否显示广告
65 | configuration.showEnterForeground = NO;
66 | //是否静音播放
67 | configuration.muted = NO;
68 | return configuration;
69 | }
70 | @end
71 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdConst.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdConst.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/9/18.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 |
11 | #define XHLaunchAdDeprecated(instead) __attribute__((deprecated(instead)))
12 |
13 | #define XHWeakSelf __weak typeof(self) weakSelf = self;
14 |
15 | #define XH_ScreenW [UIScreen mainScreen].bounds.size.width
16 | #define XH_ScreenH [UIScreen mainScreen].bounds.size.height
17 |
18 | #define XH_IPHONEX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
19 | #define XH_IPHONEXR ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(828, 1792), [[UIScreen mainScreen] currentMode].size) : NO)
20 | #define XH_IPHONEXSMAX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2688), [[UIScreen mainScreen] currentMode].size) : NO)
21 | #define XH_FULLSCREEN ((XH_IPHONEX || XH_IPHONEXR || XH_IPHONEXSMAX) ? YES : NO)
22 |
23 |
24 | #define XHISURLString(string) ([string hasPrefix:@"https://"] || [string hasPrefix:@"http://"]) ? YES:NO
25 | #define XHStringContainsSubString(string,subString) ([string rangeOfString:subString].location == NSNotFound) ? NO:YES
26 |
27 | #ifdef DEBUG
28 | #define XHLaunchAdLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
29 | #else
30 | #define XHLaunchAdLog(...)
31 | #endif
32 |
33 | #define XHISGIFTypeWithData(data)\
34 | ({\
35 | BOOL result = NO;\
36 | if(!data) result = NO;\
37 | uint8_t c;\
38 | [data getBytes:&c length:1];\
39 | if(c == 0x47) result = YES;\
40 | (result);\
41 | })
42 |
43 | #define XHISVideoTypeWithPath(path)\
44 | ({\
45 | BOOL result = NO;\
46 | if([path hasSuffix:@".mp4"]) result = YES;\
47 | (result);\
48 | })
49 |
50 | #define XHDataWithFileName(name)\
51 | ({\
52 | NSData *data = nil;\
53 | NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:nil];\
54 | if([[NSFileManager defaultManager] fileExistsAtPath:path]){\
55 | data = [NSData dataWithContentsOfFile:path];\
56 | }\
57 | (data);\
58 | })
59 |
60 | #define DISPATCH_SOURCE_CANCEL_SAFE(time) if(time)\
61 | {\
62 | dispatch_source_cancel(time);\
63 | time = nil;\
64 | }
65 |
66 | #define REMOVE_FROM_SUPERVIEW_SAFE(view) if(view)\
67 | {\
68 | [view removeFromSuperview];\
69 | view = nil;\
70 | }
71 |
72 | UIKIT_EXTERN NSString *const XHCacheImageUrlStringKey;
73 | UIKIT_EXTERN NSString *const XHCacheVideoUrlStringKey;
74 |
75 | UIKIT_EXTERN NSString *const XHLaunchAdWaitDataDurationArriveNotification;
76 | UIKIT_EXTERN NSString *const XHLaunchAdDetailPageWillShowNotification;
77 | UIKIT_EXTERN NSString *const XHLaunchAdDetailPageShowFinishNotification;
78 | /** GIFImageCycleOnce = YES(GIF不循环)时, GIF动图播放完成通知 */
79 | UIKIT_EXTERN NSString *const XHLaunchAdGIFImageCycleOnceFinishNotification;
80 | /** videoCycleOnce = YES(视频不循环时) ,video播放完成通知 */
81 | UIKIT_EXTERN NSString *const XHLaunchAdVideoCycleOnceFinishNotification;
82 | /** 视频播放失败通知 */
83 | UIKIT_EXTERN NSString *const XHLaunchAdVideoPlayFailedNotification;
84 | UIKIT_EXTERN BOOL XHLaunchAdPrefersHomeIndicatorAutoHidden;
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdConst.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdConst.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/9/18.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdConst.h"
10 |
11 | NSString *const XHCacheImageUrlStringKey = @"XHCacheImageUrlStringKey";
12 | NSString *const XHCacheVideoUrlStringKey = @"XHCacheVideoUrlStringKey";
13 |
14 | NSString *const XHLaunchAdWaitDataDurationArriveNotification = @"XHLaunchAdWaitDataDurationArriveNotification";
15 | NSString *const XHLaunchAdDetailPageWillShowNotification = @"XHLaunchAdDetailPageWillShowNotification";
16 | NSString *const XHLaunchAdDetailPageShowFinishNotification = @"XHLaunchAdDetailPageShowFinishNotification";
17 | NSString *const XHLaunchAdGIFImageCycleOnceFinishNotification = @"XHLaunchAdGIFImageCycleOnceFinishNotification";
18 | NSString *const XHLaunchAdVideoCycleOnceFinishNotification = @"XHLaunchAdVideoCycleOnceFinishNotification";
19 | NSString *const XHLaunchAdVideoPlayFailedNotification = @"XHLaunchAdVideoPlayFailedNotification";
20 |
21 | BOOL XHLaunchAdPrefersHomeIndicatorAutoHidden = NO;
22 |
23 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdController.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdController.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/5/26.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 |
11 | @interface XHLaunchAdController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdController.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdController.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/5/26.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdController.h"
10 | #import "XHLaunchAdConst.h"
11 |
12 | @interface XHLaunchAdController ()
13 |
14 | @end
15 |
16 | @implementation XHLaunchAdController
17 |
18 | -(BOOL)shouldAutorotate{
19 |
20 | return NO;
21 | }
22 |
23 | -(BOOL)prefersHomeIndicatorAutoHidden{
24 |
25 | return XHLaunchAdPrefersHomeIndicatorAutoHidden;
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdDownloader.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdDownloaderManager.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/12/1.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 | #import
11 |
12 | #pragma mark - XHLaunchAdDownload
13 |
14 | typedef void(^XHLaunchAdDownloadProgressBlock)(unsigned long long total, unsigned long long current);
15 |
16 | typedef void(^XHLaunchAdDownloadImageCompletedBlock)(UIImage *_Nullable image, NSData * _Nullable data, NSError * _Nullable error);
17 |
18 | typedef void(^XHLaunchAdDownloadVideoCompletedBlock)(NSURL * _Nullable location, NSError * _Nullable error);
19 |
20 | typedef void(^XHLaunchAdBatchDownLoadAndCacheCompletedBlock) (NSArray * _Nonnull completedArray);
21 |
22 | @protocol XHLaunchAdDownloadDelegate
23 |
24 | - (void)downloadFinishWithURL:(nonnull NSURL *)url;
25 |
26 | @end
27 |
28 | @interface XHLaunchAdDownload : NSObject
29 | @property (assign, nonatomic ,nonnull)id delegate;
30 | @end
31 |
32 | @interface XHLaunchAdImageDownload : XHLaunchAdDownload
33 |
34 | @end
35 |
36 | @interface XHLaunchAdVideoDownload : XHLaunchAdDownload
37 |
38 | @end
39 |
40 | #pragma mark - XHLaunchAdDownloader
41 | @interface XHLaunchAdDownloader : NSObject
42 |
43 | +(nonnull instancetype )sharedDownloader;
44 |
45 | - (void)downloadImageWithURL:(nonnull NSURL *)url progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHLaunchAdDownloadImageCompletedBlock)completedBlock;
46 |
47 | - (void)downLoadImageAndCacheWithURLArray:(nonnull NSArray * )urlArray;
48 | - (void)downLoadImageAndCacheWithURLArray:(nonnull NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock;
49 |
50 | - (void)downloadVideoWithURL:(nonnull NSURL *)url progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHLaunchAdDownloadVideoCompletedBlock)completedBlock;
51 |
52 | - (void)downLoadVideoAndCacheWithURLArray:(nonnull NSArray * )urlArray;
53 | - (void)downLoadVideoAndCacheWithURLArray:(nonnull NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock;
54 |
55 | @end
56 |
57 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdDownloader.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdDownloaderManager.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/12/1.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdDownloader.h"
10 | #import "XHLaunchAdCache.h"
11 | #import "XHLaunchAdConst.h"
12 |
13 | #if __has_include()
14 | #import
15 | #else
16 | #import "FLAnimatedImage.h"
17 | #endif
18 |
19 | #pragma mark - XHLaunchAdDownload
20 |
21 | @interface XHLaunchAdDownload()
22 |
23 | @property (strong, nonatomic) NSURLSession *session;
24 | @property(strong,nonatomic)NSURLSessionDownloadTask *downloadTask;
25 | @property (nonatomic, assign) unsigned long long totalLength;
26 | @property (nonatomic, assign) unsigned long long currentLength;
27 | @property (nonatomic, copy) XHLaunchAdDownloadProgressBlock progressBlock;
28 | @property (strong, nonatomic) NSURL *url;
29 |
30 | @end
31 | @implementation XHLaunchAdDownload
32 |
33 | @end
34 |
35 | #pragma mark - XHLaunchAdImageDownload
36 | @interface XHLaunchAdImageDownload()
37 |
38 | @property (nonatomic, copy ) XHLaunchAdDownloadImageCompletedBlock completedBlock;
39 |
40 | @end
41 | @implementation XHLaunchAdImageDownload
42 |
43 | -(nonnull instancetype)initWithURL:(nonnull NSURL *)url delegateQueue:(nonnull NSOperationQueue *)queue progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHLaunchAdDownloadImageCompletedBlock)completedBlock{
44 | self = [super init];
45 | if (self) {
46 | self.url = url;
47 | self.progressBlock = progressBlock;
48 | self.completedBlock = completedBlock;
49 | NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
50 | sessionConfiguration.timeoutIntervalForRequest = 15.0;
51 | self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
52 | delegate:self
53 | delegateQueue:queue];
54 | self.downloadTask = [self.session downloadTaskWithRequest:[NSURLRequest requestWithURL:url]];
55 | [self.downloadTask resume];
56 | }
57 | return self;
58 | }
59 |
60 | #pragma mark - NSURLSessionDownloadDelegate
61 | - (void)URLSession:(NSURLSession *)session
62 | downloadTask:(NSURLSessionDownloadTask *)downloadTask
63 | didFinishDownloadingToURL:(NSURL *)location {
64 | NSData *data = [NSData dataWithContentsOfURL:location];
65 | UIImage *image = [UIImage imageWithData:data];
66 | dispatch_async(dispatch_get_main_queue(), ^{
67 | if (self.completedBlock) {
68 | self.completedBlock(image,data, nil);
69 | // 防止重复调用
70 | self.completedBlock = nil;
71 | }
72 | //下载完成回调
73 | if ([self.delegate respondsToSelector:@selector(downloadFinishWithURL:)]) {
74 | [self.delegate downloadFinishWithURL:self.url];
75 | }
76 | });
77 | //销毁
78 | [self.session invalidateAndCancel];
79 | self.session = nil;
80 | }
81 |
82 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
83 | self.currentLength = totalBytesWritten;
84 | self.totalLength = totalBytesExpectedToWrite;
85 | if (self.progressBlock) {
86 | self.progressBlock(self.totalLength, self.currentLength);
87 | }
88 | }
89 |
90 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
91 | if (error){
92 | XHLaunchAdLog(@"error = %@",error);
93 | dispatch_async(dispatch_get_main_queue(), ^{
94 | if (self.completedBlock) {
95 | self.completedBlock(nil,nil, error);
96 | }
97 | self.completedBlock = nil;
98 | });
99 | }
100 | }
101 |
102 | //处理HTTPS请求的
103 | - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
104 | NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
105 | if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
106 | SecTrustRef serverTrust = protectionSpace.serverTrust;
107 | completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:serverTrust]);
108 | } else {
109 | completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
110 | }
111 | }
112 |
113 | @end
114 |
115 | #pragma makr - XHLaunchAdVideoDownload
116 | @interface XHLaunchAdVideoDownload()
117 |
118 | @property (nonatomic, copy ) XHLaunchAdDownloadVideoCompletedBlock completedBlock;
119 | @end
120 | @implementation XHLaunchAdVideoDownload
121 |
122 | -(nonnull instancetype)initWithURL:(nonnull NSURL *)url delegateQueue:(nonnull NSOperationQueue *)queue progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHLaunchAdDownloadVideoCompletedBlock)completedBlock{
123 | self = [super init];
124 | if (self) {
125 | self.url = url;
126 | self.progressBlock = progressBlock;
127 | _completedBlock = completedBlock;
128 | NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
129 | sessionConfiguration.timeoutIntervalForRequest = 15.0;
130 | self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
131 | delegate:self
132 | delegateQueue:queue];
133 | self.downloadTask = [self.session downloadTaskWithRequest:[NSURLRequest requestWithURL:url]];
134 | [self.downloadTask resume];
135 | }
136 | return self;
137 | }
138 |
139 | #pragma mark - NSURLSessionDownloadDelegate
140 | - (void)URLSession:(NSURLSession *)session
141 | downloadTask:(NSURLSessionDownloadTask *)downloadTask
142 | didFinishDownloadingToURL:(NSURL *)location {
143 | NSError *error=nil;
144 | NSURL *toURL = [NSURL fileURLWithPath:[XHLaunchAdCache videoPathWithURL:self.url]];
145 |
146 | [[NSFileManager defaultManager] copyItemAtURL:location toURL:toURL error:&error];//复制到缓存目录
147 |
148 | if(error) XHLaunchAdLog(@"error = %@",error);
149 | dispatch_async(dispatch_get_main_queue(), ^{
150 | if (self.completedBlock) {
151 | if(!error){
152 | self.completedBlock(toURL,nil);
153 | }else{
154 | self.completedBlock(nil,error);
155 | }
156 | // 防止重复调用
157 | self.completedBlock = nil;
158 | }
159 | //下载完成回调
160 | if ([self.delegate respondsToSelector:@selector(downloadFinishWithURL:)]) {
161 | [self.delegate downloadFinishWithURL:self.url];
162 | }
163 | });
164 | [self.session invalidateAndCancel];
165 | self.session = nil;
166 | }
167 |
168 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
169 | self.currentLength = totalBytesWritten;
170 | self.totalLength = totalBytesExpectedToWrite;
171 | if (self.progressBlock) {
172 | self.progressBlock(self.totalLength, self.currentLength);
173 | }
174 | }
175 |
176 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
177 | if (error){
178 | XHLaunchAdLog(@"error = %@",error);
179 | dispatch_async(dispatch_get_main_queue(), ^{
180 | if (self.completedBlock) {
181 | self.completedBlock(nil, error);
182 | }
183 | self.completedBlock = nil;
184 | });
185 | }
186 | }
187 | @end
188 |
189 | #pragma mark - XHLaunchAdDownloader
190 | @interface XHLaunchAdDownloader()
191 | @property (strong, nonatomic, nonnull) NSOperationQueue *downloadImageQueue;
192 | @property (strong, nonatomic, nonnull) NSOperationQueue *downloadVideoQueue;
193 | @property (strong, nonatomic) NSMutableDictionary *allDownloadDict;
194 | @end
195 |
196 | @implementation XHLaunchAdDownloader
197 |
198 | +(nonnull instancetype )sharedDownloader{
199 | static XHLaunchAdDownloader *instance = nil;
200 | static dispatch_once_t oneToken;
201 | dispatch_once(&oneToken,^{
202 | instance = [[XHLaunchAdDownloader alloc] init];
203 | });
204 | return instance;
205 | }
206 |
207 | - (instancetype)init{
208 | self = [super init];
209 | if (self) {
210 | _downloadImageQueue = [NSOperationQueue new];
211 | _downloadImageQueue.maxConcurrentOperationCount = 6;
212 | _downloadImageQueue.name = @"com.it7090.XHLaunchAdDownloadImageQueue";
213 | _downloadVideoQueue = [NSOperationQueue new];
214 | _downloadVideoQueue.maxConcurrentOperationCount = 3;
215 | _downloadVideoQueue.name = @"com.it7090.XHLaunchAdDownloadVideoQueue";
216 | XHLaunchAdLog(@"XHLaunchAdCachePath:%@",[XHLaunchAdCache xhLaunchAdCachePath]);
217 | }
218 | return self;
219 | }
220 |
221 | - (void)downloadImageWithURL:(nonnull NSURL *)url progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHLaunchAdDownloadImageCompletedBlock)completedBlock{
222 | NSString *key = [self keyWithURL:url];
223 | if(self.allDownloadDict[key]) return;
224 | XHLaunchAdImageDownload * download = [[XHLaunchAdImageDownload alloc] initWithURL:url delegateQueue:_downloadImageQueue progress:progressBlock completed:completedBlock];
225 | download.delegate = self;
226 | [self.allDownloadDict setObject:download forKey:key];
227 | }
228 |
229 | - (void)downloadImageAndCacheWithURL:(nonnull NSURL *)url completed:(void(^)(BOOL result))completedBlock{
230 | if(url == nil){
231 | if(completedBlock) completedBlock(NO);
232 | return;
233 | }
234 | [self downloadImageWithURL:url progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error) {
235 | if(error){
236 | if(completedBlock) completedBlock(NO);
237 | }else{
238 | [XHLaunchAdCache async_saveImageData:data imageURL:url completed:^(BOOL result, NSURL * _Nonnull URL) {
239 | if(completedBlock) completedBlock(result);
240 | }];
241 | }
242 | }];
243 | }
244 |
245 | -(void)downLoadImageAndCacheWithURLArray:(NSArray *)urlArray{
246 | [self downLoadImageAndCacheWithURLArray:urlArray completed:nil];
247 | }
248 |
249 | - (void)downLoadImageAndCacheWithURLArray:(nonnull NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock{
250 | if(urlArray.count==0) return;
251 | __block NSMutableArray * resultArray = [[NSMutableArray alloc] init];
252 | dispatch_group_t downLoadGroup = dispatch_group_create();
253 | [urlArray enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idx, BOOL *stop) {
254 | if(![XHLaunchAdCache checkImageInCacheWithURL:url]){
255 | dispatch_group_enter(downLoadGroup);
256 | [self downloadImageAndCacheWithURL:url completed:^(BOOL result) {
257 | dispatch_group_leave(downLoadGroup);
258 | [resultArray addObject:@{@"url":url.absoluteString,@"result":@(result)}];
259 | }];
260 | }else{
261 | [resultArray addObject:@{@"url":url.absoluteString,@"result":@(YES)}];
262 | }
263 | }];
264 | dispatch_group_notify(downLoadGroup, dispatch_get_main_queue(), ^{
265 | if(completedBlock) completedBlock(resultArray);
266 | });
267 | }
268 |
269 | - (void)downloadVideoWithURL:(nonnull NSURL *)url progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHLaunchAdDownloadVideoCompletedBlock)completedBlock{
270 | NSString *key = [self keyWithURL:url];
271 | if(self.allDownloadDict[key]) return;
272 | XHLaunchAdVideoDownload * download = [[XHLaunchAdVideoDownload alloc] initWithURL:url delegateQueue:_downloadVideoQueue progress:progressBlock completed:completedBlock];
273 | download.delegate = self;
274 | [self.allDownloadDict setObject:download forKey:key];
275 | }
276 |
277 | - (void)downloadVideoAndCacheWithURL:(nonnull NSURL *)url completed:(void(^)(BOOL result))completedBlock{
278 | if(url == nil){
279 | if(completedBlock) completedBlock(NO);
280 | return;
281 | }
282 | [self downloadVideoWithURL:url progress:nil completed:^(NSURL * _Nullable location, NSError * _Nullable error) {
283 | if(error){
284 | if(completedBlock) completedBlock(NO);
285 | }else{
286 | if(completedBlock) completedBlock(YES);
287 | }
288 | }];
289 | }
290 |
291 | - (void)downLoadVideoAndCacheWithURLArray:(nonnull NSArray * )urlArray{
292 | [self downLoadVideoAndCacheWithURLArray:urlArray completed:nil];
293 | }
294 |
295 | - (void)downLoadVideoAndCacheWithURLArray:(nonnull NSArray * )urlArray completed:(nullable XHLaunchAdBatchDownLoadAndCacheCompletedBlock)completedBlock{
296 | if(urlArray.count==0) return;
297 | __block NSMutableArray * resultArray = [[NSMutableArray alloc] init];
298 | dispatch_group_t downLoadGroup = dispatch_group_create();
299 | [urlArray enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idx, BOOL *stop) {
300 | if(![XHLaunchAdCache checkVideoInCacheWithURL:url]){
301 | dispatch_group_enter(downLoadGroup);
302 | [self downloadVideoAndCacheWithURL:url completed:^(BOOL result) {
303 | dispatch_group_leave(downLoadGroup);
304 | [resultArray addObject:@{@"url":url.absoluteString,@"result":@(result)}];
305 | }];
306 | }else{
307 | [resultArray addObject:@{@"url":url.absoluteString,@"result":@(YES)}];
308 | }
309 | }];
310 | dispatch_group_notify(downLoadGroup, dispatch_get_main_queue(), ^{
311 | if(completedBlock) completedBlock(resultArray);
312 | });
313 | }
314 |
315 | - (NSMutableDictionary *)allDownloadDict {
316 | if (!_allDownloadDict) {
317 | _allDownloadDict = [[NSMutableDictionary alloc] init];
318 | }
319 | return _allDownloadDict;
320 | }
321 |
322 | - (void)downloadFinishWithURL:(NSURL *)url{
323 | [self.allDownloadDict removeObjectForKey:[self keyWithURL:url]];
324 | }
325 |
326 | -(NSString *)keyWithURL:(NSURL *)url{
327 | return [XHLaunchAdCache md5String:url.absoluteString];
328 | }
329 |
330 | @end
331 |
332 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdImageManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdImageManager.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/12/2.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 | #import
11 | #import "XHLaunchAdDownloader.h"
12 |
13 | typedef NS_OPTIONS(NSUInteger, XHLaunchAdImageOptions) {
14 | /** 有缓存,读取缓存,不重新下载,没缓存先下载,并缓存 */
15 | XHLaunchAdImageDefault = 1 << 0,
16 | /** 只下载,不缓存 */
17 | XHLaunchAdImageOnlyLoad = 1 << 1,
18 | /** 先读缓存,再下载刷新图片和缓存 */
19 | XHLaunchAdImageRefreshCached = 1 << 2 ,
20 | /** 后台缓存本次不显示,缓存OK后下次再显示(建议使用这种方式)*/
21 | XHLaunchAdImageCacheInBackground = 1 << 3
22 | };
23 |
24 | typedef void(^XHExternalCompletionBlock)(UIImage * _Nullable image,NSData * _Nullable imageData, NSError * _Nullable error, NSURL * _Nullable imageURL);
25 |
26 | @interface XHLaunchAdImageManager : NSObject
27 |
28 | +(nonnull instancetype )sharedManager;
29 | - (void)loadImageWithURL:(nullable NSURL *)url options:(XHLaunchAdImageOptions)options progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHExternalCompletionBlock)completedBlock;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdImageManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdImageManager.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/12/2.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdImageManager.h"
10 | #import "XHLaunchAdCache.h"
11 |
12 | @interface XHLaunchAdImageManager()
13 |
14 | @property(nonatomic,strong) XHLaunchAdDownloader *downloader;
15 | @end
16 |
17 | @implementation XHLaunchAdImageManager
18 |
19 | +(nonnull instancetype )sharedManager{
20 | static XHLaunchAdImageManager *instance = nil;
21 | static dispatch_once_t oneToken;
22 | dispatch_once(&oneToken,^{
23 | instance = [[XHLaunchAdImageManager alloc] init];
24 |
25 | });
26 | return instance;
27 | }
28 |
29 | - (instancetype)init{
30 | self = [super init];
31 | if (self) {
32 | _downloader = [XHLaunchAdDownloader sharedDownloader];
33 | }
34 | return self;
35 | }
36 |
37 | - (void)loadImageWithURL:(nullable NSURL *)url options:(XHLaunchAdImageOptions)options progress:(nullable XHLaunchAdDownloadProgressBlock)progressBlock completed:(nullable XHExternalCompletionBlock)completedBlock{
38 | if(!options) options = XHLaunchAdImageDefault;
39 | if(options & XHLaunchAdImageOnlyLoad){
40 | [_downloader downloadImageWithURL:url progress:progressBlock completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error) {
41 | if(completedBlock) completedBlock(image,data,error,url);
42 | }];
43 | }else if (options & XHLaunchAdImageRefreshCached){
44 | NSData *imageData = [XHLaunchAdCache getCacheImageDataWithURL:url];
45 | UIImage *image = [UIImage imageWithData:imageData];
46 | if(image && completedBlock) completedBlock(image,imageData,nil,url);
47 | [_downloader downloadImageWithURL:url progress:progressBlock completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error) {
48 | if(completedBlock) completedBlock(image,data,error,url);
49 | [XHLaunchAdCache async_saveImageData:data imageURL:url completed:nil];
50 | }];
51 | }else if (options & XHLaunchAdImageCacheInBackground){
52 | NSData *imageData = [XHLaunchAdCache getCacheImageDataWithURL:url];
53 | UIImage *image = [UIImage imageWithData:imageData];
54 | if(image && completedBlock){
55 | completedBlock(image,imageData,nil,url);
56 | }else{
57 | [_downloader downloadImageWithURL:url progress:progressBlock completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error) {
58 | [XHLaunchAdCache async_saveImageData:data imageURL:url completed:nil];
59 | }];
60 | }
61 | }else{//default
62 | NSData *imageData = [XHLaunchAdCache getCacheImageDataWithURL:url];
63 | UIImage *image = [UIImage imageWithData:imageData];
64 | if(image && completedBlock){
65 | completedBlock(image,imageData,nil,url);
66 | }else{
67 | [_downloader downloadImageWithURL:url progress:progressBlock completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error) {
68 | if(completedBlock) completedBlock(image,data,error,url);
69 | [XHLaunchAdCache async_saveImageData:data imageURL:url completed:nil];
70 | }];
71 | }
72 | }
73 | }
74 |
75 | @end
76 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdImageView+XHLaunchAdCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdImageView+XHLaunchAdCache.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/9/18.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdView.h"
10 | #import "XHLaunchAdImageManager.h"
11 |
12 | @interface XHLaunchAdImageView (XHLaunchAdCache)
13 |
14 | /**
15 | 设置url图片
16 |
17 | @param url 图片url
18 | */
19 | - (void)xh_setImageWithURL:(nonnull NSURL *)url;
20 |
21 | /**
22 | 设置url图片
23 |
24 | @param url 图片url
25 | @param placeholder 占位图
26 | */
27 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder;
28 |
29 | /**
30 | 设置url图片
31 |
32 | @param url 图片url
33 | @param placeholder 占位图
34 | @param options XHLaunchAdImageOptions
35 | */
36 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(XHLaunchAdImageOptions)options;
37 |
38 | /**
39 | 设置url图片
40 |
41 | @param url 图片url
42 | @param placeholder 占位图
43 | @param completedBlock XHExternalCompletionBlock
44 | */
45 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable XHExternalCompletionBlock)completedBlock;
46 |
47 | /**
48 | 设置url图片
49 |
50 | @param url 图片url
51 | @param completedBlock XHExternalCompletionBlock
52 | */
53 | - (void)xh_setImageWithURL:(nonnull NSURL *)url completed:(nullable XHExternalCompletionBlock)completedBlock;
54 |
55 |
56 | /**
57 | 设置url图片
58 |
59 | @param url 图片url
60 | @param placeholder 占位图
61 | @param options XHLaunchAdImageOptions
62 | @param completedBlock XHExternalCompletionBlock
63 | */
64 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(XHLaunchAdImageOptions)options completed:(nullable XHExternalCompletionBlock)completedBlock;
65 |
66 | /**
67 | 设置url图片
68 |
69 | @param url 图片url
70 | @param placeholder 占位图
71 | @param GIFImageCycleOnce gif是否只循环播放一次
72 | @param options XHLaunchAdImageOptions
73 | @param GIFImageCycleOnceFinish gif播放完回调(GIFImageCycleOnce = YES 有效)
74 | @param completedBlock XHExternalCompletionBlock
75 | */
76 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder GIFImageCycleOnce:(BOOL)GIFImageCycleOnce options:(XHLaunchAdImageOptions)options GIFImageCycleOnceFinish:(void(^_Nullable)(void))cycleOnceFinishBlock completed:(nullable XHExternalCompletionBlock)completedBlock ;
77 |
78 | @end
79 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdImageView+XHLaunchAdCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdImageView+XHLaunchAdCache.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/9/18.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdImageView+XHLaunchAdCache.h"
10 | #import "XHLaunchAdConst.h"
11 |
12 | #if __has_include()
13 | #import
14 | #else
15 | #import "FLAnimatedImage.h"
16 | #endif
17 |
18 | @implementation XHLaunchAdImageView (XHLaunchAdCache)
19 | - (void)xh_setImageWithURL:(nonnull NSURL *)url{
20 | [self xh_setImageWithURL:url placeholderImage:nil];
21 | }
22 |
23 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder{
24 | [self xh_setImageWithURL:url placeholderImage:placeholder options:XHLaunchAdImageDefault];
25 | }
26 |
27 | -(void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(XHLaunchAdImageOptions)options{
28 | [self xh_setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
29 | }
30 |
31 | - (void)xh_setImageWithURL:(nonnull NSURL *)url completed:(nullable XHExternalCompletionBlock)completedBlock {
32 |
33 | [self xh_setImageWithURL:url placeholderImage:nil completed:completedBlock];
34 | }
35 |
36 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable XHExternalCompletionBlock)completedBlock{
37 | [self xh_setImageWithURL:url placeholderImage:placeholder options:XHLaunchAdImageDefault completed:completedBlock];
38 | }
39 |
40 | -(void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(XHLaunchAdImageOptions)options completed:(nullable XHExternalCompletionBlock)completedBlock{
41 | [self xh_setImageWithURL:url placeholderImage:placeholder GIFImageCycleOnce:NO options:options GIFImageCycleOnceFinish:nil completed:completedBlock ];
42 | }
43 |
44 | - (void)xh_setImageWithURL:(nonnull NSURL *)url placeholderImage:(nullable UIImage *)placeholder GIFImageCycleOnce:(BOOL)GIFImageCycleOnce options:(XHLaunchAdImageOptions)options GIFImageCycleOnceFinish:(void(^_Nullable)(void))cycleOnceFinishBlock completed:(nullable XHExternalCompletionBlock)completedBlock {
45 | if(placeholder) self.image = placeholder;
46 | if(!url) return;
47 | XHWeakSelf
48 | [[XHLaunchAdImageManager sharedManager] loadImageWithURL:url options:options progress:nil completed:^(UIImage * _Nullable image, NSData *_Nullable imageData, NSError * _Nullable error, NSURL * _Nullable imageURL) {
49 | if(!error){
50 | if(XHISGIFTypeWithData(imageData)){
51 | weakSelf.image = nil;
52 | weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
53 | weakSelf.loopCompletionBlock = ^(NSUInteger loopCountRemaining) {
54 | if(GIFImageCycleOnce){
55 | [weakSelf stopAnimating];
56 | XHLaunchAdLog(@"GIF不循环,播放完成");
57 | if(cycleOnceFinishBlock) cycleOnceFinishBlock();
58 | }
59 | };
60 | }else{
61 | weakSelf.image = image;
62 | weakSelf.animatedImage = nil;
63 | }
64 | }
65 | if(completedBlock) completedBlock(image,imageData,error,imageURL);
66 | }];
67 | }
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdView.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/12/3.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 | #import
11 | #import
12 | #import
13 |
14 | #if __has_include()
15 | #import
16 | #else
17 | #import "FLAnimatedImage.h"
18 | #endif
19 |
20 | #if __has_include()
21 | #import
22 | #else
23 | #import "FLAnimatedImageView.h"
24 | #endif
25 |
26 |
27 | #pragma mark - image
28 | @interface XHLaunchAdImageView : FLAnimatedImageView
29 |
30 | @property (nonatomic, copy) void(^click)(CGPoint point);
31 |
32 | @end
33 |
34 | #pragma mark - video
35 | @interface XHLaunchAdVideoView : UIView
36 |
37 | @property (nonatomic, copy) void(^click)(CGPoint point);
38 | @property (nonatomic, strong) AVPlayerViewController *videoPlayer;
39 | @property (nonatomic, assign) MPMovieScalingMode videoScalingMode;
40 | @property (nonatomic, assign) AVLayerVideoGravity videoGravity;
41 | @property (nonatomic, assign) BOOL videoCycleOnce;
42 | @property (nonatomic, assign) BOOL muted;
43 | @property (nonatomic, strong) NSURL *contentURL;
44 |
45 | -(void)stopVideoPlayer;
46 |
47 | @end
48 |
49 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchAdView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdView.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/12/3.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchAdView.h"
10 | #import "XHLaunchAdConst.h"
11 | #import "XHLaunchImageView.h"
12 |
13 | static NSString *const VideoPlayStatus = @"status";
14 |
15 | @interface XHLaunchAdImageView ()
16 |
17 | @end
18 |
19 | @implementation XHLaunchAdImageView
20 |
21 | - (id)init{
22 | self = [super init];
23 | if (self) {
24 | self.userInteractionEnabled = YES;
25 | self.frame = [UIScreen mainScreen].bounds;
26 | self.layer.masksToBounds = YES;
27 | UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
28 | [self addGestureRecognizer:tapGesture];
29 | }
30 | return self;
31 | }
32 |
33 | -(void)tap:(UIGestureRecognizer *)gestureRecognizer{
34 | CGPoint point = [gestureRecognizer locationInView:self];
35 | if(self.click) self.click(point);
36 | }
37 |
38 | @end
39 |
40 | #pragma mark - videoAdView
41 | @interface XHLaunchAdVideoView ()
42 | @property (nonatomic, strong) AVPlayerItem *playerItem;
43 | @end
44 |
45 | @implementation XHLaunchAdVideoView
46 |
47 | -(void)dealloc{
48 | [self.playerItem removeObserver:self forKeyPath:VideoPlayStatus];
49 | [[NSNotificationCenter defaultCenter] removeObserver:self];
50 | }
51 |
52 | - (instancetype)init{
53 | self = [super init];
54 | if (self) {
55 | self.userInteractionEnabled = YES;
56 | self.backgroundColor = [UIColor clearColor];
57 | self.frame = [UIScreen mainScreen].bounds;
58 | [self addSubview:self.videoPlayer.view];
59 | UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
60 | tapGesture.delegate = self;
61 | [self addGestureRecognizer:tapGesture];
62 | }
63 | return self;
64 | }
65 |
66 | -(void)tap:(UIGestureRecognizer *)gestureRecognizer{
67 | CGPoint point = [gestureRecognizer locationInView:self];
68 | if(self.click) self.click(point);
69 | }
70 |
71 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
72 | return YES;
73 | }
74 |
75 | #pragma mark - Action
76 | -(void)stopVideoPlayer{
77 | if(_videoPlayer==nil) return;
78 | [_videoPlayer.player pause];
79 | [_videoPlayer.view removeFromSuperview];
80 | _videoPlayer = nil;
81 |
82 | /** 释放音频焦点 */
83 | [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
84 | }
85 |
86 | - (void)runLoopTheMovie:(NSNotification *)notification{
87 | //需要循环播放
88 | if(!_videoCycleOnce){
89 | [(AVPlayerItem *)[notification object] seekToTime:kCMTimeZero];
90 | [_videoPlayer.player play];//重播
91 | }
92 | }
93 |
94 | #pragma mark - KVO
95 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
96 | if([keyPath isEqualToString:VideoPlayStatus]){
97 | NSInteger newStatus = ((NSNumber *)change[@"new"]).integerValue;
98 | if (newStatus == AVPlayerItemStatusFailed) {
99 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdVideoPlayFailedNotification object:nil userInfo:@{@"videoNameOrURLString":_contentURL.absoluteString}];
100 | }
101 | }
102 | }
103 |
104 | #pragma mark - lazy
105 | -(AVPlayerViewController *)videoPlayer{
106 | if(_videoPlayer==nil){
107 | _videoPlayer = [[AVPlayerViewController alloc] init];
108 | _videoPlayer.showsPlaybackControls = NO;
109 | _videoPlayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
110 | _videoPlayer.view.frame = [UIScreen mainScreen].bounds;
111 | _videoPlayer.view.backgroundColor = [UIColor clearColor];
112 | //注册通知控制是否循环播放
113 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(runLoopTheMovie:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
114 |
115 | /** 获取音频焦点 */
116 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
117 | [[AVAudioSession sharedInstance] setActive:YES error:nil];
118 | }
119 | return _videoPlayer;
120 | }
121 |
122 | #pragma mark - set
123 | -(void)setFrame:(CGRect)frame{
124 | [super setFrame:frame];
125 | _videoPlayer.view.frame = self.frame;
126 | }
127 |
128 | - (void)setContentURL:(NSURL *)contentURL {
129 | _contentURL = contentURL;
130 | AVAsset *movieAsset = [AVURLAsset URLAssetWithURL:contentURL options:nil];
131 | self.playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];
132 | _videoPlayer.player = [AVPlayer playerWithPlayerItem:self.playerItem];
133 | // 监听播放失败状态
134 | [self.playerItem addObserver:self forKeyPath:VideoPlayStatus options:NSKeyValueObservingOptionNew context:nil];
135 | }
136 | -(void)setVideoGravity:(AVLayerVideoGravity)videoGravity{
137 | _videoGravity = videoGravity;
138 | _videoPlayer.videoGravity = videoGravity;
139 | }
140 | -(void)setMuted:(BOOL)muted{
141 | _muted = muted;
142 | _videoPlayer.player.muted = muted;
143 | }
144 | -(void)setVideoScalingMode:(MPMovieScalingMode)videoScalingMode{
145 | _videoScalingMode = videoScalingMode;
146 | switch (_videoScalingMode) {
147 | case MPMovieScalingModeNone:{
148 | _videoPlayer.videoGravity = AVLayerVideoGravityResizeAspect;
149 | }
150 | break;
151 | case MPMovieScalingModeAspectFit:{
152 | _videoPlayer.videoGravity = AVLayerVideoGravityResizeAspect;
153 | }
154 | break;
155 | case MPMovieScalingModeAspectFill:{
156 | _videoPlayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
157 | }
158 | break;
159 | case MPMovieScalingModeFill:{
160 | _videoPlayer.videoGravity = AVLayerVideoGravityResize;
161 | }
162 | break;
163 | default:
164 | break;
165 | }
166 | }
167 |
168 | @end
169 |
170 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchImageView.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchImageView.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 |
11 | /** 启动图来源 */
12 | typedef NS_ENUM(NSInteger,SourceType) {
13 | /** LaunchImage(default) */
14 | SourceTypeLaunchImage = 1,
15 | /** LaunchScreen.storyboard */
16 | SourceTypeLaunchScreen = 2,
17 | };
18 |
19 | typedef NS_ENUM(NSInteger,LaunchImagesSource){
20 | LaunchImagesSourceLaunchImage = 1,
21 | LaunchImagesSourceLaunchScreen = 2,
22 | };
23 |
24 | @interface XHLaunchImageView : UIImageView
25 |
26 | - (instancetype)initWithSourceType:(SourceType)sourceType;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/XHLaunchAd/XHLaunchAd/XHLaunchImageView.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchImageView.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "XHLaunchImageView.h"
10 | #import "XHLaunchAdConst.h"
11 |
12 |
13 | @interface XHLaunchImageView ()
14 |
15 | @end
16 |
17 | @implementation XHLaunchImageView
18 | #pragma mark - private
19 | - (instancetype)initWithSourceType:(SourceType)sourceType{
20 | self = [super init];
21 | if (self) {
22 | self.frame = [UIScreen mainScreen].bounds;
23 | self.userInteractionEnabled = YES;
24 | self.backgroundColor = [UIColor whiteColor];
25 | switch (sourceType) {
26 | case SourceTypeLaunchImage:{
27 | self.image = [self imageFromLaunchImage];
28 | }
29 | break;
30 | case SourceTypeLaunchScreen:{
31 | self.image = [self imageFromLaunchScreen];
32 | }
33 | break;
34 | default:
35 | break;
36 | }
37 | }
38 | return self;
39 | }
40 |
41 | -(UIImage *)imageFromLaunchImage{
42 | UIImage *imageP = [self launchImageWithType:@"Portrait"];
43 | if(imageP) return imageP;
44 | UIImage *imageL = [self launchImageWithType:@"Landscape"];
45 | if(imageL) return imageL;
46 | XHLaunchAdLog(@"获取LaunchImage失败!请检查是否添加启动图,或者规格是否有误.");
47 | return nil;
48 | }
49 |
50 | -(UIImage *)imageFromLaunchScreen{
51 | NSString *UILaunchStoryboardName = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchStoryboardName"];
52 | if(UILaunchStoryboardName == nil){
53 | XHLaunchAdLog(@"从 LaunchScreen 中获取启动图失败!");
54 | return nil;
55 | }
56 | UIViewController *LaunchScreenSb = [[UIStoryboard storyboardWithName:UILaunchStoryboardName bundle:nil] instantiateInitialViewController];
57 | if(LaunchScreenSb){
58 | UIView * view = LaunchScreenSb.view;
59 | // 加入到UIWindow后,LaunchScreenSb.view的safeAreaInsets在刘海屏机型才正常。
60 | UIWindow *containerWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
61 | view.frame = containerWindow.bounds;
62 | [containerWindow addSubview:view];
63 | [containerWindow layoutIfNeeded];
64 | UIImage *image = [self imageFromView:view];
65 | containerWindow = nil;
66 | return image;
67 | }
68 | XHLaunchAdLog(@"从 LaunchScreen 中获取启动图失败!");
69 | return nil;
70 | }
71 |
72 | -(UIImage*)imageFromView:(UIView*)view{
73 | //fix bug:https://github.com/CoderZhuXH/XHLaunchAd/issues/203
74 | if (CGRectIsEmpty(view.frame)) {
75 | return nil;
76 | }
77 | CGSize size = view.bounds.size;
78 | //参数1:表示区域大小 参数2:如果需要显示半透明效果,需要传NO,否则传YES 参数3:屏幕密度
79 | UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
80 | if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
81 | [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
82 | }else{
83 | [view.layer renderInContext:UIGraphicsGetCurrentContext()];
84 | }
85 | UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
86 | UIGraphicsEndImageContext();
87 | return image;
88 | }
89 |
90 |
91 | -(UIImage *)launchImageWithType:(NSString *)type{
92 | //比对分辨率,获取启动图 fix #158:https://github.com/CoderZhuXH/XHLaunchAd/issues/158
93 | CGFloat screenScale = [UIScreen mainScreen].scale;
94 | CGSize screenDipSize = CGSizeMake([UIScreen mainScreen].bounds.size.width * screenScale, [UIScreen mainScreen].bounds.size.height * screenScale);
95 | NSString *viewOrientation = type;
96 | NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
97 | for (NSDictionary* dict in imagesDict){
98 | UIImage *image = [UIImage imageNamed:dict[@"UILaunchImageName"]];
99 | CGSize imageDpiSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
100 | if([viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]]){
101 | if([dict[@"UILaunchImageOrientation"] isEqualToString:@"Landscape"]){
102 | imageDpiSize = CGSizeMake(imageDpiSize.height, imageDpiSize.width);
103 | }
104 | if(CGSizeEqualToSize(screenDipSize, imageDpiSize)){
105 | return image;
106 | }
107 | }
108 | }
109 | return nil;
110 | }
111 |
112 | @end
113 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/Model/LaunchAdModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // LaunchAdModel.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 广告数据模型
9 |
10 | #import
11 | #import
12 |
13 | @interface LaunchAdModel : NSObject
14 |
15 | /**
16 | * 广告URL
17 | */
18 | @property (nonatomic, copy) NSString *content;
19 |
20 | /**
21 | * 点击打开连接
22 | */
23 | @property (nonatomic, copy) NSString *openUrl;
24 |
25 | /**
26 | * 广告分辨率
27 | */
28 | @property (nonatomic, copy) NSString *contentSize;
29 |
30 | /**
31 | * 广告停留时间
32 | */
33 | @property (nonatomic, assign) NSInteger duration;
34 |
35 |
36 | /**
37 | * 分辨率宽
38 | */
39 | @property(nonatomic,assign,readonly)CGFloat width;
40 | /**
41 | * 分辨率高
42 | */
43 | @property(nonatomic,assign,readonly)CGFloat height;
44 |
45 | - (instancetype)initWithDict:(NSDictionary *)dict;
46 | @end
47 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/Model/LaunchAdModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // LaunchAdModel.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 广告数据模型
9 | #import "LaunchAdModel.h"
10 |
11 | @implementation LaunchAdModel
12 |
13 | - (instancetype)initWithDict:(NSDictionary *)dict
14 | {
15 | self = [super init];
16 | if (self) {
17 |
18 | self.content = dict[@"content"];
19 | self.openUrl = dict[@"openUrl"];
20 | self.duration = [dict[@"duration"] integerValue];
21 | self.contentSize = dict[@"contentSize"];
22 | }
23 | return self;
24 | }
25 | -(CGFloat)width
26 | {
27 | return [[[self.contentSize componentsSeparatedByString:@"*"] firstObject] floatValue];
28 | }
29 | -(CGFloat)height
30 | {
31 | return [[[self.contentSize componentsSeparatedByString:@"*"] lastObject] floatValue];
32 | }
33 | @end
34 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/Tools/Network/Network.h:
--------------------------------------------------------------------------------
1 | //
2 | // Network.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 数据请求类
9 |
10 | #import
11 |
12 | typedef void(^NetworkSucess) (NSDictionary * response);
13 | typedef void(^NetworkFailure) (NSError *error);
14 |
15 | @interface Network : NSObject
16 |
17 | /**
18 | * 此处用于模拟广告数据请求,实际项目中请做真实请求
19 | */
20 | +(void)getLaunchAdImageDataSuccess:(NetworkSucess)success failure:(NetworkFailure)failure;
21 | +(void)getLaunchAdVideoDataSuccess:(NetworkSucess)success failure:(NetworkFailure)failure;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/Tools/Network/Network.m:
--------------------------------------------------------------------------------
1 | //
2 | // Network.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2016/6/28.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 数据请求类
9 | #import "Network.h"
10 |
11 | @implementation Network
12 |
13 | /**
14 | * 此处模拟广告数据请求,实际项目中请做真实请求
15 | */
16 | +(void)getLaunchAdImageDataSuccess:(NetworkSucess)success failure:(NetworkFailure)failure;
17 | {
18 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
19 |
20 | NSData *JSONData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"LaunchImageAd" ofType:@"json"]];
21 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingAllowFragments error:nil];
22 | if(success) success(json);
23 |
24 | });
25 | }
26 | /**
27 | * 此处模拟广告数据请求,实际项目中请做真实请求
28 | */
29 | +(void)getLaunchAdVideoDataSuccess:(NetworkSucess)success failure:(NetworkFailure)failure;
30 | {
31 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
32 |
33 | NSData *JSONData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"LaunchVideoAd" ofType:@"json"]];
34 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingAllowFragments error:nil];
35 | if(success) success(json);
36 |
37 | });
38 | }
39 | @end
40 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/Tools/UIViewController+/UIViewController+Nav.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Nav.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/5/3.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 |
11 | @interface UIViewController (Nav)
12 | @property(nonatomic,strong,readonly)UINavigationController *myNavigationController;
13 | @end
14 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/Tools/UIViewController+/UIViewController+Nav.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Nav.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/5/3.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "UIViewController+Nav.h"
10 |
11 | @implementation UIViewController (Nav)
12 | - (UINavigationController*)myNavigationController
13 | {
14 | UINavigationController* nav = nil;
15 | if ([self isKindOfClass:[UINavigationController class]]) {
16 | nav = (id)self;
17 | }
18 | else {
19 | if ([self isKindOfClass:[UITabBarController class]]) {
20 | nav = ((UITabBarController*)self).selectedViewController.myNavigationController;
21 | }
22 | else {
23 | nav = self.navigationController;
24 | }
25 | }
26 | return nav;
27 | }
28 | @end
29 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/11.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 首页
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/11.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 首页
9 | #import "ViewController.h"
10 |
11 | @interface ViewController ()
12 |
13 | @property (weak, nonatomic) IBOutlet UILabel *label;
14 |
15 | @end
16 |
17 | @implementation ViewController
18 |
19 | -(void)viewWillAppear:(BOOL)animated
20 | {
21 | [super viewWillAppear:animated];
22 |
23 | NSLog(@"进入首页");
24 | }
25 | - (void)viewDidLoad {
26 | [super viewDidLoad];
27 |
28 | self.navigationItem.title = @"XHLaunchAdExample";
29 |
30 | self.label.text = @"使用说明及注意事项见github:\n https://github.com/CoderZhuXH/XHLaunchAd";
31 |
32 | // Do any additional setup after loading the view from its nib.
33 | }
34 | - (void)didReceiveMemoryWarning {
35 | [super didReceiveMemoryWarning];
36 |
37 | // Dispose of any resources that can be recreated.
38 | }
39 |
40 |
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/ViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/WebViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WebViewController.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/9/8.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 广告详情页
9 |
10 | #import
11 |
12 | @interface WebViewController : UIViewController
13 | @property(nonatomic,copy)NSString *URLString;
14 | @end
15 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Class/WebViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WebViewController.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/9/8.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 广告详情页
9 |
10 | #import "WebViewController.h"
11 | #import
12 | #import "XHLaunchAd.h"
13 |
14 | @interface WebViewController ()
15 | @property(nonatomic,strong)WKWebView *webView;
16 | @property (nonatomic, strong) UIProgressView *progressView;
17 | @end
18 |
19 | @implementation WebViewController
20 |
21 | -(void)dealloc
22 | {
23 | /**
24 | 如果你设置了APP从后台恢复时也显示广告,
25 | 当用户停留在广告详情页时,APP从后台恢复时,你不想再次显示启动广告,
26 | 请在广告详情控制器销毁时,发下面通知,告诉XHLaunchAd,广告详情页面已显示完
27 | */
28 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdDetailPageShowFinishNotification object:nil];
29 |
30 | [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
31 | }
32 | -(void)viewWillAppear:(BOOL)animated
33 | {
34 | [super viewWillAppear:animated];
35 |
36 | /**
37 | 如果你设置了APP从后台恢复时也显示广告,
38 | 当用户停留在广告详情页时,APP从后台恢复时,你不想再次显示启动广告,
39 | 请在广告详情控制器将要显示时,发下面通知,告诉XHLaunchAd,广告详情页面将要显示
40 | */
41 | [[NSNotificationCenter defaultCenter] postNotificationName:XHLaunchAdDetailPageWillShowNotification object:nil];
42 | }
43 | - (void)viewDidLoad {
44 | [super viewDidLoad];
45 | self.navigationItem.title = @"详情";
46 | self.navigationController.navigationBar.translucent = YES;
47 | self.automaticallyAdjustsScrollViewInsets = NO;
48 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"←" style:UIBarButtonItemStylePlain target:self action:@selector(back)];
49 |
50 | CGFloat navbarHeight = [UIApplication sharedApplication].statusBarFrame.size.height + 44;
51 | self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, navbarHeight, self.view.bounds.size.width, self.view.bounds.size.height-navbarHeight)];
52 | self.webView.scrollView.backgroundColor = [UIColor groupTableViewBackgroundColor];
53 | [self.view addSubview:self.webView];
54 |
55 | [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
56 |
57 | if(!self.URLString) return;
58 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:(self.URLString)]];
59 | [self.webView loadRequest:request];
60 |
61 | self.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, navbarHeight-2, self.view.bounds.size.width, 2)];
62 | self.progressView.progressViewStyle = UIProgressViewStyleBar;
63 | self.progressView.progressTintColor = [UIColor blackColor];
64 | [self.navigationController.view addSubview:self.progressView];
65 | }
66 | -(void)viewWillDisappear:(BOOL)animated
67 | {
68 | [super viewWillDisappear:animated];
69 | [self.progressView removeFromSuperview];
70 | }
71 | -(void)back{
72 |
73 | if([_webView canGoBack])
74 | {
75 | [_webView goBack];
76 | }
77 | else
78 | {
79 | [self.navigationController popViewControllerAnimated:YES];
80 | }
81 | }
82 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
83 | if ([keyPath isEqualToString:@"estimatedProgress"]) {
84 |
85 | CGFloat progress = [change[NSKeyValueChangeNewKey] floatValue];
86 | [self.progressView setProgress:progress animated:YES];
87 | if(progress == 1.0)
88 | {
89 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
90 |
91 | [self.progressView setProgress:0.0 animated:NO];
92 | });
93 | }
94 |
95 | }else{
96 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
97 | }
98 | }
99 |
100 | - (void)didReceiveMemoryWarning {
101 | [super didReceiveMemoryWarning];
102 | // Dispose of any resources that can be recreated.
103 | }
104 |
105 | /*
106 | #pragma mark - Navigation
107 |
108 | // In a storyboard-based application, you will often want to do a little preparation before navigation
109 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
110 | // Get the new view controller using [segue destinationViewController].
111 | // Pass the selected object to the new view controller.
112 | }
113 | */
114 |
115 | @end
116 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/LaunchImageAd.json:
--------------------------------------------------------------------------------
1 | {
2 | "code":200,
3 | "msg":"success",
4 | "data":{
5 | "content":"http://yun.it7090.com/image/XHLaunchAd/pic_test01.jpg",
6 | "openUrl":"http://www.it7090.com",
7 | "contentSize":"1242*1786",
8 | "duration":5,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/LaunchVideoAd.json:
--------------------------------------------------------------------------------
1 | {
2 | "code":200,
3 | "msg":"success",
4 | "data":{
5 | "content":"http://yun.it7090.com/video/XHLaunchAd/video_test01.mp4",
6 | "openUrl":"http://www.it7090.com",
7 | "contentSize":"750*1336",
8 | "duration":5,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/image0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/image0.jpg
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/image1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/image1.jpg
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/image12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/image12.gif
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/image2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/image2.jpg
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/image3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/image3.jpg
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/image4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/image4.jpg
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/video0.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/video0.mp4
--------------------------------------------------------------------------------
/XHLaunchAdExample/Source/video1.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/Source/video1.mp4
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import "AppDelegate.h"
10 | #import "ViewController.h"
11 |
12 | @interface AppDelegate ()
13 |
14 | @end
15 |
16 | @implementation AppDelegate
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
20 | self.window.backgroundColor = [UIColor whiteColor];
21 |
22 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]];
23 |
24 | /** 开屏广告初始化,见XHLaunchAdManager */
25 |
26 | [self.window makeKeyAndVisible];
27 | return YES;
28 | }
29 | - (void)applicationWillResignActive:(UIApplication *)application {
30 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
31 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
32 | }
33 |
34 | - (void)applicationDidEnterBackground:(UIApplication *)application {
35 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
36 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
37 | }
38 |
39 | - (void)applicationWillEnterForeground:(UIApplication *)application {
40 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
41 | }
42 |
43 | - (void)applicationDidBecomeActive:(UIApplication *)application {
44 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
45 | }
46 |
47 | - (void)applicationWillTerminate:(UIApplication *)application {
48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "2688h",
7 | "filename" : "LaunchImage-1242x2688@3x.png",
8 | "minimum-system-version" : "12.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "extent" : "full-screen",
14 | "idiom" : "iphone",
15 | "subtype" : "1792h",
16 | "filename" : "LaunchImage-828x1792@2x.png",
17 | "minimum-system-version" : "12.0",
18 | "orientation" : "portrait",
19 | "scale" : "2x"
20 | },
21 | {
22 | "extent" : "full-screen",
23 | "idiom" : "iphone",
24 | "subtype" : "2436h",
25 | "filename" : "LaunchImage-1125x2436@3x.png",
26 | "minimum-system-version" : "11.0",
27 | "orientation" : "portrait",
28 | "scale" : "3x"
29 | },
30 | {
31 | "extent" : "full-screen",
32 | "idiom" : "iphone",
33 | "subtype" : "736h",
34 | "filename" : "LaunchImage-1242x2208@3x.png",
35 | "minimum-system-version" : "8.0",
36 | "orientation" : "portrait",
37 | "scale" : "3x"
38 | },
39 | {
40 | "extent" : "full-screen",
41 | "idiom" : "iphone",
42 | "subtype" : "667h",
43 | "filename" : "LaunchImage-750x1334@2x.png",
44 | "minimum-system-version" : "8.0",
45 | "orientation" : "portrait",
46 | "scale" : "2x"
47 | },
48 | {
49 | "orientation" : "portrait",
50 | "idiom" : "iphone",
51 | "filename" : "LaunchImage-640x960@2x.png",
52 | "extent" : "full-screen",
53 | "minimum-system-version" : "7.0",
54 | "scale" : "2x"
55 | },
56 | {
57 | "extent" : "full-screen",
58 | "idiom" : "iphone",
59 | "subtype" : "retina4",
60 | "filename" : "LaunchImage-640x1136@2x.png",
61 | "minimum-system-version" : "7.0",
62 | "orientation" : "portrait",
63 | "scale" : "2x"
64 | },
65 | {
66 | "orientation" : "portrait",
67 | "idiom" : "ipad",
68 | "filename" : "LaunchImage-ipad-768x1024.png",
69 | "extent" : "full-screen",
70 | "minimum-system-version" : "7.0",
71 | "scale" : "1x"
72 | },
73 | {
74 | "orientation" : "landscape",
75 | "idiom" : "ipad",
76 | "filename" : "LaunchImage-ipad-1024x768.png",
77 | "extent" : "full-screen",
78 | "minimum-system-version" : "7.0",
79 | "scale" : "1x"
80 | },
81 | {
82 | "orientation" : "portrait",
83 | "idiom" : "ipad",
84 | "filename" : "LaunchImage-ipad-1536x2048@2x.png",
85 | "extent" : "full-screen",
86 | "minimum-system-version" : "7.0",
87 | "scale" : "2x"
88 | },
89 | {
90 | "orientation" : "landscape",
91 | "idiom" : "ipad",
92 | "filename" : "LaunchImage-ipad-2048x1536@2x.png",
93 | "extent" : "full-screen",
94 | "minimum-system-version" : "7.0",
95 | "scale" : "2x"
96 | }
97 | ],
98 | "info" : {
99 | "version" : 1,
100 | "author" : "xcode"
101 | }
102 | }
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-1125x2436@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-1125x2436@3x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-1242x2208@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-1242x2208@3x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-1242x2688@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-1242x2688@3x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-640x1136@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-640x1136@2x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-640x960@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-640x960@2x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-750x1334@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-750x1334@2x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-828x1792@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-828x1792@2x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-1024x768.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-1536x2048@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-1536x2048@2x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-2048x1536@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-2048x1536@2x.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-768x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoderZhuXH/XHLaunchAd/11d94252aca53a43ad4a0ce58cd928e2c1608272/XHLaunchAdExample/XHLaunchAdExample/Assets.xcassets/LaunchImage.launchimage/LaunchImage-ipad-768x1024.png
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UIRequiredDeviceCapabilities
31 |
32 | armv7
33 |
34 | UIRequiresFullScreen
35 |
36 | UIStatusBarHidden
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/XHLaunchAdManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdManager.h
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/5/3.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 |
9 | #import
10 |
11 | @interface XHLaunchAdManager : NSObject
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/XHLaunchAdManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // XHLaunchAdManager.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 2017/5/3.
6 | // Copyright © 2017年 it7090.com. All rights reserved.
7 | // 代码地址:https://github.com/CoderZhuXH/XHLaunchAd
8 | // 开屏广告初始化
9 |
10 | #import "XHLaunchAdManager.h"
11 | #import "XHLaunchAd.h"
12 | #import "Network.h"
13 | #import "LaunchAdModel.h"
14 | #import "UIViewController+Nav.h"
15 | #import "WebViewController.h"
16 |
17 | /** 以下连接供测试使用 */
18 |
19 | /** 静态图 */
20 | #define imageURL1 @"http://yun.it7090.com/image/XHLaunchAd/pic_test01.jpg"
21 |
22 | /** 动态图 */
23 | #define imageURL3 @"http://yun.it7090.com/image/XHLaunchAd/gif_test01.gif"
24 |
25 | /** 视频链接 */
26 | #define videoURL1 @"http://yun.it7090.com/video/XHLaunchAd/video_test01.mp4"
27 |
28 |
29 | @interface XHLaunchAdManager()
30 |
31 | @end
32 |
33 | @implementation XHLaunchAdManager
34 |
35 | +(void)load{
36 | [self shareManager];
37 | }
38 |
39 | +(XHLaunchAdManager *)shareManager{
40 | static XHLaunchAdManager *instance = nil;
41 | static dispatch_once_t oneToken;
42 | dispatch_once(&oneToken,^{
43 | instance = [[XHLaunchAdManager alloc] init];
44 | });
45 | return instance;
46 | }
47 |
48 | - (instancetype)init{
49 | self = [super init];
50 | if (self) {
51 | //在UIApplicationDidFinishLaunching时初始化开屏广告,做到对业务层无干扰,当然你也可以直接在AppDelegate didFinishLaunchingWithOptions方法中初始化
52 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
53 | //初始化开屏广告
54 | [self setupXHLaunchAd];
55 | }];
56 | }
57 | return self;
58 | }
59 |
60 | -(void)setupXHLaunchAd{
61 |
62 | /*1.图片开屏广告 - 网络数据 */
63 | //[self example01];
64 |
65 | /*2.图片开屏广告 - 本地数据*/
66 | [self example02];
67 |
68 | /*3.视频开屏广告 - 网络数据(网络视频只支持缓存OK后下次显示,看效果请二次运行)*/
69 | //[self example03];
70 |
71 | /*4.视频开屏广告 - 本地数据 */
72 | //[self example04];
73 |
74 | /*5.如需自定义跳过按钮,请看这个示例 */
75 | //[self example05];
76 |
77 | /*6.使用默认配置快速初始化,请看下面两个示例 */
78 | //[self example06];//图片
79 | //[self example07];//视频
80 |
81 | /*7.如果你想提前批量缓存图片/视频请看下面两个示例 */
82 | //[self batchDownloadImageAndCache]; //批量下载并缓存图片
83 | //[self batchDownloadVideoAndCache]; //批量下载并缓存视频
84 |
85 | }
86 |
87 | #pragma mark - 图片开屏广告-网络数据-示例
88 | //图片开屏广告 - 网络数据
89 | -(void)example01{
90 |
91 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
92 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
93 |
94 | //1.因为数据请求是异步的,请在数据请求前,调用下面方法配置数据等待时间.
95 | //2.设为2即表示:启动页将停留2s等待服务器返回广告数据,2s内等到广告数据,将正常显示广告,否则将不显示
96 | //3.数据获取成功,配置广告数据后,自动结束等待,显示广告
97 | //注意:请求广告数据前,必须设置此属性,否则会先进入window的的根控制器
98 |
99 | [XHLaunchAd setWaitDataDuration:2];
100 |
101 | //广告数据请求
102 | [Network getLaunchAdImageDataSuccess:^(NSDictionary * response) {
103 | NSLog(@"广告数据 = %@",response);
104 | //广告数据转模型
105 | LaunchAdModel *model = [[LaunchAdModel alloc] initWithDict:response[@"data"]];
106 | //配置广告数据
107 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration new];
108 | //广告停留时间
109 | imageAdconfiguration.duration = model.duration;
110 | //广告frame
111 | imageAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.8);
112 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
113 | //注意本地广告图片,直接放在工程目录,不要放在Assets里面,否则不识别,此处涉及到内存优化
114 | imageAdconfiguration.imageNameOrURLString = model.content;
115 | //设置GIF动图是否只循环播放一次(仅对动图设置有效)
116 | imageAdconfiguration.GIFImageCycleOnce = NO;
117 | //缓存机制(仅对网络图片有效)
118 | //为告展示效果更好,可设置为XHLaunchAdImageCacheInBackground,先缓存,下次显示
119 | imageAdconfiguration.imageOption = XHLaunchAdImageDefault;
120 | //图片填充模式
121 | imageAdconfiguration.contentMode = UIViewContentModeScaleAspectFill;
122 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
123 | imageAdconfiguration.openModel = model.openUrl;
124 | //广告显示完成动画
125 | imageAdconfiguration.showFinishAnimate =ShowFinishAnimateLite;
126 | //广告显示完成动画时间
127 | imageAdconfiguration.showFinishAnimateTime = 0.8;
128 | //跳过按钮类型
129 | imageAdconfiguration.skipButtonType = SkipTypeTimeText;
130 | //后台返回时,是否显示广告
131 | imageAdconfiguration.showEnterForeground = NO;
132 |
133 | //图片已缓存 - 显示一个 "已预载" 视图 (可选)
134 | if([XHLaunchAd checkImageInCacheWithURL:[NSURL URLWithString:model.content]]){
135 | //设置要添加的自定义视图(可选)
136 | imageAdconfiguration.subViews = [self launchAdSubViews_alreadyView];
137 |
138 | }
139 | //显示开屏广告
140 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
141 |
142 | } failure:^(NSError *error) {
143 | }];
144 |
145 | }
146 |
147 | #pragma mark - 图片开屏广告-本地数据-示例
148 | //图片开屏广告 - 本地数据
149 | -(void)example02{
150 |
151 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
152 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
153 |
154 | //配置广告数据
155 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration new];
156 | //广告停留时间
157 | imageAdconfiguration.duration = 5;
158 | //广告frame
159 | imageAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.8);
160 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
161 | //注意本地广告图片,直接放在工程目录,不要放在Assets里面,否则不识别,此处涉及到内存优化
162 | imageAdconfiguration.imageNameOrURLString = @"image2.jpg";
163 | //设置GIF动图是否只循环播放一次(仅对动图设置有效)
164 | imageAdconfiguration.GIFImageCycleOnce = NO;
165 | //图片填充模式
166 | imageAdconfiguration.contentMode = UIViewContentModeScaleAspectFill;
167 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
168 | imageAdconfiguration.openModel = @"http://www.it7090.com";
169 | //广告显示完成动画
170 | imageAdconfiguration.showFinishAnimate =ShowFinishAnimateFlipFromLeft;
171 | //广告显示完成动画时间
172 | imageAdconfiguration.showFinishAnimateTime = 0.8;
173 | //跳过按钮类型
174 | imageAdconfiguration.skipButtonType = SkipTypeRoundProgressText;
175 | //后台返回时,是否显示广告
176 | imageAdconfiguration.showEnterForeground = NO;
177 | //设置要添加的子视图(可选)
178 | //imageAdconfiguration.subViews = [self launchAdSubViews];
179 | //显示开屏广告
180 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
181 |
182 | }
183 |
184 | #pragma mark - 视频开屏广告-网络数据-示例
185 | //视频开屏广告 - 网络数据
186 | -(void)example03{
187 |
188 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
189 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
190 |
191 | //1.因为数据请求是异步的,请在数据请求前,调用下面方法配置数据等待时间.
192 | //2.设为2即表示:启动页将停留2s等待服务器返回广告数据,2s内等到广告数据,将正常显示广告,否则将不显示
193 | //3.数据获取成功,配置广告数据后,自动结束等待,显示广告
194 | //注意:请求广告数据前,必须设置此属性,否则会先进入window的的根控制器
195 | [XHLaunchAd setWaitDataDuration:2];
196 |
197 | //广告数据请求
198 | [Network getLaunchAdVideoDataSuccess:^(NSDictionary * response) {
199 |
200 | NSLog(@"广告数据 = %@",response);
201 |
202 | //广告数据转模型
203 | LaunchAdModel *model = [[LaunchAdModel alloc] initWithDict:response[@"data"]];
204 |
205 | //配置广告数据
206 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration new];
207 | //广告停留时间
208 | videoAdconfiguration.duration = model.duration;
209 | //广告frame
210 | videoAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
211 | //广告视频URLString/或本地视频名(请带上后缀)
212 | //注意:视频广告只支持先缓存,下次显示(看效果请二次运行)
213 | videoAdconfiguration.videoNameOrURLString = model.content;
214 | //是否关闭音频
215 | videoAdconfiguration.muted = NO;
216 | //视频缩放模式
217 | videoAdconfiguration.videoGravity = AVLayerVideoGravityResizeAspectFill;
218 | //是否只循环播放一次
219 | videoAdconfiguration.videoCycleOnce = NO;
220 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
221 | videoAdconfiguration.openModel = model.openUrl;
222 | //广告显示完成动画
223 | videoAdconfiguration.showFinishAnimate =ShowFinishAnimateFadein;
224 | //广告显示完成动画时间
225 | videoAdconfiguration.showFinishAnimateTime = 0.8;
226 | //后台返回时,是否显示广告
227 | videoAdconfiguration.showEnterForeground = NO;
228 | //跳过按钮类型
229 | videoAdconfiguration.skipButtonType = SkipTypeTimeText;
230 | //视频已缓存 - 显示一个 "已预载" 视图 (可选)
231 | if([XHLaunchAd checkVideoInCacheWithURL:[NSURL URLWithString:model.content]]){
232 | //设置要添加的自定义视图(可选)
233 | videoAdconfiguration.subViews = [self launchAdSubViews_alreadyView];
234 |
235 | }
236 |
237 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
238 |
239 | } failure:^(NSError *error) {
240 |
241 | }];
242 |
243 | }
244 |
245 | #pragma mark - 视频开屏广告-本地数据-示例
246 | //视频开屏广告 - 本地数据
247 | -(void)example04{
248 |
249 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
250 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
251 |
252 | //配置广告数据
253 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration new];
254 | //广告停留时间
255 | videoAdconfiguration.duration = 5;
256 | //广告frame
257 | videoAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
258 | //广告视频URLString/或本地视频名(请带上后缀)
259 | videoAdconfiguration.videoNameOrURLString = @"video0.mp4";
260 | //是否关闭音频
261 | videoAdconfiguration.muted = NO;
262 | //视频填充模式
263 | videoAdconfiguration.videoGravity = AVLayerVideoGravityResizeAspectFill;
264 | //是否只循环播放一次
265 | videoAdconfiguration.videoCycleOnce = NO;
266 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
267 | videoAdconfiguration.openModel = @"http://www.it7090.com";
268 | //跳过按钮类型
269 | videoAdconfiguration.skipButtonType = SkipTypeRoundProgressTime;
270 | //广告显示完成动画
271 | videoAdconfiguration.showFinishAnimate = ShowFinishAnimateLite;
272 | //广告显示完成动画时间
273 | videoAdconfiguration.showFinishAnimateTime = 0.8;
274 | //后台返回时,是否显示广告
275 | videoAdconfiguration.showEnterForeground = NO;
276 | //设置要添加的子视图(可选)
277 | //videoAdconfiguration.subViews = [self launchAdSubViews];
278 | //显示开屏广告
279 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
280 |
281 | }
282 | #pragma mark - 自定义跳过按钮-示例
283 | -(void)example05{
284 |
285 | //注意:
286 | //1.自定义跳过按钮很简单,configuration有一个customSkipView属性.
287 | //2.自定义一个跳过的view 赋值给configuration.customSkipView属性便可替换默认跳过按钮,如下:
288 |
289 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
290 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
291 |
292 | //配置广告数据
293 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration new];
294 | //广告停留时间
295 | imageAdconfiguration.duration = 5;
296 | //广告frame
297 | imageAdconfiguration.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width/400*711);
298 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
299 | imageAdconfiguration.imageNameOrURLString = @"image12.gif";
300 | //缓存机制(仅对网络图片有效)
301 | imageAdconfiguration.imageOption = XHLaunchAdImageDefault;
302 | //图片填充模式
303 | imageAdconfiguration.contentMode = UIViewContentModeScaleToFill;
304 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
305 | imageAdconfiguration.openModel = @"http://www.it7090.com";
306 | //广告显示完成动画
307 | imageAdconfiguration.showFinishAnimate = ShowFinishAnimateFlipFromLeft;
308 | //广告显示完成动画时间
309 | imageAdconfiguration.showFinishAnimateTime = 0.8;
310 | //后台返回时,是否显示广告
311 | imageAdconfiguration.showEnterForeground = NO;
312 |
313 | //设置要添加的子视图(可选)
314 | imageAdconfiguration.subViews = [self launchAdSubViews];
315 |
316 | //start********************自定义跳过按钮**************************
317 | imageAdconfiguration.customSkipView = [self customSkipView];
318 | //********************自定义跳过按钮*****************************end
319 |
320 | //显示开屏广告
321 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
322 |
323 | }
324 |
325 | #pragma mark - 使用默认配置快速初始化 - 示例
326 | /**
327 | * 图片
328 | */
329 | -(void)example06{
330 |
331 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
332 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
333 |
334 | //使用默认配置
335 | XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration defaultConfiguration];
336 | //广告图片URLString/或本地图片名(.jpg/.gif请带上后缀)
337 | imageAdconfiguration.imageNameOrURLString = imageURL3;
338 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
339 | imageAdconfiguration.openModel = @"http://www.it7090.com";
340 | [XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
341 | }
342 |
343 | /**
344 | * 视频
345 | */
346 | -(void)example07{
347 |
348 | //设置你工程的启动页使用的是:LaunchImage 还是 LaunchScreen.storyboard(不设置默认:LaunchImage)
349 | [XHLaunchAd setLaunchSourceType:SourceTypeLaunchImage];
350 |
351 | //使用默认配置
352 | XHLaunchVideoAdConfiguration *videoAdconfiguration = [XHLaunchVideoAdConfiguration defaultConfiguration];
353 | //广告视频URLString/或本地视频名(请带上后缀)
354 | videoAdconfiguration.videoNameOrURLString = @"video0.mp4";
355 | //广告点击打开页面参数(openModel可为NSString,模型,字典等任意类型)
356 | videoAdconfiguration.openModel = @"http://www.it7090.com";
357 | [XHLaunchAd videoAdWithVideoAdConfiguration:videoAdconfiguration delegate:self];
358 | }
359 |
360 | #pragma mark - 批量下载并缓存
361 | /**
362 | * 批量下载并缓存图片
363 | */
364 | -(void)batchDownloadImageAndCache{
365 |
366 | [XHLaunchAd downLoadImageAndCacheWithURLArray:@[[NSURL URLWithString:imageURL1]] completed:^(NSArray * _Nonnull completedArray) {
367 |
368 | /** 打印批量下载缓存结果 */
369 |
370 | //url:图片的url字符串,
371 | //result:0表示该图片下载失败,1表示该图片下载并缓存完成或本地缓存中已有该图片
372 | NSLog(@"批量下载缓存图片结果 = %@" ,completedArray);
373 | }];
374 | }
375 |
376 | /**
377 | * 批量下载并缓存视频
378 | */
379 | -(void)batchDownloadVideoAndCache{
380 |
381 | [XHLaunchAd downLoadVideoAndCacheWithURLArray:@[[NSURL URLWithString:videoURL1]] completed:^(NSArray * _Nonnull completedArray) {
382 |
383 | /** 打印批量下载缓存结果 */
384 |
385 | //url:视频的url字符串,
386 | //result:0表示该视频下载失败,1表示该视频下载并缓存完成或本地缓存中已有该视频
387 | NSLog(@"批量下载缓存视频结果 = %@" ,completedArray);
388 |
389 | }];
390 |
391 | }
392 |
393 | #pragma mark - subViews
394 | -(NSArray *)launchAdSubViews_alreadyView{
395 |
396 | CGFloat y = XH_FULLSCREEN ? 46:22;
397 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width-140, y, 60, 30)];
398 | label.text = @"已预载";
399 | label.font = [UIFont systemFontOfSize:12];
400 | label.textColor = [UIColor whiteColor];
401 | label.textAlignment = NSTextAlignmentCenter;
402 | label.layer.cornerRadius = 5.0;
403 | label.layer.masksToBounds = YES;
404 | label.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
405 | return [NSArray arrayWithObject:label];
406 |
407 | }
408 |
409 | -(NSArray *)launchAdSubViews{
410 |
411 | CGFloat y = XH_FULLSCREEN ? 54 : 30;
412 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width-170, y, 60, 30)];
413 | label.text = @"subViews";
414 | label.font = [UIFont systemFontOfSize:12];
415 | label.textColor = [UIColor whiteColor];
416 | label.textAlignment = NSTextAlignmentCenter;
417 | label.layer.cornerRadius = 5.0;
418 | label.layer.masksToBounds = YES;
419 | label.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
420 | return [NSArray arrayWithObject:label];
421 |
422 | }
423 |
424 | #pragma mark - customSkipView
425 | //自定义跳过按钮
426 | -(UIView *)customSkipView{
427 |
428 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
429 | button.backgroundColor =[UIColor orangeColor];
430 | button.layer.cornerRadius = 5.0;
431 | button.layer.borderWidth = 1.5;
432 | button.layer.borderColor = [UIColor lightGrayColor].CGColor;
433 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
434 | button.titleLabel.font = [UIFont systemFontOfSize:14];
435 | CGFloat y = XH_FULLSCREEN ? 54 : 30;
436 | button.frame = CGRectMake([UIScreen mainScreen].bounds.size.width-100,y, 85, 30);
437 | [button addTarget:self action:@selector(skipAction) forControlEvents:UIControlEventTouchUpInside];
438 | return button;
439 | }
440 |
441 | //跳过按钮点击事件
442 | -(void)skipAction{
443 |
444 | //移除广告
445 | [XHLaunchAd removeAndAnimated:YES];
446 | }
447 |
448 | #pragma mark - XHLaunchAd delegate - 倒计时回调
449 | /**
450 | * 倒计时回调
451 | *
452 | * @param launchAd XHLaunchAd
453 | * @param duration 倒计时时间
454 | */
455 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd customSkipView:(UIView *)customSkipView duration:(NSInteger)duration{
456 | //设置自定义跳过按钮时间
457 | UIButton *button = (UIButton *)customSkipView;//此处转换为你之前的类型
458 | //设置时间
459 | [button setTitle:[NSString stringWithFormat:@"自定义%lds",duration] forState:UIControlStateNormal];
460 | }
461 |
462 | #pragma mark - XHLaunchAd delegate - 其他
463 | /**
464 | 广告点击事件回调(return YES移除广告,NO不移除广告)
465 | */
466 | -(BOOL)xhLaunchAd:(XHLaunchAd *)launchAd clickAtOpenModel:(id)openModel clickPoint:(CGPoint)clickPoint{
467 |
468 | NSLog(@"广告点击事件");
469 |
470 | //openModel即配置广告数据设置的点击广告时打开页面参数(configuration.openModel)
471 |
472 | if(openModel == nil) return NO;
473 |
474 | WebViewController *VC = [[WebViewController alloc] init];
475 | NSString *urlString = (NSString *)openModel;
476 | VC.URLString = urlString;
477 | //此处不要直接取keyWindow
478 | UIViewController* rootVC = [[UIApplication sharedApplication].delegate window].rootViewController;
479 | [rootVC.myNavigationController pushViewController:VC animated:YES];
480 |
481 | return YES;//YES移除广告,NO不移除广告
482 | }
483 |
484 | /**
485 | * 图片本地读取/或下载完成回调
486 | *
487 | * @param launchAd XHLaunchAd
488 | * @param image 读取/下载的image
489 | * @param imageData 读取/下载的imageData
490 | */
491 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd imageDownLoadFinish:(UIImage *)image imageData:(NSData *)imageData{
492 |
493 | NSLog(@"图片下载完成/或本地图片读取完成回调");
494 | }
495 |
496 | /**
497 | * 视频本地读取/或下载完成回调
498 | *
499 | * @param launchAd XHLaunchAd
500 | * @param pathURL 视频保存在本地的path
501 | */
502 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd videoDownLoadFinish:(NSURL *)pathURL{
503 |
504 | NSLog(@"video下载/加载完成 path = %@",pathURL.absoluteString);
505 | }
506 |
507 | /**
508 | * 视频下载进度回调
509 | */
510 | -(void)xhLaunchAd:(XHLaunchAd *)launchAd videoDownLoadProgress:(float)progress total:(unsigned long long)total current:(unsigned long long)current{
511 |
512 | NSLog(@"总大小=%lld,已下载大小=%lld,下载进度=%f",total,current,progress);
513 | }
514 |
515 | /**
516 | * 广告显示完成
517 | */
518 | -(void)xhLaunchAdShowFinish:(XHLaunchAd *)launchAd{
519 |
520 | NSLog(@"广告显示完成");
521 | }
522 |
523 | /**
524 | 如果你想用SDWebImage等框架加载网络广告图片,请实现此代理(注意:实现此方法后,图片缓存将不受XHLaunchAd管理)
525 |
526 | @param launchAd XHLaunchAd
527 | @param launchAdImageView launchAdImageView
528 | @param url 图片url
529 | */
530 | //-(void)xhLaunchAd:(XHLaunchAd *)launchAd launchAdImageView:(UIImageView *)launchAdImageView URL:(NSURL *)url
531 | //{
532 | // [launchAdImageView sd_setImageWithURL:url];
533 | //
534 | //}
535 |
536 | @end
537 |
--------------------------------------------------------------------------------
/XHLaunchAdExample/XHLaunchAdExample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // XHLaunchAdExample
4 | //
5 | // Created by zhuxiaohui on 16/6/13.
6 | // Copyright © 2016年 it7090.com. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------