iOS中从CALayer获取UIImage

46
在我的应用程序中,我创建了一个CALayer(带有一些子层 - CALayer由添加为子层的形状组成)。
我正在尝试创建一个UIImage,以便能够将其上传到服务器(我已经有了上传代码)。 但是,我无法弄清楚如何将CALayer添加到UIImage中。
这是可能的吗?
4个回答

114

看起来你想将自己的图层渲染为UIImage。在这种情况下,下面的方法应该能解决问题。只需将此方法添加到您的视图或控制器类中,或在CALayer上创建一个类别。

Obj-C

- (UIImage *)imageFromLayer:(CALayer *)layer
{
  UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, 0);

  [layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();

  UIGraphicsEndImageContext();

  return outputImage;
}

Swift

func imageFromLayer(layer:CALayer) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(layer.frame.size, layer.isOpaque, 0)
    layer.render(in: UIGraphicsGetCurrentContext()!)
    let outputImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return outputImage!
}

1
交换为 UIGraphicsBeginImageContextWithOptions(layer.frame.size, YES, 0); - Jonny
11
我建议使用 UIGraphicsBeginImageContextWithOptions(layer.frame.size, layer.opaque, 0); - jfeldman
你如何更新代码以将CALayer的一部分(给定图层中的矩形)呈现为图像而不是整个框架? - zumzum
UIGraphicsGetCurrentContext() 返回 nil。 - Hogdotmac

18

Todd的回答是正确的,但对于视网膜屏幕应该有一点区别:

- (UIImage *)imageFromLayer:(CALayer *)layer
{

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        UIGraphicsBeginImageContextWithOptions([layer frame].size, NO, [UIScreen mainScreen].scale);
    else
        UIGraphicsBeginImageContext([layer frame].size);

    [layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return outputImage;
}

4
UIGraphicsBeginImageContextWithOptions()scale 参数指定为 0 时,默认使用 UIScreen.mainScreen.scale。根据文档,“应用于位图的比例因子。如果您指定一个值为 0.0,则比例因子将设置为设备主屏幕的比例因子。” 您的答案在功能上与 @Todd Yandell 的答案没有任何改进。 - Slipp D. Thompson

12

我已经基于此创建了一个 Swift 扩展程序:

extension UIImage {
    class func imageWithLayer(layer: CALayer) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(layer.bounds.size, layer.opaque, 0.0)
        layer.renderInContext(UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }
}

使用方法:

var gradient = CAGradientLayer()
gradient.colors = [UIColor.redColor().CGColor, UIColor.blueColor().CGColor]
gradient.frame = CGRect(x: 0, y: 0, width: 200, height: 200)

let image = UIImage.imageWithLayer(gradient)

6

增加一些错误检查上下文的Swift 3版本。

extension UIImage {
  class func image(from layer: CALayer) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(layer.bounds.size, 
  layer.isOpaque, UIScreen.main.scale)

    defer { UIGraphicsEndImageContext() }

    // Don't proceed unless we have context
    guard let context = UIGraphicsGetCurrentContext() else {
      return nil
    }

    layer.render(in: context)
    return UIGraphicsGetImageFromCurrentImageContext()
  }
}

1
你可以使用 defer 来处理 UIGraphicsEndImageContext() - Valérian
很好的建议。已更新为用户延迟。 - Justin Wright

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接