iOS7—Day by day—Day7:Taking Snapshots of UIViews

来源:互联网 发布:新疆行知实验学校 编辑:程序博客网 时间:2024/05/23 01:57

这篇文章是天天品尝iOS7甜点系列的一部分,你可以查看完整的系列目录:天天品尝iOS7甜点


Introduction – 介绍

本章的实例程序能够在github上面进行访问,访问地址:github.com/ShinobiControls/iOS7-day-by-day

有一些情况你需要得到UIView对象的快照,为了提高分享快照应用程序的性能。已经存在的方法目前遭遇一下一些问题:

  • 代码并不是十分的简单
  • 复杂的渲染选项,例如层就很难复制
  • OpenGL代码层需要特别的代码
  • 制作快照的过程详单缓慢

事实上,并没有制作快照的通用代码可供复制使用。

但是在iOS7上面,一切变得如此简单,在UIViewUIScreen中的新方法可能很简单的实现多种效果的快照功能。

Snapshotting for Animation – 动画快照

我们经常要需要一个动画视图,但是这样做很复杂,并且需要编写很多的代码来进行行为的控制。

本篇我们使用的demo创建一个UIView子类,它由一系列的子view构成。每一个都进行旋转来展现一个优美的几何图形。

需要生成上述情况,我们需要需要下面的代码结构:

123456789101112
- (void)generateRotations {  for (CGFloat angle = 0; angle < 2 * M_PI; angle += M_PI / 20.0) {      UIView *newView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 250)];      newView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));      newView.layer.borderColor = [UIColor grayColor].CGColor;      newView.layer.borderWidth = 1;      newView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:0.4];      newView.transform = CGAffineTransformMakeRotation(angle);      newView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;      [self addSubview:newView];  }}

在创建这个视图的时候,并不是最好的方式来创建这个效果。尽管这样也是有效的。但是它们都展示在同一个点上面。

在view controller中,我们将会创建一些有用的工具方法,在项目中它会被反复的运用。第一个创建的方法是旋转视图,并且添加成一个子视图:

  • (void)createComplexView{complexView = [[SCRotatingViews alloc] initWithFrame:self.view.bounds];[self.containerView addSubview:conplexView];}

第二个简单的动画方法,可以模拟一个视图,压缩它的大小为{0, 0}:

  • (void)animateViewAwayAndReset:(UIView *)view {[UIView animateWithDuration:2.0 animations:^{view.bounds = CGRectZero;} completion:^(BOOL finished){[view removeFromSuperview:]; [self performSelector:@selector(createComplexView) withObject:nil afterDelay:1];}];};

当动画运行完成之后就会删除提供的视图,并且延迟创建一个新的_complexView来重置app

工具栏中的按钮Animate实现的方法如下:

123
- (IBAction)handleAnimate:(id)sender {  [self animateViewAwayAndReset:_complexView];}

下图就展示了我们创建的旋转视图时候的问题:

这个问题肯定是需要修改的,所以我们将会修改SCRotatingViews的结构.

一个新的快照的方法由此产生,通过添加工具拦中的SShot按钮调用下面的方法:

123456
- (IBAction)handleSnapshot:(id)sender {  UIView *snapshotView = [_complexView snapshotViewAfterScreenUpdates:NO];  [self.containerView addSubview:snapshotView];  [_complexView removeFromSuperview];  [self animateViewAwayAndReset:snapshotView];}

我们调用snapshotViewAfterScreenUpdates:来创建一个复合视图的快照。这返回的UIView代表了调用视图的整体外观。由于我们可以看出来创建一个快照是如此的简单,令人振奋,而且比以前的旧方法(需要生成一个位图)要快得多。

一旦我们获得了快照之后,我们就可以把它添加到容器中,并且删除存在的复合视图,然后我们就可以让快照视图实现动画效果:

Pre/post View Updates

方法snapshotViewAfterScreenUpdates:有一个BOOL类型的入参,它是指定是否立即生成快照,还是在需要视图更新的时候生成。

在实例中,我们需要在SCRotatingViews中添加下面的方法:

12345
- (void)recolorSubviews:(UIColor *)newColor {  for (UIView *subview in self.subviews) {      subview.backgroundColor = newColor;  }}

这个方法调用时候就是重新设置所有子视图的背景色。

为了验证上述BOOL类型入参形成的效果,我们创建两个方法,对应到工具栏中的Pre按钮和Post按钮。

12345678910111213141516171819
- (IBAction)handlePreUpdateSnapshot:(id)sender {  // Change the views  [_complexView recolorSubViews:[[UIColor redColor] colorWithAlphaComponent:0.3]];  // Take a snapshot, Don't wait for changes to be applied  UIView *snapshotView = [_complexView snapshotViewAfterScreenUpdates:NO];  [self.containerView addSubview:snapshotView];  [_complexView removeFromSuperview];  [self animateViewAwayAndReset:snapshotView];}- (IBAction)handlePostUpdateSnapshot:(id)sender {  // Change the views  [_complexView recolorSubViews:[[UIColor redColor] colorWithAlphaComponent:0.3]];  // Take a snapshot, Don't wait for changes to be applied  UIView *snapshotView = [_complexView snapshotViewAfterScreenUpdates:YES];  [self.containerView addSubview:snapshotView];  [_complexView removeFromSuperview];  [self animateViewAwayAndReset:snapshotView];}

上面两个方法除了snapshotViewAfterUpdates:方法的入参不一样,其他都是相同的。首先我们调用recolorSubviews:方法,然后执行相同的生成快照的程序。下面是展示两种方法生成快照不同的效果图:

正如预期,设置NO将会立即生成快照,并且没有包含调用重新设置颜色的方法。设置YES会等到当前队列中所有的方法运行完成之后才会进行生成快照。

Snapshotting to an image – 生成快照图片

在进行上述动画的时候,我们实际上是把快照生成为一个UIView,然而有些时候生成一个真实的图片是很有帮助的。例如,我们希望在进行动画之前,让当前的视图模糊化。有另外一个方法可能实现这样的意图drawViewHierarchyInRect:afterScreenUpdates:.这样可以允许你用一个核心的图形上下文进行绘制视图,所以你可以得到当前视图的位图。 很显然,这个方法比snapshotViewAfterScreenUpdates:效率低,但是如果你需要一个位图,那这是最好的方法了。

我们绑定工具栏的Image按钮,来实现下面的方法:

123456789101112131415161718
- (IBAction)handleImageSnapshot:(id)sender {  // Want to create an image context - the size of complex view and the scale of the device screen  UIGraphicsBeginImageContextWithOptions(_complexView.bounds.size, NO, 0.0);  // Render our snapshot into the image context  [_complexView drawViewHierarchyInRect:_complexView.bounds afterScreenUpdates:NO];    // Grab the image from the context  UIImage *complexViewImage = UIGraphicsGetImageFromCurrentImageContext();  // Finish using the context  UIGraphicsEndImageContext();    UIImageView *iv = [[UIImageView alloc] initWithImage:[self applyBlurToImage:complexViewImage]];  iv.center = _complexView.center;  [self.containerView addSubview:iv];  [_complexView removeFromSuperview];  // Let's wait a bit before we animate away  [self performSelector:@selector(animateViewAwayAndReset:) withObject:iv afterDelay:1.0];}

我们首先创建一个图像上下文,为_complexView正确的大小和缩放比例,然后再次调用drawHierarchyInRect:afterScreenUpdates:方法,这个方法的第二个入参和上面介绍的是一样的效果。

然后我们将图像上下文放入UIImage中,然后将会把这个图像显示到UIImageView上面,然后和上述一样替换视图并且让它运行动画。为了说明我们为什么需要UIImage而不是UIView,我们创建一个方法来模糊化UIImage:

12345678910
- (UIImage *)applyBlurToImage:(UIImage *)image {  CIContext *context = [CIContext contextWithOptions:nil];  CIImage *ci_image = [CIImage imageWithCGImage:image.CGImage];  CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];  [filter setValue:ci_image forKey:kCIInputImageKey];  [filter setValue:@5 forKey:kCIInputRadiusKey];  CIImage *result = [filter valueForKey:kCIOutputImageKey];  CGImageRef cgImage = [context createCGImage:result fromRect:[result extent]];  return [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];}

这是一个简单的应用程序的CoreImage过滤器,应用高斯滤波器和返回一个新的UIImage。

下面就是我们刚刚创建的一个效果:

Limitaions – 局限性

以前生成快照需要调用OpenGL-backed的很复杂的方法。振奋的是新版本的UIView可以与OpenGL无缝的结合。

因为创建的快照必须是能够在手机屏幕上面看到的部分,如果超出了屏幕范围,生成的快照就会如下图所示:

Conclusion – 总结

在iOS快照UIView元素一直是非常有用的,和iOS7我们终于有了一个合理的API方法允许我们采取的通用的方式生成视图快照。这并不意味着没有限制,你仍然需要使用替代方法对于某些场景,但是这个方法可以解决90%的有关快照的问题。

本文翻译自:iOS7 Day-by-Day :: Day 7 :: Taking Snapshots of UIViews

 2014-01-09 Thu  iOS7, iOS7 Day-by-Day, 翻译

0 0
原创粉丝点击