当我们对一个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]
大功告成,(*^__^*) ……小伙伴们快去试试效果吧~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)