允许用户在UIImage上绘制rect,以便裁剪图像

时间:2012-06-01 11:23:24

标签: ios ios5 uiimage crop

我确信已经从不同的角度多次询问过这个问题,但我现在还没有找到答案。

我想要实现的目标 我想要做的是显示UIImage,并允许用户在图像上绘制一个矩形,并最终裁剪他们的选择。

到目前为止的研究 我在这里找到了关于处理裁剪的SO的先前问题,但是它们经常处理不会改变的静态裁剪区域,这确实导致了这种机制的以下约束

  1. 您感兴趣的图片区域可能会被定位 在任何地方,例如,如果你试图裁剪路标,它可能 以一个图像为中心,但在另一个图像上对齐,因此你 无法预测要裁剪的区域。
  2. 例如,感兴趣区域的大小和规模可能会发生变化 一个图像可能是该路标的特写,因此裁剪区域 会更大,但另一张图片可能是从a 距离意味着裁剪区域会更小。
  3. 通过上述两个变量的组合,几乎不可能准确地预测图像中感兴趣的区域的位置,因此我依靠用户通过能够“绘制”一个方框来定义它我们感兴趣的区域,在这种情况下,是一个路标。

    这在Android上都是很好的,因为你可以用一个好的意图委托所有的努力工作,例如:

    Intent intent = new Intent("com.android.camera.action.CROP");

    但是,我找不到iOS的等价物。

    我从source发现了这段代码:

    - (UIImage *)imageByDrawingCircleOnImage:(UIImage *)image
    {
        // begin a graphics context of sufficient size
        UIGraphicsBeginImageContext(image.size);
    
        // draw original image into the context
        [image drawAtPoint:CGPointZero];
    
        // get the context for CoreGraphics
        CGContextRef ctx = UIGraphicsGetCurrentContext();
    
        // set stroking color and draw circle
        [[UIColor redColor] setStroke];
    
        // make circle rect 5 px from border
        CGRect circleRect = CGRectMake(0, 0,
                    image.size.width,
                    image.size.height);
        circleRect = CGRectInset(circleRect, 5, 5);
    
        // draw circle
        CGContextStrokeEllipseInRect(ctx, circleRect);
    
        // make image out of bitmap context
        UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
    
        // free the context
        UIGraphicsEndImageContext();
    
        return retImage;
    }
    

    我认为这是一个很好的裁剪起点(但它确实是裁剪圈),它确实依赖于在调用CGRectMake时预定义要裁剪的区域。

    previous question还详细说明了如何进行实际裁剪。

    我假设允许用户绘制矩形,我需要与手势集成吗?

    问题: 如何允许用户在图像视图上绘制矩形,以便裁剪该区域?

2 个答案:

答案 0 :(得分:2)

您可以尝试BJImageCropper

  

一个简单的UIView子类,允许用户裁剪图像。如果你使用它,我很想知道! Twitter:@barrettjacobsen

答案 1 :(得分:0)

这篇文章已有5年历史,但未来的参考资料,这就是我设法完成它的方法。以下代码是Rob's answer和一些图像裁剪的组合

这里使用Xcode 9和Swift 4

  1. 添加2个ViewControllers
  2. 为第一个视图控制器添加ImageView和2个按钮,为最后一个视图控制器添加另一个图像视图

    enter image description here

    将所有视图链接到源文件

    1. 查看控制器

      import UIKit
      
      extension UIView {
      func snapshot(afterScreenUpdates: Bool = false) -> UIImage {
      UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
      drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
      let image = UIGraphicsGetImageFromCurrentImageContext()!
      UIGraphicsEndImageContext()
      return image
      }
      }
      
      extension UIImage {
      func crop( rect: CGRect) -> UIImage {
      var rect = rect
      rect.origin.x*=self.scale
      rect.origin.y*=self.scale
      rect.size.width*=self.scale
      rect.size.height*=self.scale
      
      let imageRef = self.cgImage!.cropping(to: rect)
      let image = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation)
      return image
      }
      }
      
    2. 类ViewController:UIViewController {

      var rec: CGRect!
      var cropImage: UIImage!
      @IBOutlet weak var imageView: UIImageView!
      
      private let shapeLayer: CAShapeLayer = {
          let _shapeLayer = CAShapeLayer()
          _shapeLayer.fillColor = UIColor.clear.cgColor
          _shapeLayer.strokeColor = UIColor.green.cgColor
          _shapeLayer.lineWidth = 2
          return _shapeLayer
      }()
      
      private var startPoint: CGPoint!
      
      override func viewDidLoad() {
          super.viewDidLoad()
          imageView.layer.addSublayer(shapeLayer)
      }
      
      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
          clear()
          startPoint = touches.first?.location(in: imageView)
      }
      
      func clear() {
          imageView.layer.sublayers = nil
          imageView.image = UIImage(named: "aa")
          imageView.layer.addSublayer(shapeLayer)
      }
      
      override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
          guard let startPoint = startPoint, let touch = touches.first else { return }
          let point: CGPoint
      
          if let predictedTouch = event?.predictedTouches(for: touch)?.last {
              point = predictedTouch.location(in: imageView)
          } else {
              point = touch.location(in: imageView)
          }
          updatePath(from: startPoint, to: point)
      }
      
      override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
          guard let startPoint = startPoint, let touch = touches.first else { return }
          let point = touch.location(in: imageView)
          updatePath(from: startPoint, to: point)
          imageView.image = imageView.snapshot(afterScreenUpdates: true)
          shapeLayer.path = nil
      }
      
      override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
          shapeLayer.path = nil
      }
      
      private func updatePath(from startPoint: CGPoint, to point: CGPoint) {
          let size = CGSize(width: point.x - startPoint.x, height: point.y - startPoint.y)
          rec = CGRect(origin: startPoint, size: size)
          shapeLayer.path = UIBezierPath(rect: rec).cgPath
      }
      @IBAction func btnTapped(_ sender: Any) {
          clear()
      }
      
      @IBAction func btnCropTapped(_ sender: Any) {
          let newRec = CGRect(origin: CGPoint(x: rec.origin.x, y: rec.origin.y), size: rec.size)
          cropImage = imageView.image?.crop(rect: newRec)
          print(rec)
          print(newRec)
          self.performSegue(withIdentifier: "toImage", sender: nil)
      }
      
      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
          if segue.identifier == "toImage" {
              if let destination = segue.destination as? ImageViewController {
                  destination.croppedImage = cropImage
              }
          }
      }
      

      }