如何对使用了android autolayoutt的UIView添加动画

当我们对一个UIView使用了autolayout自动布局之后,也就意味着我们放弃了传统的通过设置view的frame等方式手动的修改、确定这个view的位置、尺寸属性。甚至从某种程度上讲,我们应该忘记view的frame属性:它的确定不再取决于我(手动的直接修改),而是通过我们在 storyboard或者code中提供的约束条件(constraints),通过一个自动布局引擎(苹果为autolayout采用的是
Cassowary布局引擎,参考文档: 点击打开链接 ),计算出这个view的frame。因此我们可以认为使用了autolayout的view的frame属性是一个只读的属性。在代码里认为的改动这个view的frame并不能对这个view的frame产生真正的效果(事实也确实如此)。
现在问题就来了,在以前我们经常通过对一个view的frame的修改产生view移动的动画效果,那么在使用了autolayout的view世界中我 们该如何实现相同的效果呢?答案是,我们“将计就计”,通过改变这个view上的某个约束constraint然后在uiview的animation block中触发layout来实现。
一、预期效果
下面我们以一个简单的例子来进行详细的说明:
如上图所示,整个界面都使用了autolayout,现在我们想实现这样一个效果:当我们点击显示生日的按钮的时候,整个view向上滑动,同时向上推出一 个日期选取器(date picker),类似于点击textfield,弹出键盘后整个界面为了避免被遮住而向上移动的效果。选取完成日期后点击生日日期按钮或者完成按钮整个 view向下缩回,同时date picker向下滑出可视范围。 &&
二、实现细节
首先来看一眼storyboard中view的层级结构:如下图所示,从图中我们可以看到,整个view的布局相当简单,就两级:根view和我们的 date picker view,其中date picker view包含了一个完成按钮和系统的date picker。这样的话,要实现整个view和date picker view同时上移的效果,我们只需要对根view和date picker view同时做动画即可。 &
考虑如何实现根view的动画效果,这里我们可以巧妙的通过修改根view的bounds属性来实现根view的上移效果。注意这里我 们需要明白view的bounds属性和frame属性的区别,前者是相对于当前view的本地坐标系而言的,而后者则是相对于当前view的父view的坐标系而言的。 &&
简单的讲,frame决定了一个view相对于父view的position和size信息。而bounds则决定了当前view展示的内容相对于本地坐 标系的位置。这里我们将view自身的可视内容和subviews可以看做一页纸上的内容信息,而view本身可以看成是一枚放于纸上的放大镜,放大镜的 大小不一定是和纸(content size)相同大小的。bounds属性的作用就是确定这枚放大镜相对于纸的位置:一个bounds =(0, 200, 300, 300)就意味着我们要将这枚放大镜向纸的下方移动200个points,但放大镜相对于父view的位置仍是保持不变的,这样给我们的效果就是这个
view(显示的内容)向上移动了200个points. &
改动bounds的origin属性并不会改动这个view的frame,通过这种展示内容的移动给我们产生一种view向上移动了的幻觉。如上图中,“哪个位置...”为成为我们放大镜中看到的第一行。 &&
根view上移动画的效果解决了,下面我们再来看日期选取器date picker,在storyboard中对其增加的约束如下:定高207、trailing/leading/top相对于super view (根view)的位置。 &
确定date picker view y轴方向上下移动的约束显然是top约束,点开top约束,可以看到该约束的详细内容:
一个约束可以描述为:
firstItem.attributeA = secondItem.attributeB * multipler + constant
结合上图我们可以得出date picker view的top约束为:&
datePickerView.Top&=&topLayoutGuide.bottom&*&1&+&400
我们可以通过修改这里的constant值来修改这个top约束以达到预期效果,事实上通过修改而不是删除旧的constraint再添加新的constraint也正是苹果所推荐的,在NSLayoutConstraint.h头文件中有如下说明: & &
这样,date picker view的上下移动就可以通过获取并修改其top约束来实现。需要注意的是在代码中获取date picker view的top约束实际上是要在其父view的constraints数组中查找,这是因为每个view的constraints数组中保存的实际上是 layout 子view所需的约束的集合。 & &&
我们还要定义个辅助BOOL变量,已判断date picker view是否以弹出: & &
@property&(nonatomic,&assign)&BOOL&hasShowPickerV
接下来定义一个辅助函数,用于查找date picker view的top约束并修改其constant属性为给定的值:
-&(void)replacePickerContainerViewTopConstraintWithConstant:(CGFloat)constant
&&&&for&(NSLayoutConstraint&*constraint&in&self.pickerContainerView.superview.constraints)&{
&&&&&&&&if&(constraint.firstItem&==&self.pickerContainerView&&&&constraint.firstAttribute&==&NSLayoutAttributeTop)&{
&&&&&&&&&&&&constraint.constant&=&
代码里我们在picker container view (即文中的date picker view)的superview的constraints属性中查找,如果发现firstItem和firstAttribute属性分别是date picker view和top,则该constraint即为目标约束,然后修改其constant属性。 & &&
在view首次被加载的时候我们想确保date picker view 处于整个view的最底部即隐藏的状态,因而我们在viewcontroller的viewDidLoad方法中调用辅助方法修改一下date picker view的top约束: & &
[self&replacePickerContainerViewTopConstraintWithConstant:self.view.frame.size.height];
在首次点击birthday button的时候动画修改根view的bounds和date picker view的top constraint,注意上移gap的计算。再次点击birthday button的时候将根view的bounds恢复到正常值,date picker view的top constraint也恢复到viewDidLoad中设置的值:
-&(IBAction)didTapOnBirthdayButton:(id)sender
&&self.hasShowPickerView&=&!self.hasShowPickerV
&&if&(self.hasShowPickerView)&{
&&&&CGRect&birthdayButtonFrame&=&self.birthdayButton.
&&&&birthdayButtonFrame&=&[self.view&convertRect:birthdayButtonFrame&fromView:self.birthdayButton.superview];
&&&&CGFloat&birthdayButtonYOffset&=&birthdayButtonFrame.origin.y&+&birthdayButtonFrame.size.
&&&&CGFloat&gap&=&birthdayButtonYOffset&-&(self.view.frame.size.height&-&self.pickerContainerView.frame.size.height);
&&&&CGRect&bounds&=&self.view.
&&&&if&(gap&&&0)&{
&&&&&&bounds.origin.y&=&
&&&&}&else&{
&&&&&&gap&=&0;
&&&&[self&replacePickerContainerViewTopConstraintWithConstant:birthdayButtonYOffset];
&&&&[UIView&animateWithDuration:0.25&animations:^{
&&&&&&self.view.bounds&=&
&&&&&&[self.view&layoutIfNeeded];
&&}&else&{
&&&&[self&replacePickerContainerViewTopConstraintWithConstant:self.view.frame.size.height];
&&&&CGRect&bounds&=&self.view.
&&&&bounds.origin.y&=&0;
&&&&[UIView&animateWithDuration:0.25&animations:^{
&&&&&&self.view.bounds&=&
&&&&&&[self.view&layoutIfNeeded];
上述代码中的[self.view layoutIfNeed]去掉也是没问题的。可能比较费解的是根view.bounds.origin.y的上移gap的计算以及top constraint的constant值的计算,关键实在真正理解view的frame和bounds的意义。
至此程序达到了预期的效果。 & &
在使用autolayout之前我们写程序控制界面的构成就好比是开一辆手动挡的汽车,虽然频繁换挡(修改frame)很繁琐,却也很享受那种可以完全控 制汽车档位的自由感。使用了autolayout之后则一下子升级为了自动挡汽车,切换档位的活不再由我们直接操作,而只能通过油门 (constraints)的大小来间接的改变汽车的档位。在自动挡汽车里,我们必须要放弃直接控制档位的想法,那是不可能的了,我们必须要学会通过熟练 掌握脚下的油门和刹车来控制车速!在习惯了自动挡之后,相信大家也一样能够得心应手的做自己想做的事情。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:21199次
积分:1088
积分:1088
排名:千里之外
原创:87篇
转载:31篇
(8)(7)(1)(2)(1)(2)(1)(3)(5)(3)(5)(3)(7)(3)(2)(2)(4)(4)(2)(3)(12)(16)(22)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'1.AutoLayout相关的几个易混淆的方法
setNeedsLayout
layoutIfNeeded
layoutSubViews
setNeedsUpdateConstraints
updateConstraitsIfNeed
updateConstraints
子视图在界面上的显示大概经过了:更新约束-通过约束依赖关系得到具体的frame-展示到界面。上面几个是和autolayout相关的方法,有必要大概了解一下这些方法具体是怎么用的以及在什么情况下触发。
1.[layoutView setNeedsUpdateConstraints]:告诉layoutView需要更新约束,在下次计算或者更新约束会更新约束
2.[layoutView updateConstraintsIfNeeded]:告诉layoutView立即更新约束,
3.updateConstraints:系统更新约束的实际方法
总结上面的3点就是,setNeedsUpdateConstraints确保了在将来某一时刻调用updateConstraintsIfNeeded之后会接着调用updateConstraints,从而达到更新view的约束的目的。但是要注意的是,如果仅仅单独调用2,不一定能够保证会调用updateConstraints,因为如果view上的约束是没有变动的且没有标记需要update的,这时就不会调用updateConstraints。
4.[layoutView setNeedsLayout]:告诉layoutView页面需要更新,但不立即执行
5.[layoutView layoutIfNeeded]:告诉layoutView页面布局立即更新
6.layoutSubviews:系统重写布局的实际方法
总结以上3点,setNeedsLayout确保了在将来某个时刻通过调用layoutIfNeeded之后会调用系统的layoutSubviews,从而重写对view重新布局。同样的如果单独调用5,不一定能够保证调用layoutSubviews。[注:笔者写了个demo发现,调用setNeedsLayout会直接调用layoutSubviews]。如果想要每次都能立即更新布局,那就要把两个方法一起用,同样也适用于1和2。
系统调用layoutSubViews时,就会调用updateConstraintsIfNeeded,通过更新约束,用superView到subView的层次顺序,来计算frame,反向确定布局。
上有关于上面几个方法的深入解答并分享了作者的实用经验:
如果仅想要立即改变约束,调用setNeedsLayout如果改变view的一些属性(如offsets)可能会导致布局的改变,那么调用setNeedsUpdateConstraints,更多的时候后面需要加setNeedsLayout。如果想要立即改变布局,如会形成新的frame,那么需要在调用layoutIfNeeded。
2.AutoLayout与动画
###[UIView animateWithDuration]方法
传统的动画主要是通过计算frame来进行动画,在autolayout下,主要是利用约束,动画的本质实际上是从一种约束状态变成另一种约束状态,从而来达到动画的目的。
这个例子的Demo在。
ViewController.h文件中:
@property (weak, nonatomic) IBOutlet UIView *animateV
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leftC
通过改变leftConstraint的值来实现具体的平移动画,具体代码如下:
self.leftConstraint.constant = 200;
[UIView animateWithDuration:2 animations:^{
[self.view layoutIfNeeded];
对于这类简单动画,只需要在animation的block中调用layoutIfNeeded即可,从经验来看,只要调用这个方法即可。
使用Masonry写动画
如果你厌倦了苹果官方的NSLayoutConstraint的繁杂写法以及VFL的奇怪语法,那么是个不错的选择。
Masonry的官方介绍,它是一个轻量级的布局框架,拥有自己的描述语法,采用优雅的链式语法封装自动布局,更加简单的添加和更新约束,提供了友好的属性和Debug功能,支持iOS和Mac,最重要的是大大提高了可读性。
目前Masonry处于维护状态(bugfix),原因是它的开发人员考虑到越来越多的人会用swift,所以开发人员更专注开发其swift版本–。这篇文章仍然用Masonry。
用cocoaPods引用Masonry:
pod 'Masonry'
Masonry提供了非常优雅的属性和方法:
属性:MASViewAttribute
Masonry提供的属性及其对应的NSLayoutAttribute关系如下:
MASViewAttribute
NSLayoutAttribute
view.mas_left
NSLayoutAttributeLeft
view.mas_right
NSLayoutAttributeRight
view.mas_top
NSLayoutAttributeTop
view.mas_bottom
NSLayoutAttributeBottom
view.mas_leading
NSLayoutAttributeLeading
view.mas_trailing
NSLayoutAttributeTrailing
view.mas_width
NSLayoutAttributeWidth
view.mas_height
NSLayoutAttributeHeight
view.mas_centerX
NSLayoutAttributeCenterX
view.mas_centerY
NSLayoutAttributeCenterY
view.mas_baseline
NSLayoutAttributeBaseline
我们可以很方便的获得到属性。
常见用法示例(引自)
//让当前view的top bottom left right和view2完全一样,表现为和view2大小一样
make.edges.equalTo(view2);
// 让当前view进行这样的约束:top = superview.top + 5, left = superview.left + 10,bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
//让当前view的size相对于titleLabel的size要greater than or equal to
make.size.greaterThanOrEqualTo(titleLabel)
// 让当前view进行约束:width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
// 当前view的中心点和button1相等
make.center.equalTo(button1)
// 当前view约束:centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
还可以将属性连起来写:
// 左右低对superview对齐,高和otherView对齐
make.left.right.and.bottom.equalTo(superview); //接近于自然语言了
make.top.equalTo(otherView);
1.mas_makeConstraints
给view添加约束的方法,
[masView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.view).offset(20);
make.top.equalTo(self.view).offset(200);
make.width.offset(80);
make.height.offset(80);
2.mas_updateConstraints
更新约束的方法,如果view已经使用了mas_makeConstraints这个方法后,在更新约束时需使用这个方法。
3.mas_remakeConstraints
重新添加约束,它是先将view上的约束全部uninstall掉,然后添加约束。
比较友好的Debug(引自)
在添加约束的时候避免不了的会遇到约束错误,苹果原生的报错真的很不友好,让人看不懂,如下:
Unable to simultaneously satisfy constraints.....blah blah blah....
&&NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(&=5000)]&&,
&&NSAutoresizingMaskLayoutConstraint:0x839ea20 h=--& v=--& V:[MASExampleDebuggingView:0x)]&&,
&&NSLayoutConstraint:0x7189c70 UILabel:0x7186980.bottom == MASExampleDebuggingView:0x7186560.bottom - 10&&,
&&NSLayoutConstraint:0x7189560 V:|-(1)-[UILabel:0x7186980]
(Names: '|':MASExampleDebuggingView:0x7186560 )&&
Will attempt to recover by breaking constraint
&NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(&=5000)]&
光UILabel:0x7186980可能也会非上一点时间找具体的Label是哪个。
Masonry重写了NSLayoutConstraint的(NSString *)description的方法,使得一起看起来较为友好了:
Unable to simultaneously satisfy constraints......blah blah blah....
&&NSAutoresizingMaskLayoutConstraint:0x8887740 MASExampleDebuggingView:superview.height == 416&&,
&&MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height &= 5000&&,
&&MASLayoutConstraint:BottomConstraint UILabel:messageLabel.bottom == MASExampleDebuggingView:superview.bottom - 10&&,
&&MASLayoutConstraint:ConflictingConstraint[0] UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1&&
Will attempt to recover by breaking constraint
&MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height &= 5000&
可以具体是UILabel:messageLabel.height出了问题。
在ViewController.m中,先对masView进行约束:
masView = [UIView new];
masView.backgroundColor = [UIColor redColor];
[self.view addSubview:masView];
[masView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.view).offset(20);//距离self.view左侧20
make.top.equalTo(self.view).offset(200);//距离self.view顶部200
make.width.offset(80);//宽度80
make.height.offset(80);//高度80
动画的实现代码:
[masView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.and.height.offset(100);
make.leading.equalTo(self.view).offset(100);
[UIView animateWithDuration:3 animations:^{
[self.view layoutIfNeeded];
先用mas_updateConstraints更新masView的约束,然后调用animateWithDuration方法。
通过用masonry实现的动画可读性更高,利于维护,且能实现比较多的动画效果。
3.实际经验
实践出真知,需要通过实际项目来进行实践验证。我也总结项目过程中的一些实际经验:
不能有任何一个约束的warning。对于约束的warning是要引起最够重视的,我们发现在iOS8下约束的warning没有引起crash,但是在iOS6上却crash。这个原因应该是苹果开发autolayout的历史变迁所致。所以当我们发现约束有warning的时候一定要改正确,项目中保证0warning。复杂页面的约束出现较多约束错误时,可以先清掉所有的约束,重新加约束。有时候发现约束越改越混乱,不如清掉所有的约束重新理清思路加约束。动画不一定非要是约束动画。我们知道autolayout到最终都是转化为对应的frame,所以frame是关键点。当一个视图(superView)布局好了之后,它的subView实际上仍然可以用传统的frame进行动画,不一定非要写约束。但是这个方法是不倡导的,既然用了约束就该将约束进行到底。牺牲了部分的效率。约束在转化为具体的frame过程中必然会产生性能上的问题,我们发现在iPhone4的iOS6上面表现的效果并不是很好,但是随着硬件的越来越强大,这个性能问题最终会被忽略掉的。
对于一般应用类的app上的动画效果,使用上述方法已经足够。方法最够的简单,需要理解的是其中的约束思想。文中用到的demo已经上传到。
通过这三篇关于AutoLayout的介绍文章,相信足以解决AutoLayout这个问题了。
本文已收录于以下专栏:
相关文章推荐
原创blog,转载请注明出处
blog.csdn.net/hello_hwc
欢迎关注我的iOS SDK详解专栏
http://blog.csdn.net/column/details/huan...
参考帖子:自动布局 Autolayout 报错:Unable to simultaneously satisfy constraints.参考帖子: Auto Layout 进阶参考帖子:谈Story...
在Main.storyboard拖入一个UIView,随便设置一个背景色,
使用autolayout  为紫色的view添加约束
:(0,0,100,100) ,
为该view添加动画代...
对于一个基于约束的布局视图,如何改变其值并且带有动画的特效,下面提供两种方法:
如下图,图中有一个很长的view和两个button,现在要求,改变view的高度,并且 两个Button随之上移,并且带...
用自动布局, 有时候拉出来一条约束, 需要改变, 单纯的改变会比较生硬, 这时想到加个简单的animation动画,
[UIView animateWithDuration:0.2f animati...
使用了Autolaout的视图的动画
方法一:简单的说就是先找到想要变化的NSLayoutConstraint
然后改变constant值
NSLayoutConstraint * _b...
记录一下,方便查阅:/questions//setneedslayout-vs-setneedsupdateconstraints-an...
虽然使用Autolayout;
如何对使用了autolayout的UIView添加动画
如何解决IOS 动画中 Autolayout 与View Transforms的冲突
How do I adjust the ancho...
他的最新文章
讲师:姜飞俊
讲师:汪木铃
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)关于Autolayout的动画使用_王子也寂寞self_新浪博客
一.xib模式下
当添加完约束之后,如果想改变约束后并且让改变的约束产生动画,不能直接用[UIViewanimateWithDuration:4animations:^{
&对约束动画的各种改变。。。
这样不会有任何的动画效果。正确的方式是调用layoutIfNeeded。
一个方法{
各种动画的。。。
&&[UIViewanimateWithDuration:4animations:^{
//只需要在块里调用一下下面这个方法就可以让上面的各种动画生效。​
[self.viewlayoutIfNeeded];
​为啥上图就系统繁忙。。。。。。
示例
​二.代码模式下
在添加约束​​的时候要注意相互之间的关系,如果只涉及到自身尺寸的调整,则[自己的view
addConstraints:]如果是与其他控件之间有相关的算法联系,则这个约束要添加到距离两个控件最近的父空间中,则[距离两个控件最近的父view
addConstraints:];
1.​​如何创建约束
使用​​方法:+(instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relationtoItem:(nullableid)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c;
这个方法中参数的含义:​
view1:目标控件
attr1:目标控件的属性
relation:关系(=,&=,&=)​
view2:参照物控件(可以为空)
attr2:参照物的属性(当view2为空时,参照物属性为NSLayoutAttributeNotAnAttribute)
multiplier:乘数
c:加上的常量
也就是一个公式:view1的attr1 &关系
&view2的attr2 &*
&multiplier &+
&c;​
宽度约束示例
简单的使用
王子也寂寞self
博客等级:
博客积分:0
博客访问:3,530
关注人气:0
荣誉徽章:查看: 7661|回复: 0
如何对使用了autolayout的UIView添加动画
当我们对一个UIView使用了autolayout自动布局之后,也就意味着我们放弃了
传统的通过设置view的frame等方式手动的修改、确定这个view的位置、尺寸属性。
甚至从某种程度上讲,我们应该忘记view的frame属性:它的确定不再取决于我
(手动的直接修改),而是通过我们在storyboard或者code中提供的约束条件
(constraints),通过一个自动布局引擎(苹果为autolayout采用的是Cassowary
布局引擎,参考文档:点击打开链接),计算出这个view的frame。因此我们可以
认为使用了autolayout的view的frame属性是一个只读的属性。在代码里认为的改动
这个view的frame并不能对这个view的frame产生真正的效果(事实也确实如此)。
现在问题就来了,在以前我们经常通过对一个view的frame的修改产生view移动
的动画效果,那么在使用了autolayout的view世界中我们该如何实现相同的效果呢?
答案是,我们“将计就计”,通过改变这个view上的某个约束constraint然后在uiview的
animation block中触发layout来实现。
一、预期效果
下面我们以一个简单的例子来进行详细的说明:
如上图所示,整个界面都使用了autolayout,现在我们想实现这样一个效果:
当我们点击显示生日的按钮的时候,整个view向上滑动,同时向上推出一个日期
选取器(date picker),类似于点击textfield,弹出键盘后整个界面为了避免被遮
住而向上移动的效果。选取完成日期后点击生日日期按钮或者完成按钮整个view向
下缩回,同时date picker向下滑出可视范围。
二、实现细节
首先来看一眼storyboard中view的层级结构:如下图所示,从图中我们可以
看到,整个view的布局相当简单,就两级:根view和我们的date picker view,其
中date picker view包含了一个完成按钮和系统的date picker。这样的话,要实现
整个view和date picker view同时上移的效果,我们只需要对根view和date picker
view同时做动画即可。
考虑如何实现根view的动画效果,这里我们可以巧妙的通过修改根view的
bounds属性来实现根view的上移效果。注意这里我们需要明白view的bounds属性
和frame属性的区别,前者是相对于当前view的本地坐标系而言的,而后者则是相
对于当前view的父view的坐标系而言的。
简单的讲,frame决定了一个view相对于父view的position和size信息。而
bounds则决定了当前view展示的内容相对于本地坐标系的位置。这里我们将view
自身的可视内容和subviews可以看做一页纸上的内容信息,而view本身可以看成
是一枚放于纸上的放大镜,放大镜的大小不一定是和纸(content size)相同大小
的。bounds属性的作用就是确定这枚放大镜相对于纸的位置:一个bounds =
(0, 200, 300, 300)就意味着我们要将这枚放大镜向纸的下方移动200个points,但
放大镜相对于父view的位置仍是保持不变的,这样给我们的效果就是这个view(显
示的内容)向上移动了200个points.
改动bounds的origin属性并不会改动这个view的frame,通过这种展示内容的
移动给我们产生一种view向上移动了的幻觉。如上图中,“哪个位置...”为成为我们
放大镜中看到的第一行。
根view上移动画的效果解决了,下面我们再来看日期选取器date picker,在
storyboard中对其增加的约束如下:定高207、trailing/leading/top相对于super
view (根view)的位置。
确定date picker view y轴方向上下移动的约束显然是top约束,点开top约束,
可以看到该约束的详细内容:
一个约束可以描述为:firstItem.attributeA = secondItem.attributeB * multipler
+ constant。
结合上图我们可以得出date picker view的top约束为
datePickerView.Top = topLayoutGuide.bottom * 1 + 400
我们可以通过修改这里的constant值来修改这个top约束以达到预期效果,事实
上通过修改而不是删除旧的constraint再添加新的constraint也正是苹果所推荐的,
在NSLayoutConstraint.h头文件中有如下说明:
这样,date picker view的上下移动就可以通过获取并修改其top约束来实现。
需要注意的是在代码中获取datepicker view的top约束实际上是要在其父view的
constraints数组中查找,这是因为每个view的constraints数组中保存的实际上是
layout 子view所需的约束的集合。
我们还要定义个辅助BOOL变量,已判断date picker view是否以弹出:
&span style=&font-size:18&&@property (nonatomic, assign) BOOL hasShowPickerV&/span&复制代码
接下来定义一个辅助函数,用于查找date picker view的top约束并修改其
constant属性为给定的值:
- (void)replacePickerContainerViewTopConstraintWithConstant:(CGFloat)constant{
for (NSLayoutConstraint *constraint in self.pickerContainerView.superview.constraints) {
if (constraint.firstItem == self.pickerContainerView && constraint.firstAttribute == NSLayoutAttributeTop) {
constraint.constant =
}}复制代码
代码里我们在picker container view (即文中的date picker view)的
superview的constraints属性中查找,如果发现firstItem和firstAttribute属性分别是
date picker view和top,则该constraint即为目标约束,然后修改其constant属性。
在view首次被加载的时候我们想确保date picker view 处于整个view的最底部即隐
藏的状态,因而我们在viewcontroller的viewDidLoad方法中调用辅助方法修改一下
date picker view的top约束:
&span style=&font-size:18&&[self replacePickerContainerViewTopConstraintWithConstant:self.view.frame.size.height];&/span&复制代码
在首次点击birthday button的时候动画修改根view的bounds和date picker
view的top constraint,注意上移gap的计算。再次点击birthday button的时候将根
view的bounds恢复到正常值,date picker view的top constraint也恢复到viewDidLoad
中设置的值:
&span style=&font-size:18&&- (IBAction)didTapOnBirthdayButton:(id)sender{
self.hasShowPickerView = !self.hasShowPickerV
if (self.hasShowPickerView) {
CGRect birthdayButtonFrame = self.birthdayButton.
birthdayButtonFrame = [self.view convertRect:birthdayButtonFrame fromView:self.birthdayButton.superview];
CGFloat birthdayButtonYOffset = birthdayButtonFrame.origin.y + birthdayButtonFrame.size.
CGFloat gap = birthdayButtonYOffset - (self.view.frame.size.height - self.pickerContainerView.frame.size.height);
CGRect bounds = self.view.
if (gap & 0) {
bounds.origin.y =
[self replacePickerContainerViewTopConstraintWithConstant:birthdayButtonYOffset];
[UIView animateWithDuration:0.25 animations:^{
self.view.bounds =
[self.view layoutIfNeeded];
[self replacePickerContainerViewTopConstraintWithConstant:self.view.frame.size.height];
CGRect bounds = self.view.
bounds.origin.y = 0;
[UIView animateWithDuration:0.25 animations:^{
self.view.bounds =
[self.view layoutIfNeeded];
}}&/span&复制代码
上述代码中的[self.view layoutIfNeed]去掉也是没问题的。可能比较费解的是
根view.bounds.origin.y的上移gap的计算以及top constraint的constant值的计算,
关键实在真正理解view的frame和bounds的意义。
至此程序达到了预期的效果。
在使用autolayout之前我们写程序控制界面的构成就好比是开一辆手动挡的汽
车,虽然频繁换挡(修改frame)很繁琐,却也很享受那种可以完全控制汽车档位的
自由感。使用了autolayout之后则一下子升级为了自动挡汽车,切换档位的活不再由
我们直接操作,而只能通过油门(constraints)的大小来间接的改变汽车的档位。在
自动挡汽车里,我们必须要放弃直接控制档位的想法,那是不可能的了,我们必须要
学会通过熟练掌握脚下的油门和刹车来控制车速!在习惯了自动挡之后,相信大家也
一样能够得心应手的做自己想做的事情。
上一篇:下一篇:

我要回帖

更多关于 ios autolayout 动画 的文章

 

随机推荐