求助,最近使用UIView+AutolayOut有个问题非常之不解

 我来答
野人无事不言L
2016-07-13
知道答主
回答量:0
采纳率:0%
帮助的人:0
展开全部
当我们对一个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是否以弹出: <span style="font-size:18px;">@property (nonatomic, assign) BOOL hasShowPickerView; </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 = 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:18px;"> [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:18px;">- (IBAction)didTapOnBirthdayButton:(id)sender { self.hasShowPickerView = !self.hasShowPickerView; if (self.hasShowPickerView) { CGRect birthdayButtonFrame = self.birthdayButton.frame; birthdayButtonFrame = [self.view convertRect:birthdayButtonFrame fromView:self.birthdayButton.superview]; CGFloat birthdayButtonYOffset = birthdayButtonFrame.origin.y + birthdayButtonFrame.size.height; CGFloat gap = birthdayButtonYOffset - (self.view.frame.size.height - self.pickerContainerView.frame.size.height); CGRect bounds = self.view.bounds; if (gap > 0) { bounds.origin.y = gap; } else { gap = 0; } [self replacePickerContainerViewTopConstraintWithConstant:birthdayButtonYOffset]; [UIView animateWithDuration:0.25 animations:^{ self.view.bounds = bounds; [self.view layoutIfNeeded]; }]; } else { [self replacePickerContainerViewTopConstraintWithConstant:self.view.frame.size.height]; CGRect bounds = self.view.bounds; bounds.origin.y = 0; [UIView animateWithDuration:0.25 animations:^{ self.view.bounds = bounds; [self.view layoutIfNeeded]; }]; } }</span> 上述代码中的[self.view layoutIfNeed]去掉也是没问题的。可能比较费解的是根view.bounds.origin.y的上移gap的计算以及top constraint的constant值的计算,关键实在真正理解view的frame和bounds的意义。 至此程序达到了预期的效果。
意法半导体(中国)投资有限公司
2023-06-12 广告
单片机,即单片微控制器,也称为单片微型计算机,是将中央处理器(CPU)、存储器(ROM,RAM)、输入/输出接口和其他功能部件集成在一块 在一个小块的集成电路上,从而实现对整个电路或系统的数字式控制。单片机不是完成某一个逻辑功能的芯片,而是... 点击进入详情页
本回答由意法半导体(中国)投资有限公司提供
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询
?>

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式