App啟動廣告頁的實現和封裝

weixin_33830216發表於2016-09-08

歡迎訪問我的部落格muhlenXi,該文章出自我的部落格。

版權宣告:本文為muhlenXi原創文章,轉載請註明出處,未經允許不得轉載.

導語:

也許你也注意到了,現在很多App在啟動頁載入完畢後,還會出現一個n秒的廣告頁面,頁面中有一個倒數計時的按鈕,我們可以通過點選跳過那個按鈕來跳過,我們點選廣告的時候,會進入廣告的詳情頁面。如果我們不做任何操作的話,當倒數計時為0秒是會自動進入主頁面。

接下來我們就研究研究這個是如何實現的。

解決方案

我們仔細想想:不妨也有兩種思路來解決。

  • 1、一種是App初次執行時,將廣告頁面的圖片URL和要點選廣告要跳轉的URL資料通過伺服器下載下來,然後再非同步下載圖片資料,最後將圖片的資料和跳轉URL儲存到本地沙盒中。第二次執行App的時候會顯示廣告介面,這時候再通過伺服器更新本地沙盒中的資料。第一次由於本地沙盒中沒有資料則不會顯示。目前大部分App採用此方式

  • 2、第二種是每次啟動時就通過伺服器非同步下載圖片資料和跳轉URL,然後將其顯示出來。這樣做的優點時,可以實時更新廣告頁面資料。缺點是當網路出現阻塞時或無網路時,會出現一個空白的廣告介面。

    針對該缺點,有一個解決思路,就是當網路不好時,用一個固定的圖片和跳轉URL來替換或者只用一個固定的圖片來替換,點選廣告則不跳轉。但是當跳轉URL失效時,需要通過迭代版本,重新上架來更新,週期比較長。

第一種方案。

請點選這裡檢視演示視訊

XYJAdvertisementView的實現

Xcode中通過File -> New ->File... 新建一個繼承與UIVIewXYJLaunchAdvertisingView

編寫程式的核心在於要有一個完整的思路!其次為了高效率的程式設計,我們需要學會一些偷懶的方法,比如巨集定義的使用、型別常量的使用、變數的高效命名...

【1】在XYJAdvertisementView.h檔案中,我們需要幾個巨集定義和型別常量。程式碼如下:

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

我們還需宣告一個圖片路徑和一個顯示廣告的show方法。

@property (nonatomic,copy) NSString * filePath; //!<  �圖片路徑 用於屬性傳值
- (void) showAD;  //顯示廣告頁面方法

【2】在XYJAdvertisementView.m檔案中,在匿名類中宣告我們需要的控制元件。

宣告如下:

@property (nonatomic,strong) UIImageView * adImageView;
@property (nonatomic,strong) UIButton * countBtn;  //倒數計時
@property (nonatomic,strong) NSTimer  * countTimer;
@property (nonatomic,assign) NSInteger  count;  //記錄當前的秒數

接下來我們依次實現相應的方法,我們要記住高內聚低耦合的原則

通過重寫initWithFrame方法來搭建我們的UI介面

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //其他控制元件的初始化寫在這裡
        //1.廣告圖片
        _adImageView = [[UIImageView alloc] initWithFrame:frame];
        _adImageView.userInteractionEnabled = YES;
        _adImageView.backgroundColor =  [UIColor yellowColor];
        _adImageView.contentMode = UIViewContentModeScaleAspectFill;
        _adImageView.clipsToBounds = YES;
        UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesHandle:)];
        [_adImageView addGestureRecognizer:tap];
        //2.跳過按鈕
        CGFloat btnW = 60.0f;
        CGFloat btnH = 30.0f;
        _countBtn = [[UIButton alloc] initWithFrame:CGRectMake(kScreenWidth-btnW-24, btnH, btnW, btnH)];
        [_countBtn addTarget:self action:@selector(dismissAD) forControlEvents:UIControlEventTouchUpInside];
        _countBtn.titleLabel.font = [UIFont systemFontOfSize:15];
        [_countBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [_countBtn setTitle:[NSString stringWithFormat:@"跳過%ld",adTime] forState:UIControlStateNormal];
        _countBtn.backgroundColor = [UIColor colorWithRed:38/255.0 green:38/255.0 blue:38/255.0 alpha:0.6];
        _countBtn.layer.cornerRadius = 4;
        [self addSubview:_adImageView];
        [self addSubview:_countBtn];
    }
    return self;
}
  • 通過懶載入的方式實現我們的定時器countTimer*

    所謂的懶載入也就是延時載入,即當物件需要用到的時候再去載入,簡單理解就是,重寫物件的get方法。當我們重寫get方法時,一定要注意,先要判斷當前物件是否為空,為空的話再去例項化物件。

    懶載入的優點如下:
    1、物件的例項化在get方法中實現,可以降低耦合度。
    2、不需要在viewDidLoad中再例項化物件,可以簡化程式碼,同時增強程式碼的可讀性。
    3、有效減少記憶體的佔用率。

程式碼如下:

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

實現我們的事件響應方法

//廣告介面點選
- (void) tapGesHandle:(UITapGestureRecognizer *) tap
{
   [self dismissAD];
   [[NSNotificationCenter defaultCenter] postNotificationName:pushToADNotiName object:nil userInfo:nil];
}
//定時器響應
- (void) countDownEventHandle
{
    _count--;
    [_countBtn setTitle:[NSString stringWithFormat:@"跳過%ld",_count] forState:UIControlStateNormal];
    if (_count == 0) {
        [self dismissAD];
    }
}
//跳過按鈕觸發
- (void) dismissAD
{
    [self.countTimer invalidate];
    self.countTimer = nil;
    [UIView animateWithDuration:0.3 animations:^{
    self.alpha = 0;
    } completion:^(BOOL finished) {
       [self removeFromSuperview];
    }];
}

實現我們前面宣告的Show方法

//啟動定時器
- (void) startTimer
{
    _count = adTime;
    [[NSRunLoop mainRunLoop] addTimer:self.countTimer forMode:NSRunLoopCommonModes];
}
//顯示廣告頁面
- (void)showAD
{
    [self startTimer];
    UIWindow * window = [UIApplication sharedApplication].keyWindow;
    [window addSubview:self];
}
//圖片賦值
- (void)setFilePath:(NSString *)filePath
{
    _filePath = filePath;
    _adImageView.image = [UIImage imageWithContentsOfFile:filePath];
}

到這裡,我們的View就定製完成,我們還需要一個資料管理類來管理沙盒中的廣告資料!

資料管理類XYJADDataManager的實現

通過File -> New ->File... 新建一個繼承與NSObjectXYJADDataManager

【1】在XYJADDataManager.h檔案中,宣告一個新增廣告的方法宣告。程式碼如下:

匯入標頭檔案:

#import "XYJAdvertisementView.h"

方法宣告:

+ (void) addXYJAdvertisementView;

【2】在XYJADDataManager.m檔案中,實現相應的方法。程式碼如下:

在實現show方法之前,我們要先實現一些幫助方法

通過圖片的名字獲取該圖片在沙盒中的絕對路徑

+ (NSString *)getFilePathWithImageName:(NSString *)imageName
{
    if (imageName) {
        NSArray * paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        //圖片預設儲存在Cache目錄下
        return [paths[0] stringByAppendingPathComponent:imageName];
    }
    return nil;
}

判斷該路徑下是否存在檔案

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

刪除舊的圖片

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

向伺服器請求廣告資料

該方法中要根據實際專案需求做相應調整

+ (void) UpdateAdvertisementDataFromServer
{
    //TODO 在這裡請求廣告的資料,包含圖片的圖片路徑和點選圖片要跳轉的URL
    //我們這裡假設從伺服器中獲得的 圖片的下載URl和跳轉URl如下所示
    NSString * imageurl = @"http://pic.paopaoche.net/up/2012-2/20122220201612322865.png";
    NSString * pushtoURl = @"http://www.jianshu.com";
    //獲取圖片名
   NSString * imageName = [[imageurl componentsSeparatedByString:@"/"] lastObject];
   //將圖片名與沙盒中的資料比較
    NSString * oldImageName =[[NSUserDefaults standardUserDefaults] objectForKey:adImageName];
    if ((oldImageName == nil) || (![oldImageName isEqualToString:imageName]) ) {
        //非同步下載廣告資料
        [self downloadADImageWithUrl:imageurl iamgeName:imageName];
        //儲存跳轉路徑到沙盒中
        [[NSUserDefaults standardUserDefaults] setObject:pushtoURl forKey:pushToADUrl];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
 }

非同步下載圖片資料

+ (void) downloadADImageWithUrl:imageUrl iamgeName:(NSString *) imageName
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //TODO 非同步操作
        //1、下載資料
        NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
        UIImage * image = [UIImage imageWithData:data];
        //2、獲取儲存檔案的路徑
        NSString * filePath = [self getFilePathWithImageName:imageName];
        //3、寫入檔案到沙盒中
        BOOL ret = [UIImagePNGRepresentation(image) writeToFile:filePath atomically:YES];
        if (ret) {
            NSLog(@"廣告圖片儲存成功");
            [self deleteOldImage];
            //儲存�圖片名和下載路徑
            [[NSUserDefaults standardUserDefaults] setObject:imageName forKey:adImageName];
            [[NSUserDefaults standardUserDefaults] setObject:imageUrl forKey:adDownloadUrl];
            [[NSUserDefaults standardUserDefaults] synchronize];
        } else {
            NSLog(@"廣告圖片儲存失敗");
        }
    });
}

實現addXYJAdvertisementView方法

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

最後一步,在AppDelegate檔案中新增廣告

  • 1、在AppDelegate.h檔案中匯入XYJADDataManager.h檔案 。
  • 2、在didFinishLaunchingWithOptions方法中加入如下程式碼即可。
//新增啟動廣告
[XYJADDataManager addXYJAdvertisementView];
  • 3、如果你需要獲取廣告點選事件,則需要進行如下操作。

在主介面ViewController的viewDidLoad方法中新增:

//新增廣告點選的監聽
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pushToADVC) name:pushToADNotiName object:nil];

同時實現事件響應方法:

- (void) pushToADVC
{
    //TODO 在這裡處理廣告事件響應
    NSLog(@"廣告點選了");
    XYJADWebViewController * webVC = [[XYJADWebViewController alloc] init];
    webVC.url = [[NSUserDefaults standardUserDefaults] objectForKey:pushToADUrl];
    [self.navigationController pushViewController:webVC animated:YES];
}

編譯執行後,會發現跟我們前面的效果展示是一樣的。

點這裡下載完整Demo

目前暫無實現第二種方案

感謝閱讀,有什麼建議可以給我留言

相關文章