iOS 如何提升图像绘制性能

本文译自 Guides and Sample Code中给出的对问题
Q: What can I do to improve my image drawing performance (CGContextDrawImage, UIImage/-drawInRect:, etc)? 的回答。

通常,在iOS开发中我们应该避免频繁调用-drawRect: 方法,
这一点对提升iPhone 4的性能至关重要,因为iPhone4屏幕着有4倍于之前的iPhone机型的像素点。(这篇文档虽然是2010年的,但我认为对于现在的iOS开发仍然适用)

如果你遇到了图像绘制性能问题,那么很可能你是在view的-drawRect: 方法中使用了 CGContextDrawImage或者UIImage/-drawInRect 快速地做了很多无效的重绘,甚至是每一帧都做了重绘。注意,iPhone/iPod touch/iPad刷新屏幕的方式决定了整个view会在你调用-setNeedsDisplayInRect:-setNeedsDisplay:
时进行重绘。也许你会认为更新view的一部分(比如子视图)只会对那一部分进行重绘,而事实并非如此,view会被当成一个整体,当调用-setNeedsDisplayInRect:-setNeedsDisplay: 整个view 都会被刷新。

每个UIView都有一个CALayer和多个图像作为层内容常驻内存,只要这个 CALayer还保留在层次结构中。这意味着你在app中看到的大多数操作,包括移动、旋转、view或layer的缩放,不需要重绘。因此对图像进行移动、旋转、缩放动画的最佳方式是先为图像创建view或layer然后用Core Animation 来为其添加动画。

让我们用一个简单的例子来说明吧,假设你想绘制一个围绕垂直于屏幕的z轴旋转的精灵,一种方式是给CGContextRotateCTM 加一个
rotation transform 并在旋转过程中在UIView的 -drawRect 方法中用CGContextDrawImage 重新绘制图像。但更好的方式是将这个图像设置为CALayer的content,然后为这个layer加上旋转动画。

1、将UIImage设置为UIImageView的图层内容:

    imageView.image = image;
2、z轴旋转的一个view:

- (void)rotateView:(UIView *)view toAngle:(float)angle
 {    
  [UIView beginAnimations:nil context:nil];
  [view setTransform:CGAffineTransformMakeRotation(angle)];
  [UIView commitAnimations]; 
 }

更多关于Core Animation的知识参考 Core Animation Programming Guide

如果一个图像有多个独立的移动模块,你应该将其拆分成多个view或layer,然后为它们分别加上动画。

有些情况下(不常见),CALayer 和 Core Animation也不适用。遇到这些情况,我们应该谨记CGContextDrawImageUIImage/-drawInRect:
会在解压和重新采样上消耗大量时间,因为图像并没有被缓存到图层树(layer tree)中。如果可以的话,调整你的资源,使图像大小(以像素为单位)与显示的大小(以像素为单位)相同,即iPhone 3GS及以前的图片资源格式为 image.png,普通Retina为 image@2x.png, plus Retina为image@3x.png,还有就是你可以通过将图像绘制到bitmap 上下文来将你创建的图像缓存起来。

注意:当图像绘制中使用`CGContextDrawImage``UIImage/-drawInRect:` 解压缩、缩放时,会特别消耗性能。

即使view或layer的contents以一种无法完全避免使用-drawRect: 的方式变化,我们也应该通过把这些contents分割成几个独立的view 或layer的方式来减少需要重新绘制的数量。即view包含若干独立模块时,建议将其拆分成一个含有多个subview或 sublayer的view,这样就可以避免在绘制局部的时候消耗的是刷新整个view的性能。

下面是如何对image进行resize的方法:

- (UIImage*)resizeImage:(UIImage*)image toWidth:(NSInteger)width height:(NSInteger)height
{
    // Create a graphics context with the target size
    // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
    // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
    CGSize size = CGSizeMake(width, height);
    if (NULL != UIGraphicsBeginImageContextWithOptions)
        UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    else
        UIGraphicsBeginImageContext(size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Flip the context because UIKit coordinate system is upside down to Quartz coordinate system
    CGContextTranslateCTM(context, 0.0, height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // Draw the original image to the context
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), image.CGImage);

    // Retrieve the UIImage from the current context
    UIImage *imageOut = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return imageOut;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值