horo

最近项目中需要对图片(包括静态图和动图GIF)进行一些自定义的操作,比如添加文本,图片,或是其他页面元素。就此把所学分享出来。

Quartz2D

Quartz2D是苹果公司提供的二维绘图引擎,可以实现绘图,绘制文字,绘制图片,截图,裁剪图片等功能。


静态图:即把当前View上的所有东西保存成一个新的图片


func screenCapture(view: UIView)-> UIImage {
    //创建一个位图的上下文
    UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
    //将view layer层上的东西渲染到当前上下文
    view.layer.render(in: UIGraphicsGetCurrentContext()!)
    //获取新图片
    let image = UIGraphicsGetImageFromCurrentImageContext()
    //关闭上下文
    UIGraphicsEndImageContext()
    return image!
}

这样我们通过将图片加载到UIImageView上,再在UIImageView添加子View(图片或是文本),
然后调用上面方法即可得到一张新的图片。


动态图:如果按照上述方法调用,只会得到一帧的图片,不是我们想要的效果。然后,动态图是由一张张静态图组成,
因此可以通过拆分图片,在每张图片添加自定义内容,然后再重新组成新的GIF图片。


func gifScreenCapture(rootView: UIImageView)-> UIImage {
    //1.将rootview的子节点subview先变成image(这里假定只有一个子View)
    let label = rootView.viewWithTag(1231) as! UILabel
    let labelImage = screenCapture(view: label)
        
    //2.将动图的每帧拆分,并在上面绘制其他图片
    let image = rootView.image!
        
    //获取图片相对于ImageView的比例,作为绘制其他内容的参照    
    let ratioX = image.size.width * 1.0 / rootView.frame.size.width
    let ratioY = image.size.height * 1.0 / rootView.frame.size.height
        
    //以image的实际尺寸设置位图上下文
    UIGraphicsBeginImageContextWithOptions(image.size, false, 0)
        
    var resultImages: [UIImage] = []
    
    //设定子View相对于Image的坐标系
    let newFrame = CGRect(x: label.frame.origin.x * ratioX,
                          y: label.frame.origin.y * ratioY,
                          width: label.frame.width * ratioX,
                          height: label.frame.height * ratioY)
        
    image.images?.forEach({ (_image) in
            
        let drawFrame = CGRect(x: 0,
                               y: 0,
                               width: image.size.width,
                               height: image.size.height)
        //这里设置上下文(画布)的背景颜色为白色(可选)
        //因为项目中GIF素材的每帧是没有背景的,所以最后拼成新图的时候有重影(这个问题困扰了很久,直到在xcode中看了下预览图才明白问题所在)
        //如果有碰到重影的同学,这里可以检查下
        let context = UIGraphicsGetCurrentContext()!
        UIColor.white.setFill()
        context.fill(drawFrame)
            
        //将每帧图片重新绘制在画布上
        _image.draw(in: drawFrame)
        //将子View生成的image绘制在画布上
        labelImage.draw(in: newFrame)
        //重新绘制新的每帧图片
        let imageSlice = UIGraphicsGetImageFromCurrentImageContext()
        resultImages.append(imageSlice!)
    })
    UIGraphicsEndImageContext()
        
    //3.将每帧图片重新合成动图
    return UIImage.animatedImage(with: resultImages, duration: image.duration)!
}

分类:

技术点:

相关文章: