【问题标题】:Allowing users to draw rect on a UIImage, with the intention of cropping the image允许用户在 UIImage 上绘制矩形,以裁剪图像
【发布时间】:2012-06-06 15:19:51
【问题描述】:

我确信这个问题已经从不同的角度被问过很多次了,但我还无法在这里找到答案。

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

研究至今 我在这里找到了关于处理裁剪的先前问题,但是它们通常处理不变的静态裁剪区域,这确实导致了这种机制的以下限制

  1. 您感兴趣的图像区域可能位于 任何地方,例如,如果你想裁剪一个路标,它可能 以一张图像为中心,但在另一张图像上左对齐,因此您 无法预测要裁剪的区域。
  2. 感兴趣区域的大小和比例可能会发生变化,例如 一张图片可能是该路标的特写,因此裁剪区域 会更大,但另一张图片可能是从 距离意味着裁剪区域会更小。

结合上述两个变量,几乎不可能准确预测图像中感兴趣的区域在哪里,所以我依靠用户通过能够“绘制”一个框来定义这一点我们感兴趣的区域,在本例中是路标。

在 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 还详细说明了如何进行实际裁剪。

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

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

【问题讨论】:

  • 我想将额外的 UIView 添加为 UIImageView 的子视图会容易得多 - 或者只是在它之上。用户将调整此 UIView 的大小,当您准备裁剪时,您只需使用它的尺寸。

标签: ios ios5 uiimage crop


【解决方案1】:

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

这里使用的是 Xcode 9 和 Swift 4

  1. 添加 2 个视图控制器

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

将所有视图链接到源文件

  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
    }
    }
    

类视图控制器: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
        }
    }
}

}

【讨论】:

    【解决方案2】:

    你可以试试BJImageCropper

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      • 1970-01-01
      • 2021-06-21
      • 2015-12-09
      • 1970-01-01
      相关资源
      最近更新 更多