怎样做一个iOS App的启动分层引导动画?

一. 为什么要写这篇文章?

这是一个很古老的话题,从两年前新浪微博开始使用多层动画制作iOS App的启动引导页让人眼前一亮(当然,微博是不是历史第一个这个问题值得商榷)之后,各种类型的引导页层出不穷,到如今,github上也有了各种的成型的library存在供选择,同时不少app也已经慢慢的开始返璞归真回归单一静态引导页。虽然时尚的潮流不停的在变化,但是我一直在思索,这种多图层的启动引导动画到底是什么个结构?实现起来究竟有多难?本文,将试图探寻这个话题。

二. 我们要做成什么样子的?

首先定下目标,我们要实现的是启动引导画面中的一种——多层次动画。然后我们需要设定一个动画的主题,我们需要表达我们的情感,或者抒情~或者动人~或者逗比~。当然这大部分是设计师的工作。
好吧,既然是demo,而我又不懂设计又不懂美学又不懂PS大法,那么,就大概也许做成一个这样子的把~~~

总结一下最终目标要有几个要点:
1、4个页。
2、每个页都可能有若干分层,动画速度不同。
3、整个滑动的手感应该是顺滑并且是页面式的。

三. 用什么控件做?

开头我讲过,这是要探索,而不是为了实现,所以绝对不能借助任何3rd的library来完成。最大限度的利用apple的原生控件,是解决问题之道。

So,我们当然选用UIScrollView咯~~~除非你是个手工控。。。就要用最基本的UIView实现一个类似的滑动效果的UIScrollView。

啥?你问我UIScrollView是啥?

。。。。。。

下面是UIScrollView的几个关键属性,我相信你是明白的。需要注意的是,伴随着scrollview的左右拖动,contentOffset是在一直变换的。数值范围:(0,0) – (320 * 3, 0)。而这个属性,是我们需要使用的关键数值。

四. 怎么做?

上面我啰嗦了半天,最后告诉大家要用UIScrollView做,那么问题来了,挖掘机技术哪家强?啊不,到底应该怎么做?下面是干货~

1. 首先我们要把我们承载整个动画场面的scrollView造出来

如下,需要设置scrollView的几个关键属性:frame, contentSize, alwaysBounceHorizontal, paginEnabled(这个如果是NO,那么页面间的弹性效果就没了), delegate(需要设置从而获取scrollview的滚动状态)等等。

//初始化 scrollview
- (void)initScrollView
{
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
 
    _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, screenSize.height)];
//我们的scrollView的frame应该是屏幕大小
    _scrollView.contentSize = CGSizeMake(screenSize.width * 4, screenSize.height);
//但是我们希望我们scrollView的可被展现区域是4个屏幕横排那么大
    _scrollView.alwaysBounceHorizontal = YES;//横向一直可拖动
    _scrollView.pagingEnabled = YES;//关键属性,打开page模式。
    _scrollView.delegate = self;
    _scrollView.showsHorizontalScrollIndicator = NO;//不要显示滚动条~
    
    [self.view addSubview:_scrollView];
}

现在我们已经准备好了动画的画布,下面开始将每一页的元素加上去。

2. 加入页面元素

还是不要全篇幅贴代码了,以第一页为例把。
前面掉渣天的蛇鸡屎(我)的demo图已经表明,第一页,我们要有3个UILabel,一个UIImageView。
那么好,这些元素我们就给他声明出来。

@interface ViewController () 
@property (strong, nonatomic) UIScrollView *scrollView;//这是基本!
 
@property (strong, nonatomic) UIImageView *girlImageView;
@property (strong, nonatomic) UILabel *label_page1_1;
@property (strong, nonatomic) UILabel *label_page1_2;
@property (strong, nonatomic) UILabel *label_page1_3;
 
@end

然后把第一页的元素,加进来~

//为了更方便的初始化UILabel,我为UILabel增加了一个简易的类方法。是为了让代码更简洁可读。
+ (instancetype)labelWithText:(NSString *)text font:(UIFont *)font color:(UIColor *)color origin:(CGPoint)origin
{
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(origin.x, origin.y, 1000, 20)];
    label.text = text;
    label.font = font;
    label.textColor = color;
    [label sizeToFit];
    return label;
}
 
//然后我们将第一页的元素加进来。
 
    self.label_page1_1 = [UILabel labelWithText:@"我要买iPhone6!" font:[UIFont systemFontOfSize:18.0f] color:[UIColor redColor] origin:CGPointMake(140, 200)];
    [self.scrollView addSubview:self.label_page1_1];
 
    self.label_page1_2 = [UILabel labelWithText:@"我要看医生演唱会~~~~" font:[UIFont systemFontOfSize:18.0f] color:[UIColor blackColor] origin:CGPointMake(140, 240)];
    [self.scrollView addSubview:self.label_page1_2];
 
    self.label_page1_3 = [UILabel labelWithText:@"我要去大理!" font:[UIFont systemFontOfSize:18.0f] color:[UIColor orangeColor] origin:CGPointMake(140, 280)];
    [self.scrollView addSubview:self.label_page1_3];
    
    self.girlImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image_girl"]];
    self.girlImageView.frame = CGRectMake(100, kScreenHeight - 200 - 50, 100, 200);
   [self.scrollView addSubview:self.girlImageView];

3. 让第一页动起来~~

在第一页刚刚显示的时候,我们就希望第一页的元素能够有一个动起来的效果。那我们在上面刚刚加入第一页元素之后,可以紧接着做下面的事情:

    self.girlImageView.transform = CGAffineTransformMakeTranslation(-200, 0);
    self.label_page1_1.transform = CGAffineTransformMakeTranslation(- 100, 0);
    self.label_page1_2.transform = CGAffineTransformMakeTranslation(100, 0);
    self.label_page1_3.transform = CGAffineTransformMakeTranslation(- 120, 0);
    
    [UIView animateWithDuration:0.7
                     animations:^{
                         self.girlImageView.transform = CGAffineTransformMakeTranslation(0, 0);
                         self.label_page1_1.transform = CGAffineTransformMakeTranslation(0, 0);
                         self.label_page1_2.transform = CGAffineTransformMakeTranslation(0, 0);
                         self.label_page1_3.transform = CGAffineTransformMakeTranslation(0, 0);
                     }];

可以看到,我们分别给第一页的四个元素不同的水平位移,然后希望它用0.7秒的时间,移动到之前init他们时候的位置。这样就完成了第一个4层的错位动画。

然后,我们希望在手指滑动scrollview 的时候,第一页的四个元素可以有相应的分层错位动画,那么我们第一需要拿到当前scrollView的位移量,也就是前面提到的很重要的contentOffset。这个值,在:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

中,可以实时的获取。

具体来看,怎么做。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat currentX = scrollView.contentOffset.x;
    
    if (currentX <= kScreenWidth)
    {
        self.girlImageView.transform = CGAffineTransformMakeTranslation((kScreenWidth + 100.0f) * currentX / kScreenWidth, 0);
        self.label_page1_2.transform = CGAffineTransformMakeTranslation(- 200 * currentX / kScreenWidth, 0);
    }
}

呵呵,是不是看不懂,那就对了。。。

下面解释下,首先抛出两个定理:

定理一:在scrollview的滑动过程中,视觉上看,scrollview上的元素的移动方向与手指滑动方向相反,并且移动的距离与手指滑动的距离相等。但所有元素在scrollview上的物理位置并未改变。

定理二:在scrollview的滑动过程中,当且仅当scrollview上的元素的物理移动距离与手指滑动距离相等并且移动方向相反时,scrollview的元素视觉位置保持不变。

然后我们有两个需求:

第一,希望那个小女孩跟随手指滑动的时候,视觉上不是向左移动一直到消失,而是向右移动,待滑动到第二页的时候,小女孩出现在屏幕右侧。

我们应该明确,小女孩的移动,只能是在scrollview上位置的移动。根据定理二,我们知道,如果保持视觉上小女孩位置不变,小女孩在scrollView上的实际物理位移应该是:

公式 4.3.1 baseDistance = kScreenWidth 屏幕宽度

那么如果我们希望在移动到第二页之后,小女孩的视觉位置右移了100像素,那么小女孩在scrollView上的实际物理位移应该是:

公式 4.3.2 distance = baseDistance + 100

第一页到第二页,scrollView一共位移是 kScreenWidth ,当前scrollView位移是 contentOffset.x ,可以得出,当前位移的比例:

公式 4.3.3 status = scrollView.contentOffest.x / distance

由 4.3.1 4.3.2 4.3.3可得,我们设置小女孩位移的方式:

self.girlImageView.transform = CGAffineTransformMakeTranslation((kScreenWidth + 100.0f) * currentX / kScreenWidth, 0);

第二个需求,希望第一页中,第二个label的向左移动速度快于其他两个label。

根据定理二,和类似于上面的推倒(推导)方式,也易得第二个label的位移方式:

self.label_page1_2.transform = CGAffineTransformMakeTranslation(- 200 * currentX / kScreenWidth, 0);

五. 总结

综上所述,我们知道了分层动画的基本原理。如果使用更多的图层,更多的位移或者角度变化,就能组合出更加复杂的分层动画。

可以看到,分层动画的基本原理并不复杂,但是为什么那么多人倾向于借助3rd的library来实现呢?一个字,懒。

现如今移动开发领域对于美感和交互的要求越来越高,而开发出一款精美的app,设计师所需要付出的灵感和努力也越发显得重要。作为一个不怎么有美感的iOS工程师,想要在移动浪潮中立于不败之地,不断尝试更多新的可能远比实现更多的功能更加重要。

最后的最后,附一个demo运行效果:

点击查看demo

时间: 2024-11-10 07:16:25

怎样做一个iOS App的启动分层引导动画?的相关文章

(Android+IOS)正在做一个新闻App,做的差不多了,听听大家的建议 (图)

(Android+IOS)正在做一个新闻App,做的差不多了,听听大家的建议! 新闻采集器做好了,前端展示APP界面感觉还不是很好,还需要改进改进,希望发布(Android和IOS版本)前听听大家的建议! 新闻采集器做好了,前端展示APP界面感觉还不是很好,还需要改进改进,希望发布前听听大家的建议!

如何做一个iOS分形App

如何做一个iOS分形App 原文链接 : 如何做一个iOS分形App 原文作者 : Silviu Pop 译文出自 : 开发技术前线 www.devtf.cn 译者 : alier1226 校对者: MrLoong 状态 : 完成 介绍 在这个教程中,我们会做一个可以渲染Mandelbrot Set的应用程序,我们可以缩放和平铺它来看分形那令人惊叹的复杂之美.最终的结果如下: 着色程序的代码 void main() { #define iterations 128 vec2 position =

iOS App初次启动时的用户引导页制作实例分享

iOS App初次启动时的用户引导页制作实例分享 作者:老初 字体:[增加 减小] 类型:转载 时间:2016-03-09我要评论 这篇文章主要介绍了iOS App初次启动时的用户引导页制作实例分享,其中判断程序是否是第一次或版本更新以后第一次启动是一个关键点,需要的朋友可以参考下 应用程序APP一般都有引导页,引导页可以作为操作指南指导用户熟悉使用:也可以展现给用户,让用户了解APP的功能作用.引导页制作简单,一般只需要一组图片,再把图片组展现出来就可以了.展示图片组常用UIScrollVie

第一个IOS APP后续(审核,上架)

上一篇<第一个IOS APP总结 >我觉得预留2个星期审核上架应该够了,实际上用了1个星期,提交第5次版本审核过,相当于修改了4次,纪录一下避免下次再犯错.1.0.1版是第一个版本:拒绝!说是我的资源里面包含有免费字样,他说我选择的收费模式已经表明是免费,所以不需要再图片上再次申明.我原来的图片是这样的:我把永久免费去掉了,变成了这样:然后我提交了1.0.2版本:继续拒绝!原因是我不符合最小内容原则,我一看评审人员的截图,就是进入APP后没有数据,空白页面,我看了测试人员的测试时间是北京时间半

从AppStore获取一个iOS App的版本信息

做了一个iOS下的App,普通情况下,AppStore会提示更新的,但是用户如果没开推送,或者不打开AppStore,是不知道有新版本的,所以要做版本更新提醒,当AppStore上有新的版本时,提示用户进行更新.如何关键是如何获得AppStore上的版本信息,可以通过苹果提供的REST接口进行查询. NSString *urlStr = [[NSString alloc] initWithFormat:@"http://itunes.apple.com/lookup?id=%@",@&

教你动手做一个 iOS 越狱 app

前言 俗话说得好, 万事开头难. 仅仅是上图一个如此简单地不能再简单的小app, 其实都不算是app, 只是注入了一段代码进系统中, 等到特定的函数方法调用的时候就会被我们hook掉, 执行我们写的代码, 仅此而已. 哈哈, 就是这样一个小功能, 搞得我焦头烂额了一个晚上, 所以大家不用害怕, 连我这样的菜鸟都能实现, 大家只要认真看我的总结, 就一定可以自己也动手在自己越狱iPhone, iPad上嗨起来! ps : 为了纪念这神圣的一刻, 就算是这么简单的程序我也把它放到了我的Github上

判断ios app 第一次启动 别人的 嘿嘿

流行的解决方案是在大多数地方是用[NSUserDefaults standardUserDefaults的一个关键,如果它不存在,这意味着这是应用程序启动,否则,它不是第一次 在appdelegate.m中找到 “application:didFinishLaunchingWithOptions:” 方法, 添加以下代码: if (![[NSUserDefaults standardUserDefaults] boolForKey:@"everLaunched"]) {    [[NS

怎样判断ios app 第一次启动以及每次更新后的第一次启动

流行的解决方案是在大多数地方是用[NSUserDefaults standardUserDefaults]的一个关键,如果它不存在,这意味着这是应用程序第一次启动,否则,它不是第一次启动 在appdelegate.m中找到 “application:didFinishLaunchingWithOptions:”方法, 添加以下代码: 1 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"firstLaunch"]) { 2

第一个iOS App RadioStation

同样来自Mitch, Bennett, Lees的教材,本人编译环境为Xcode 7 新建IOS Project, SingleView Application. 在Xcode环境中 - UI布局,添加UI对象,链接UI组件和UI对象. UI布局:通过拖曳右下角的UI组件到Main.Storyboard中的空白Layout中     添加UI对象 本例中在ViewController的实例变量中添加了 IBOutlet类的各个UI组件的对象 - 对应三个动态Label,一个Slider 在类外面