Swift - 使用 UIBezierPath 剪切图像

Posted

技术标签:

【中文标题】Swift - 使用 UIBezierPath 剪切图像【英文标题】:Swift - Clipping image with UIBezierPath 【发布时间】:2018-04-16 08:47:25 【问题描述】:

我想从图像中剪裁出贝塞尔路径。出于某种原因,图像仍然是未剪辑的。以及如何定位路径以便正确切割?

extension UIImage 

func imageByApplyingMaskingBezierPath(_ path: UIBezierPath, _ pathFrame: CGFrame) -> UIImage 

    UIGraphicsBeginImageContext(self.size)
    let context = UIGraphicsGetCurrentContext()!
    context.saveGState()

    path.addClip()
    draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))

    let maskedImage = UIGraphicsGetImageFromCurrentImageContext()!

    context.restoreGState()
    UIGraphicsEndImageContext()

    return maskedImage



【问题讨论】:

尝试删除 context.saveGState() 和 context.restoreGState() 如果您绘制路径而不是剪切它,它是否会出现在您希望剪切的大小和位置? @DavidRönnqvist 路径是我想要成为黑洞的路径。图像是我想要裁剪的。我添加了一个示例图片 【参考方案1】:

您需要将path.cgPath 添加到当前上下文中,还需要删除context.saveGState()context.restoreGState()

使用此代码

func imageByApplyingMaskingBezierPath(_ path: UIBezierPath, _ pathFrame: CGRect) -> UIImage 

            UIGraphicsBeginImageContext(self.size)
            let context = UIGraphicsGetCurrentContext()!

            context.addPath(path.cgPath)
            context.clip()
            draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))

            let maskedImage = UIGraphicsGetImageFromCurrentImageContext()!

            UIGraphicsEndImageContext()

            return maskedImage
        

使用它

let testPath = UIBezierPath()
testPath.move(to: CGPoint(x: self.imageView.frame.width / 2, y: self.imageView.frame.height))
testPath.addLine(to: CGPoint(x: 0, y: 0))
testPath.addLine(to: CGPoint(x: self.imageView.frame.width, y: 0))
testPath.close()

self.imageView.image = UIImage(named:"Image")?.imageByApplyingMaskingBezierPath(testPath, self.imageView.frame)

结果

【讨论】:

@luda 我的回答对你有帮助吗? 嗨 Reinier Melain,我需要裁剪图像的内部而不是外部。还是我没听懂你?我有一个需要从图像中删除的区域的 UIBezierPath。查看我添加的示例图片【参考方案2】:

你可以这样试试。

 var path = UIBezierPath()
 var shapeLayer = CAShapeLayer()
 var cropImage = UIImage()


 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
    if let touch = touches.first as UITouch?
        let touchPoint = touch.location(in: self.YourimageView)
        print("touch begin to : \(touchPoint)")
        path.move(to: touchPoint)
    


override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) 
    if let touch = touches.first as UITouch?
        let touchPoint = touch.location(in: self.YourimageView)
        print("touch moved to : \(touchPoint)")
        path.addLine(to: touchPoint)
        addNewPathToImage()
    


override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    if let touch = touches.first as UITouch?
        let touchPoint = touch.location(in: self.YourimageView)
        print("touch ended at : \(touchPoint)")
        path.addLine(to: touchPoint)
        addNewPathToImage()
        path.close()
    

 func addNewPathToImage()
    shapeLayer.path = path.cgPath
    shapeLayer.strokeColor = strokeColor.cgColor
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.lineWidth = lineWidth
    YourimageView.layer.addSublayer(shapeLayer)

   func cropImage()

    UIGraphicsBeginImageContextWithOptions(YourimageView.bounds.size, false, 1)
    tempImageView.layer.render(in: UIGraphicsGetCurrentContext()!)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    self.cropImage = newImage!
    


    @IBAction func btnCropImage(_ sender: Any) 
         cropImage()
    

在特定按钮操作上绘制路径后,只需调用您的 imageByApplyingMaskingBezierPath

【讨论】:

嗨 kalpash,我正在做类似的事情。最后,我有一个需要从图像中删除的区域的 UIBezierPath。最后一部分怎么做?查看我添加的示例图片 什么是 tempImageView? 只是图像视图名称忽略它。【参考方案3】:

这是基于 UIBezierPath 从图像中获取剪辑的 Swift 代码,它的实现速度非常快。如果图像已经显示在屏幕上,则此方法有效,通常是这种情况。生成的图像将具有透明背景,这是大多数人在剪辑照片图像的一部分时想要的。您可以在本地使用生成的剪切图像,因为您将在我称为 imageWithTransparentBackground 的 UIImage 对象中拥有它。这个非常简单的代码还向您展示了如何将图像保存到相机胶卷,以及如何将其直接放入粘贴板,以便用户可以将该图像直接粘贴到文本消息中,将其粘贴到 Notes、电子邮件等中。请注意,为了将图像写入相机胶卷,您需要编辑 info.plist 并提供“隐私 - 照片库使用说明”的原因

import Photos // Needed if you save to the camera roll

为剪辑提供 UIBezierPath。这是我的声明。

let clipPath = UIBezierPath()

使用某些命令组合使用您自己的逻辑填充 clipPath。以下是我在绘图逻辑中使用的一些。为 aPointOnScreen 等提供 CGPoint 等效项 构建相对于主屏幕的路径,因为 self.view 是这个应用程序的 ViewController(对于此代码),并且 self.view.layer 通过 clipPath 呈现。

                        
clipPath.move(to: aPointOnScreen)
clipPath.addLine(to: otherPointOnScreen)
clipPath.addLine(to: someOtherPointOnScreen)

clipPath.close()

此逻辑使用所有设备屏幕作为上下文大小。为此声明了一个 CGSize。 fullScreenX 和 fullScreenY 是我已经捕获设备宽度和高度的变量。如果您正在剪辑的照片已经放大并且在整个屏幕上显示的尺寸足够大,那就太好了。所见即所得。

                
let mainScreenSize =  CGSize(width: fullScreenX, height: fullScreenY)

// Get an empty context
UIGraphicsBeginImageContext(mainScreenSize)
// Specify the clip path
clipPath.addClip()
// Render through the clip path from the whole of the screen.
self.view.layer.render(in: UIGraphicsGetCurrentContext()!)
// Get the clipped image from the context
let image : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
// Done with the context, so end it.
UIGraphicsEndImageContext()
                
// The PNG data has the alpha channel for the transparent background
let imageData = image.pngData()
// Below is the local UIImage to use within your code
let imageWithTransparentBackground = UIImage.init(data: imageData!)
// Make the image available to the pasteboard.
UIPasteboard.general.image = imageWithTransparentBackground
// Save the image to the camera roll.
phphotoLibrary.shared().performChanges(
                    PHAssetChangeRequest.creationRequestForAsset(from: imageWithTransparentBackground!)
                , completionHandler:  success, error in
                    if success 
                        //
                    
                    else if let error = error 
                        //
                    
                    else 
                        //
                    
                )

【讨论】:

以上是关于Swift - 使用 UIBezierPath 剪切图像的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 3.x 中使用 UIBezierPath 画一条线

拖动并缩放使用 UIBezierPath IOS/Swift 绘制的矩形

Swift UIBezierPath 画圆,确定开始的位置

如何在 Swift 中未知宽度和高度的视图中居中 UIBezierPath 椭圆?

添加 Line 方法后 Swift Uibezierpath 没有响应

UIBezierPath 绘制 iOS Swift 的性能更好?