如何在iOS中的UIImageView中裁剪圆圈内的图像 [英] How to crop image inside the circle in UIImageView in iOS

查看:102
本文介绍了如何在iOS中的UIImageView中裁剪圆圈内的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,我有一个 UIImageView 显示主图像,另一个 UIImageView 用作面具它显示了一个透明且在其不透明之外的圆圈,可以使用 UIPanGestureRecognizer 移动此圆圈,我想知道将圆圈内的图像裁剪成新的方法图片。这是附加的代码和屏幕截图

I have an app where I have a UIImageView which displays main image and another UIImageView being used as a mask which shows a circle which is transparent and outside its opaque, this circle can be moved using a UIPanGestureRecognizer, I want to know a wayout to crop the image inside the circle into a new image. Here is the attached code and screen shot

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // create pan gesture

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(handlePan:)];
    [self.view addGestureRecognizer:pan];


    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = [[self makeCircleAtLocation:self.view.center radius:100.0] CGPath];
    shapeLayer.strokeColor = [[UIColor clearColor] CGColor];
    shapeLayer.fillColor = nil;
    shapeLayer.lineWidth = 3.0;

    // Add CAShapeLayer to our view

    [self.view.layer addSublayer:shapeLayer];

    // Save this shape layer in a class property for future reference,
    // namely so we can remove it later if we tap elsewhere on the screen.

    self.circleLayer = shapeLayer;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
// Create a UIBezierPath which is a circle at a certain location of a certain radius.
// This also saves the circle's center and radius to class properties for future reference.

- (UIBezierPath *)makeCircleAtLocation:(CGPoint)location radius:(CGFloat)radius
{
    self.circleCenter = location;
    self.circleRadius = radius;

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path addArcWithCenter:self.circleCenter
                    radius:self.circleRadius
                startAngle:0.0
                  endAngle:M_PI * 2.0
                 clockwise:YES];

    return path;
}

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static CGPoint oldCenter;

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        // If we're starting a pan, make sure we're inside the circle.
        // So, calculate the distance between the circle's center and
        // the gesture start location and we'll compare that to the
        // radius of the circle.

        CGPoint location = [gesture locationInView:gesture.view];
        CGPoint translation = [gesture translationInView:gesture.view];
        location.x -= translation.x;
        location.y -= translation.y;

        CGFloat x = location.x - self.circleCenter.x;
        CGFloat y = location.y - self.circleCenter.y;
        CGFloat distance = sqrtf(x*x + y*y);

        // If we're outside the circle, cancel the gesture.
        // If we're inside it, keep track of where the circle was.


        oldCenter = self.circleCenter;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        // Let's calculate the new center of the circle by adding the
        // the translationInView to the old circle center.

        CGPoint translation = [gesture translationInView:gesture.view];
        CGPoint newCenter = CGPointMake(oldCenter.x + translation.x, oldCenter.y + translation.y);

      //  CGPoint newCenter = [gesture locationInView:self.view];
        if (newCenter.x < 160) {
            newCenter.x = 160;
        }
        else if (newCenter.x > self.view.frame.size.width - 160) {
            newCenter.x = self.view.frame.size.width - 160;
        }
        if (newCenter.y < 242) {
            newCenter.y = 242;
        }
        else if (newCenter.y > self.view.frame.size.height - imageMain.center.y) {
            newCenter.y = self.view.frame.size.height - imageMain.center.y;
        }

        // Update the path for our CAShapeLayer

       // self.circleLayer.path = [[self makeCircleAtLocation:newCenter radius:self.circleRadius] CGPath];
        imageCircle.center = newCenter;

    }
}

@end

结果是

推荐答案

要保存蒙面图像,在iOS 7中你要使用 drawViewHierarchyInRect ,在早期版本的iOS中,您将使用 renderInContext 。您可能还想使用 CGImageCreateWithImageInRect 裁剪图像。请参阅下面的 handleTap 作为示例。

To save the masked image, in iOS 7 you'd use drawViewHierarchyInRect, and in earlier versions of iOS, you'd use renderInContext. You might also want to crop the image with CGImageCreateWithImageInRect. See my handleTap below for an example.

但我注意到您显然是通过覆盖图像来屏蔽。我建议使用 UIBezierPath 作为图像视图的图层蒙版的基础,以及 CAShapeLayer 你会用来绘制圆圈(假设你画圆圈时想要一个边框。如果你的面具是一个规则的形状(如圆圈),它可能更灵活,使它成为 CAShapeLayer 带有 UIBezierPath (而不是图像),因为这样你不仅可以用平移手势移动它,还可以缩放它,用捏手势:

But I note that you are apparently masking by overlaying an image. I might suggest using a UIBezierPath for the basis of both a layer mask for the image view, as well as the CAShapeLayer you'll use to draw the circle (assuming you want a border as you draw the circle. If your mask is a regular shape (such as a circle), it's probably more flexible to make it a CAShapeLayer with a UIBezierPath (rather than an image), because that way you can not only move it around with a pan gesture, but also scale it, too, with a pinch gesture:

< img src =https://i.stack.imgur.com/ss7IP.gifalt =在此处输入图像说明>

这是示例实现:

//  ViewController.swift
//  CircleMaskDemo
//
//  Created by Robert Ryan on 4/18/18.
//  Copyright © 2018 Robert Ryan. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var pinch: UIPinchGestureRecognizer!
    @IBOutlet weak var pan: UIPanGestureRecognizer!

    private let maskLayer = CAShapeLayer()

    private lazy var radius: CGFloat = min(view.bounds.width, view.bounds.height) * 0.3
    private lazy var center: CGPoint = CGPoint(x: view.bounds.midX, y: view.bounds.midY)

    private let pathLayer: CAShapeLayer = {
        let _pathLayer = CAShapeLayer()
        _pathLayer.fillColor = UIColor.clear.cgColor
        _pathLayer.strokeColor = UIColor.black.cgColor
        _pathLayer.lineWidth = 3
        return _pathLayer
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.layer.addSublayer(pathLayer)
        imageView.layer.mask = maskLayer

        updateCirclePath(at: center, radius: radius)

        // imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(pinch)
        imageView.addGestureRecognizer(pan)
    }

    private var oldCenter: CGPoint!
    private var oldRadius: CGFloat!

    @IBAction func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        let scale = gesture.scale

        if gesture.state == .began { oldRadius = radius }

        updateCirclePath(at: center, radius: oldRadius * scale)
    }

    @IBAction func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: gesture.view)

        if gesture.state == .began { oldCenter = center }

        let newCenter = CGPoint(x: oldCenter.x + translation.x, y: oldCenter.y + translation.y)

        updateCirclePath(at: newCenter, radius: radius)
    }

    @IBAction func handleTap(_ gesture: UITapGestureRecognizer) {
        let fileURL = try! FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("image.png")

        let scale  = imageView.window!.screen.scale
        let radius = self.radius * scale
        let center = CGPoint(x: self.center.x * scale, y: self.center.y * scale)

        let frame = CGRect(x: center.x - radius,
                           y: center.y - radius,
                           width: radius * 2.0,
                           height: radius * 2.0)

        // temporarily remove the circleLayer

        let saveLayer = pathLayer
        saveLayer.removeFromSuperlayer()

        // render the clipped image

        UIGraphicsBeginImageContextWithOptions(imageView.frame.size, false, 0)
        imageView.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)

        // capture the image and close the context

        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        // add the circleLayer back

        imageView.layer.addSublayer(saveLayer)

        // crop the image

        let imageRef = image.cgImage!.cropping(to: frame)!
        let cropped = UIImage(cgImage: imageRef)

        // save the image

        let data = UIImagePNGRepresentation(cropped)!
        try? data.write(to: fileURL)

        // tell the user we're done

        let alert = UIAlertController(title: nil, message: "Saved", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }

    private func updateCirclePath(at center: CGPoint, radius: CGFloat) {
        self.center = center
        self.radius = radius

        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
        maskLayer.path = path.cgPath
        pathLayer.path = path.cgPath
    }
}

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return (gestureRecognizer == pinch && otherGestureRecognizer == pan) ||
            (gestureRecognizer == pan && otherGestureRecognizer == pinch)
    }
}

如果您不想在圆圈周围绘制边框,那么它就更容易了,因为您可以提取与 circleLayer 相关的任何内容。

If you don't want to draw the border around the circle, then it's even easier, as you can pull anything related to circleLayer.

如果您对Objective-C示例感兴趣,请参阅此答案的先前修订版

If you're interested in Objective-C example, see previous revision of this answer.

这篇关于如何在iOS中的UIImageView中裁剪圆圈内的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆