• 前言
    引导页,一个酷炫的页面,自从微博用了之后一下就火起来了,对于现在来说一个app如果没有引导页似乎总显那么不接地气,那么为了让我们的app也“高大上”一次,我写了一个demo来实现启动引导页的实现,鉴于我的强迫症,使用起来也是尽可能的简单才算罢休,这不,一句代码就搞定了,而且支持版本更新后显示新的引导页,先看效果:

LaunchIntroduction.gif

demo中封装了两个方法以供调用,一个是在滚动视图的最后一个页面带有进入按钮,一个是不带按钮,直接滚动就可隐藏引导页。


  • 特点
    1、使用简单,一句代码搞定
    2、支持自定义指示器的颜色
    3、支持应用升级后显示新的引导页
  • 下载地址:LaunchIntroductionDemo 下载
    先说使用方法,来不及的童靴可以先尝尝鲜:

    1、导入LaunchIntroductionView.m 和 LaunchIntroductionView.h 文件到工程中
    2、在APPDelegate中包含头文件 #import "LaunchIntroductionView.h"
    3、调用:
    [LaunchIntroductionView sharedWithImages:@[@"launch0.jpg",@"launch1.jpg",@"launch2.jpg",@"launch3"]];
    4、还有一种调用方法,是带button的:
    [LaunchIntroductionView sharedWithImages:@[@"launch0.jpg",@"launch1.jpg",@"launch2.jpg",@"launch3"] buttonImage:@"login" buttonFrame:CGRectMake(kScreen_width/2 - 551/4, kScreen_height - 150, 551/2, 45)];


  • 讲解

  • 头文件
    1. /**
    2. * 选中page的指示器颜色,默认白色
    3. */
    4. @property (nonatomic, strong) UIColor *currentColor;
    5. /**
    6. * 其他状态下的指示器的颜色,默认
    7. */
    8. @property (nonatomic, strong) UIColor *nomalColor;
    9. /**
    10. * 不带按钮的引导页,滑动到最后一页,再向右滑直接隐藏引导页
    11. *
    12. * @param imageNames 背景图片数组
    13. *
    14. * @return LaunchIntroductionView对象
    15. */
    16. +(instancetype)sharedWithImages:(NSArray *) imageNames;
    17. /**
    18. * 带按钮的引导页
    19. *
    20. * @param imageNames 背景图片数组
    21. * @param buttonImageName 按钮的图片
    22. * @param frame 按钮的frame
    23. *
    24. * @return LaunchIntroductionView对象
    25. */
    26. +(instancetype)sharedWithImages:(NSArray *) imageNames buttonImage:(NSString *) buttonImageName buttonFrame:(CGRect ) frame;
  • 核心实现
  • 实现原理
    创建一个UIView对象view,然后在view上添加一个scrollview,scrollview上添加需要的图片及按钮等,然后程序第一次启动或者应用升级后把view添加到当前的window上,这样view就显示了出来,当滑动到最后或者点击进入程序的按钮时将view从window上移除,为了更好的体验效果,在view移除时加了一个0.5s的动画,产生了一个view渐渐消失的效果。
  • 详细讲解
    1、下面是调用接口的实现,均采用的是单例形式
    得益于i_Steven的一席话,已修改,详情见文章底部的更新,不再使用单例的形式,也欢迎围观我的这篇文章单例的使用及避免对单例的滥用
    不带按钮时默认是滑动到最后时将引导页隐藏
    1. #pragma mark - 创建单例-->>不带button
    2. +(instancetype)sharedWithImages:(NSArray *)imageNames{
    3. static LaunchIntroductionView *launch = nil;
    4. static dispatch_once_t onceToken;
    5. dispatch_once(&onceToken, ^{
    6. images = imageNames;//背景数组
    7. isScrollOut = YES;//不带按钮时默认是滑动到最后时将引导页隐藏
    8. launch = [[LaunchIntroductionView alloc] initWithFrame:CGRectMake(0, 0, kScreen_width, kScreen_height)];
    9. launch.backgroundColor = [UIColor whiteColor];
    10. });
    11. return launch;
    12. }

    带按钮时默认是点击按钮时将引导页隐藏,滑动并不会隐藏引导页,按钮出现在最后一个页面

    1. #pragma mark - 创建单例-->>带button
    2. +(instancetype)sharedWithImages:(NSArray *)imageNames buttonImage:(NSString *)buttonImageName buttonFrame:(CGRect)frame{
    3. static LaunchIntroductionView *launch = nil;
    4. static dispatch_once_t onceToken;
    5. dispatch_once(&onceToken, ^{
    6. images = imageNames;
    7. isScrollOut = NO;//带按钮默认是点击按钮时将引导页隐藏
    8. enterBtnFrame = frame;//button的frame
    9. enterBtnImage = buttonImageName;//button的图片名字
    10. launch = [[LaunchIntroductionView alloc] initWithFrame:CGRectMake(0, 0, kScreen_width, kScreen_height)];
    11. launch.backgroundColor = [UIColor whiteColor];
    12. });
    13. return launch;
    14. }

    2、初始化时根据是不是首次启动或者更新版本后首次启动来判断要不要添加引导页到window上

    1. #pragma mark - 初始化
    2. -(instancetype)initWithFrame:(CGRect)frame
    3. {
    4. self = [super initWithFrame:frame];
    5. if (self) {
    6. //使用KVO监听指示器颜色的变化
    7. [self addObserver:self forKeyPath:@"currentColor" options:NSKeyValueObservingOptionNew context:nil];
    8. [self addObserver:self forKeyPath:@"nomalColor" options:NSKeyValueObservingOptionNew context:nil];
    9. if ([self isFirstLauch]) {
    10. UIWindow *window = [[UIApplication sharedApplication] windows].lastObject;
    11. [window addSubview:self];
    12. [self addImages];
    13. }else{
    14. [self removeFromSuperview];
    15. self = nil;
    16. }
    17. }
    18. return self;
    19. }

    3、版本更新或者首次启动的判断

    1. #pragma mark - 判断是不是首次登录或者版本更新
    2. -(BOOL )isFirstLauch{
    3. //获取当前版本号
    4. NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary];
    5. NSString *currentAppVersion = infoDic[@"CFBundleShortVersionString"];
    6. //获取上次启动应用保存的appVersion
    7. NSString *version = [[NSUserDefaults standardUserDefaults] objectForKey:kAppVersion];
    8. //版本升级或首次登录
    9. if (version == nil || ![version isEqualToString:currentAppVersion]) {
    10. [[NSUserDefaults standardUserDefaults] setObject:currentAppVersion forKey:kAppVersion];
    11. [[NSUserDefaults standardUserDefaults] synchronize];
    12. return YES;
    13. }else{
    14. return NO;
    15. }
    16. }

    4、滚动视图的创建就很简单了,不具体再介绍,大家可以下载代码看具体的实现,接下来主要说一下我在demo中遇到的稍微费点劲的问题:滚动方向的判断
    demo中现在用的是这样的判断:

    1. #pragma mark - 判断滚动方向
    2. -(BOOL )isScrolltoLeft:(UIScrollView *) scrollView{
    3. //返回YES为向左反动,NO为右滚动
    4. if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x < 0) {
    5. return YES;
    6. }else{
    7. return NO;
    8. }
    9. }

    其中translationInView的官方解释是这样的:

    translation in the coordinate system of the specified view

英文不太好,我的理解大概就是在一个特定view中的坐标,那么到底是干什么用的呢?log一下看看,结果返现只要往左滑动屏幕,由

  1. [scrollView.panGestureRecognizer translationInView:scrollView.superview].x

得到的值就为负数,反之则为正数,而且在手指触摸的起始地方为坐标的(0,0)点,那么好了,这样我就能判断左右滑动的方向了,然后就可以根据带不带button,在scrollview的delegate中在最后一个界面上来判断要不要隐藏引导页了:

  1. #pragma mark - scrollView Delegate
  2. -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
  3. int cuttentIndex = (int)(scrollView.contentOffset.x + kScreen_width/2)/kScreen_width;
  4. if (cuttentIndex == images.count - 1) {//保证是在最后一个页面
  5. if ([self isScrolltoLeft:scrollView]) {//是否为左滑
  6. if (!isScrollOut) {//在设置为滑动不能隐藏引导页的情况下直接返回
  7. return ;
  8. }
  9. [self hideGuidView];//反之在左滑成立的情况下隐藏引导页
  10. }
  11. }
  12. }

当然,并不是所有的事情看起来都那么理所当然,我刚开始用到的判断滑动方向的方法并不是这样的,而是:

  1. -(BOOL )isScrolltoLeft:(UIScrollView *) scrollView{
  2. BOOL ret = NO;
  3. static CGFloat newX = 0;
  4. static CGFloat oldX = 0;
  5. newX = scrollView.contentOffset.x;
  6. if (newX > oldX) {
  7. ret = YES;
  8. }else{
  9. ret = NO;
  10. }
  11. oldX = newX;
  12. return ret;//返回YES就是向左滑动,返货NO就是向右滑动
  13. }

根据scrollView的contentOffset来判断,这样做在scrollview的bounce为yes的情况下是完全没问题的,bounce如果为NO在最后一个页面是无法判断作画效果的,原因也很简单,但却让我费了一番功夫,bounce为no的情况下,在最后一个页面向左滑动时scrollview的contentOffset是不会发生发生辩护的,所以导致在无按钮的情况下无论怎么滑引导页都不会隐藏,那bounce就设为yes不就行了?但是引导页如果带个bounce效果,那实在是让人无法接受的,查找了多方资料才找到上面的那个方法,希望大家以后避免这个坑,少走弯路。


  • 详细使用方法
    1、不带按钮、不定义指示器的颜色:

    1. [LaunchIntroductionView sharedWithImages:@[@"launch0.jpg",@"launch1.jpg",@"launch2.jpg",@"launch3"]];

    是的,就这么一句代码就搞定了,我们只需要传入背景图片即可。
    2、不带按钮,定制指示器的颜色:

    1. LaunchIntroductionView *launch = [LaunchIntroductionView sharedWithImages:@[@"launch0.jpg",@"launch1.jpg",@"launch2.jpg",@"launch3"]];
    2. launch.currentColor = [UIColor redColor];
    3. launch.nomalColor = [UIColor greenColor];

    3、带按钮、不定制指示器的颜色:

    1. [LaunchIntroductionView sharedWithImages:@[@"launch0.jpg",@"launch1.jpg",@"launch2.jpg",@"launch3"] buttonImage:@"login" buttonFrame:CGRectMake(kScreen_width/2 - 551/4, kScreen_height - 150, 551/2, 45)];

    传的参数稍微多了点,不过也是应该并且值得的:背景图片数组,进入应用的按钮图片,按钮的frame,共三个参数
    4、带按钮,定制指示器的颜色

    1. LaunchIntroductionView *launch = [LaunchIntroductionView sharedWithImages:@[@"launch0.jpg",@"launch1.jpg",@"launch2.jpg",@"launch3"] buttonImage:@"login" buttonFrame:CGRectMake(kScreen_width/2 - 551/4, kScreen_height - 150, 551/2, 45)];
    2. launch.currentColor = [UIColor redColor];
    3. launch.nomalColor = [UIColor greenColor];


  • End

  • 更新1
    首先感谢i_Steven的意见,刚开始实现这个效果的时候想着用单例挺方便的,但随之而来的内存问题确实是存在的,或许占得内存确实是不大的,但是这确实是一个问题,是问题就得改,于是我改了:

    1. #pragma mark - 创建对象-->>不带button
    2. +(instancetype)sharedWithImages:(NSArray *)imageNames{
    3. images = imageNames;
    4. isScrollOut = YES;
    5. launch = [[LaunchIntroductionView alloc] initWithFrame:CGRectMake(0, 0, kScreen_width, kScreen_height)];
    6. launch.backgroundColor = [UIColor whiteColor];
    7. return launch;
    8. }
    1. #pragma mark - 创建对象-->>带button
    2. +(instancetype)sharedWithImages:(NSArray *)imageNames buttonImage:(NSString *)buttonImageName buttonFrame:(CGRect)frame{
    3. images = imageNames;
    4. isScrollOut = NO;
    5. enterBtnFrame = frame;
    6. enterBtnImage = buttonImageName;
    7. launch = [[LaunchIntroductionView alloc] initWithFrame:CGRectMake(0, 0, kScreen_width, kScreen_height)];
    8. launch.backgroundColor = [UIColor whiteColor];
    9. return launch;
    10. }

    于是也催生了我这篇单例的使用及避免对单例的滥用文章,欢迎围观。

  • 更新2
    更新时间: 2017.01.12
    更新内容:添加storyboard支持

使用storyboard时调用的方法

需要注意的是,如果项目不是使用的storyboard创建时调用此方法会导致crash。

IOS 一句代码搞定启动引导页的更多相关文章

  1. 对百度WebUploader的二次封装,精简前端代码之图片预览上传(两句代码搞定上传)

    前言 本篇文章上一篇: 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传) 此篇是在上面的基础上扩展出来专门上传图片的控件封装. 首先我们看看效果: 正文 使用方式同 ...

  2. 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)

    前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...

  3. QQ在线交谈一句代码搞定

    现在有很多网页都有QQ在线咨询,还有什么QQ客服什么的,看着很高大上的一个功能,其实要实现很简单,只需要一句代码就搞定. 还是按以前的套路,先看效果图,再晒源代码 点击图标 再点击 就可以聊天了 再来 ...

  4. python交换两个变量的值,一句代码搞定

    a = 10 b = 20 # 不需要中间变量,一步搞定 a, b = b, a

  5. C# CLosedXML四句代码搞定DataTable数据导出到Excel

    最近用到DataTable导出到Excel,网上看了一下,都不怎么好使,逛了下GitHub一下完美解决了 用到的.net库CLosedXML,这个库用于读取,处理和写入Excel 2007+(.xls ...

  6. AJ学IOS(41)UI之核心动画 两行代码搞定3D转场

    AJ分享,必须精品 效果: 代码: 其实代码很少,苹果都给封装好了 // 1.创建核心动画 CATransition *ca = [CATransition animation]; // 1.1动画过 ...

  7. iOS开发三步搞定百度推送

    iOS开发三步搞定百度推送   百度推送很简单,准备工作:在百度云推送平台注册应用,上传证书. 步骤一: 百度云推送平台 http://push.baidu.com/sdk/push_client_s ...

  8. 一句JS搞定只允许输入数字和字母

    一句JS搞定输入框只允许用户输入数字和字母类型的内容,对象是input输入框,当然也可以其它对象,只不过input输入框用的频率非常高.一句代码,不信么?那就看下边代码: <INPUT clas ...

  9. Asp.Net Core 轻松学-一行代码搞定文件上传 JSONHelper

    Asp.Net Core 轻松学-一行代码搞定文件上传   前言     在 Web 应用程序开发过程中,总是无法避免涉及到文件上传,这次我们来聊一聊怎么去实现一个简单方便可复用文件上传功能:通过创建 ...

随机推荐

  1. MFC的学习路线

    首先,MFC是比较难的!比C#和VB要难得多.MFC是基于C++的.首先C++必须熟悉.MFC主要是学习里面的控件的使用. 建议学习路线: 1. 易语言中文编程从入门到精通: https://deta ...

  2. [NOIP2015提高组]子串

    题目:洛谷P2679.Vijos P1982.codevs4560.UOJ#149. 题目大意:有长度为n的A串和长度为m的B串.现在要从A串中取出k个互不重叠的子串,使它们按顺序相连后得到B串.问有 ...

  3. Vue和vue-template-compiler版本之间的问题

    今天把远程仓库拉下项目,运行'npm run dev'时,报错 Module build failed: Error: Cannot find module 'vue-template-compile ...

  4. POJ 1258 Agri-Net (最小生成树+Prim)

    Agri-Net Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 39820   Accepted: 16192 Descri ...

  5. Xcode加入应用图标以及启动界面

    寻找了许久的资料,记录下Xcode加入应用图标以及启动界面的学习笔记: 很实用的网址: 1.(苹果官网)Designing for iOS 2.iOS 8 人机交互指南 图标和图片的尺寸參考: 更加具 ...

  6. hadoop集群中动态添加新的DataNode节点

    集群中现有的计算能力不足,须要另外加入新的节点时,使用例如以下方法就能动态添加新的节点: 1.在新的节点上安装hadoop程序,一定要控制好版本号,能够从集群上其它机器cp一份改动也行 2.把name ...

  7. 在cmd命令行中弹出Windows对话框(使用mshta.exe命令)

    有时候用bat写一些小脚本最后会弹出对话框提示操作成功,可以用mshta.exe来实现,它是Windows系统的相关程序,用来执行.HTA文件,一般计算机上面都有这个程序,实现如下: mshta vb ...

  8. jQuery插件开发初探

    最简单的插件 $.fn.changeStyle = function (colorStr) { $(this).css('color',colorStr); } 应用如下: <!DOCTYPE ...

  9. 6. 使用Axis开发WebService程序

    转自:http://www.itkeyword.com/doc/7529577946427268306/Apache-Servlet-WebSOAPWebService 所谓Web Service就是 ...

  10. HDU2665 kth number 线段树做法

    题意:求区间第k小 思路: 线段树 每个节点上保存 当前区间已经排序好的序列 (归并一下就好了嘛 复杂度 O(l)的) 这样建树的时空复杂度都是 O(nlogn)的 对于 每次询问 二分一个答案 在树 ...