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

如何对使用了autolayout的UIView添加动画,第1张

当我们对一个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,d出键盘后整个界面为了避免被遮住而向上移动的效果。选取完成日期后点击生日日期按钮或者完成按钮整个 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是否以d出:

@property (nonatomic, assign) BOOL hasShowPickerView

接下来定义一个辅助函数,用于查找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约束:

[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.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]

    }]

  }

}

上述代码中的[self.view layoutIfNeed]去掉也是没问题的。可能比较费解的是根view.bounds.origin.y的上移gap的计算以及top constraint的constant值的计算,关键实在真正理解view的frame和bounds的意义。

至此程序达到了预期的效果。

第一种

添加两个文件  UIViewExt.h与UIViewExt.m文件

在ViewController.h文件中导入头文件

#import"UIViewExt.h"

然后宏定义 获取设备的高与宽

#define HEIGHT self.view.height

#define WIDTH self.view.width

然后在ViewController.m文件中初始化各种控件时就可以使用视图的相对位置

self.lblName=[[UILabelalloc]initWithFrame:CGRectMake(self.view.left+50,self.view.top+100,WIDTH/8,HEIGHT/16)]

self.lblPassworw=[[UILabelalloc]initWithFrame:CGRectMake(self.view.left+50,self.lblName.bottom+10,WIDTH/8,HEIGHT/16)]

第二种

这种方法是 等比缩放

首先在AppDelegate.h文件里面

宏定义 获取设备的高与宽

#define SCREENHEIGHT [[UIScreen mainScreen] bounds].size.height

#define SCREENWIDTH [[UIScreen mainScreen] bounds].size.width

接槐或斗着声明两个属性变量

@property(assign,nonatomic)floatautoSizeScaleX

@property(assign,nonatomic)floatautoSizeScaleY

在AppDelegate.m文件里面

//初始化AppDelegate单例的方法

AppDelegate*myDelegate=[[UIApplicationsharedApplication]delegate]

//判断屏幕的高大于480即为iPhone5或以上设备因为它们屏幕都是等比增长的

if(SCREENHEIGHT>480)

{

/**

*以iPhone5为基准若是iPhone5

则myDelegate.autoSizeScaleX=SCREENWIDTH/320

即为myDelegate.autoSizeScaleX=320/320

若是iPhone6

则myDelegate.autoSizeScaleX=SCREENWIDTH/320

即为myDelegate.autoSizeScaleX=375/320

*/

myDelegate.autoSizeScaleX=SCREENWIDTH/320

myDelegate.autoSizeScaleY=SCREENHEIGHT/568

}

else{

/**

*否则即为iPhone4

*/

myDelegate.autoSizeScaleX=1.0

myDelegate.autoSizeScaleY=1.0

}

在ViewController.h文件中使用时导入头文件

#import"AppDelegate.h"

模仿系统的CGRectMake方法 重写一个CGRectMake1方法  在初始化控件时用这个方法就可以实现等比缩放 来失陪不同屏幕尺寸的iPhone

/**

*  CG_INLINE为内联函数

将铅磨CGRectMake重新定义为CGRectMake1

*

*  @param x      <#x description#>

*  @param y      <#y description#>

*  @param width  <#width description#>

*  @param height <#height description#>

*

*  @return rect的大小团轿

*/

CG_INLINECGRect

CGRectMake1(CGFloatx,CGFloaty,CGFloatwidth,CGFloatheight)

{

CGRectrect

AppDelegate*myDelegate=[[UIApplicationsharedApplication]delegate]

rect.origin.x= x *  myDelegate.autoSizeScaleX

rect.origin.y= y *  myDelegate.autoSizeScaleY

rect.size.width= width * myDelegate.autoSizeScaleX

rect.size.height= height * myDelegate.autoSizeScaleY

returnrect

}

 1.首先导入系统库 Accelerate.framework

陆链2.其次在要实现毛玻璃效果的页面添加头文件 #import

3.再次添加实现函数如下:

//加模糊效果函数,传入参数:image是图片,blur是模糊度(0~2.0之间)源游

- (UIImage *)blurryImage:(UIImage *)image withBlurLevel:(CGFloat)blur

 早裂孙 {

//模糊度,

if ((blur <0.1f) || (blur >2.0f))

{

blur = 0.5f

}

//boxSize必须大于0

int boxSize = (int)(blur * 100)

boxSize -= (boxSize % 2) + 1

NSLog(@"boxSize:%i",boxSize)

//图像处理

CGImageRef img = image.CGImage

//图像缓存,输入缓存,输出缓存

vImage_Buffer inBuffer, outBuffer

vImage_Error error

//像素缓存

void *pixelBuffer

//数据源提供者,Defines an opaque type that supplies Quartz with data.

CGDataProviderRef inProvider = CGImageGetDataProvider(img)

// provider’s data.

CFDataRef inBitmapData = CGDataProviderCopyData(inProvider)

//宽,高,字节/行,data

inBuffer.width = CGImageGetWidth(img)

inBuffer.height = CGImageGetHeight(img)

inBuffer.rowBytes = CGImageGetBytesPerRow(img)

inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData)

//像数缓存,字节行*图片高

pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img))

outBuffer.data = pixelBuffer

outBuffer.width = CGImageGetWidth(img)

outBuffer.height = CGImageGetHeight(img)

outBuffer.rowBytes = CGImageGetBytesPerRow(img)

// 第三个中间的缓存区,抗锯齿的效果

void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img))

vImage_Buffer outBuffer2

outBuffer2.data = pixelBuffer2

outBuffer2.width = CGImageGetWidth(img)

outBuffer2.height = CGImageGetHeight(img)

outBuffer2.rowBytes = CGImageGetBytesPerRow(img)

//将一个隐式的M×N区域颗粒和具有箱式滤波器的效果的ARGB8888源图像进行卷积运算得到作用区域。

error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend)

error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend)

error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend)

if (error)

{

NSLog(@"error from convolution %ld", error)

}

//NSLog(@"字节组成部分:%zu",CGImageGetBitsPerComponent(img))

//颜色空间DeviceRGB

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB()

//用图片创建上下文,CGImageGetBitsPerComponent(img),7,8

CGContextRef ctx = CGBitmapContextCreate(

outBuffer.data,

outBuffer.width,

outBuffer.height,

8,

outBuffer.rowBytes,

colorSpace,

CGImageGetBitmapInfo(image.CGImage))

//根据上下文,处理过的图片,重新组件

CGImageRef imageRef = CGBitmapContextCreateImage (ctx)

UIImage *returnImage = [UIImage imageWithCGImage:imageRef]

//clean up

CGContextRelease(ctx)

CGColorSpaceRelease(colorSpace)

free(pixelBuffer)

free(pixelBuffer2)

CFRelease(inBitmapData)

CGImageRelease(imageRef)

return returnImage

}

4.使用的时候直接传入所需参数,将返回的image直接运行就可以了

UIImage * maoImage = [UIImage imageNamed:@"aboutBackImage.jpg"]

tabBarImage.image = [self blurryImage:maoImage withBlurLevel:0.5]

大功告成,(*^__^*) ……小伙伴们快去试试效果吧~


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/12260372.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存