Home > Enterprise >  Resize ImageView by pressing on a circle on it's corner and dragging it to make it smaller/bigg
Resize ImageView by pressing on a circle on it's corner and dragging it to make it smaller/bigg

Time:01-24

I've been doing this small app that contains a UIImageView. I can tap on it and 4 circles on each corner of the image should appear. I have to be able to drag my finger from the corner to resize the image. However the resizing doesn't work. From what I understood - I have to update the constraints of the imageView in the touchesMoved method.

I was using this post as a reference: How to resize UIView by dragging from its edges?

Setup imageView, scrollView and buttons

 struct ResizeRect{
        var topTouch = false
        var leftTouch = false
        var rightTouch = false
        var bottomTouch = false
        var middelTouch = false
    }

This is for my circles on the corners

   private var topLeftCircleLayer: CAShapeLayer!
   private var topRightCircleLayer: CAShapeLayer!
   private var bottomLeftCircleLayer: CAShapeLayer!
   private var bottomRightCircleLayer: CAShapeLayer!

Constraints for imageView

    private var imageViewTopConstraint: NSLayoutConstraint!
    private var imageViewBottomConstraint: NSLayoutConstraint!
    private var imageViewLeadingConstraint: NSLayoutConstraint!
    private var imageViewTrailingConstraint: NSLayoutConstraint!
    private var originalImageFrame: CGRect = .zero
    private var resizeRect = ResizeRect()

Setting up my views

 override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.delegate = self

        setupView()

        addTapGestureRecognizer()
        addPinchGestureRecognizer()
        addRotateButton()
        addDeletePhotoButton()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        borderLayer.path = UIBezierPath(rect: imageView.bounds).cgPath
        addConstraintsForItems()
        createCircles()
    }

    private func addConstraintsForItems() {
        imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 180)
        imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -180)
        imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 70)
        imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -70)

        NSLayoutConstraint.activate([imageViewTopConstraint, imageViewBottomConstraint, imageViewLeadingConstraint, imageViewTrailingConstraint])
   }

    private func setupView() {
        view.addSubview(scrollView)
        view.addSubview(rotateButton)
        view.addSubview(deleteButton)

        scrollView.addSubview(imageView)

        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = UIColor.black.cgColor
        borderLayer.lineWidth = 2
        borderLayer.isHidden = true
        imageView.layer.addSublayer(borderLayer)
    }

Circle creation

     private func updateCircles() {
        let topLeft = CGPoint(x: imageView.frame.minX, y: imageView.frame.minY)
        topLeftCircleLayer.position = topLeft
        let topRight = CGPoint(x: imageView.frame.maxX, y: imageView.frame.minY)
        topRightCircleLayer.position = topRight
        let bottomLeft = CGPoint(x: imageView.frame.minX, y: imageView.frame.maxY)
        bottomLeftCircleLayer.position = bottomLeft
        let bottomRight = CGPoint(x: imageView.frame.maxX, y: imageView.frame.maxY)
        bottomRightCircleLayer.position = bottomRight
        imageView.layer.insertSublayer(topLeftCircleLayer, at: 0)
        imageView.layer.insertSublayer(topRightCircleLayer, at: 1)
        imageView.layer.insertSublayer(bottomLeftCircleLayer, at: 2)
        imageView.layer.insertSublayer(bottomRightCircleLayer, at: 3)
    }

    private func createCircles() {
        topLeftCircleLayer = createCircle(at: CGPoint(x: imageView.frame.minX, y: imageView.frame.minY))
        topRightCircleLayer = createCircle(at: CGPoint(x: imageView.frame.maxX, y: imageView.frame.minY))
        bottomLeftCircleLayer = createCircle(at: CGPoint(x: imageView.frame.minX, y: imageView.frame.maxY))
        bottomRightCircleLayer = createCircle(at: CGPoint(x: imageView.frame.maxX, y: imageView.frame.maxY))
    }

    private func createCircle(at position: CGPoint) -> CAShapeLayer {
        let circle = CAShapeLayer()
        circle.path = UIBezierPath(arcCenter: position, radius: 10, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath
        circle.fillColor = UIColor.systemPink.cgColor
        circle.strokeColor = UIColor.white.cgColor
        circle.lineWidth = 6
        circle.isHidden = !isCirclesVisible
        imageView.layer.addSublayer(circle)
        return circle
    }

And this is the most important part where I try to drag the corner

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first{

            let touchStart = touch.location(in: self.view)
            print(touchStart)

            resizeRect.topTouch = false
            resizeRect.leftTouch = false
            resizeRect.rightTouch = false
            resizeRect.bottomTouch = false

            if touchStart.y > imageView.frame.maxY - proxyFactor &&  touchStart.y < imageView.frame.maxY   proxyFactor {
                resizeRect.bottomTouch = true
                print("bottom")
            }

            if touchStart.x > imageView.frame.maxX - proxyFactor && touchStart.x < imageView.frame.maxX   proxyFactor {
                resizeRect.rightTouch = true
                print("right")
            }

            if touchStart.x > imageView.frame.minX - proxyFactor &&  touchStart.x < imageView.frame.minX   proxyFactor {
                resizeRect.leftTouch = true
                print("left")
            }

            if touchStart.y > imageView.frame.minY - proxyFactor &&  touchStart.y < imageView.frame.minY   proxyFactor {
                resizeRect.topTouch = true
                print("top")
            }

        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first{
            let currentTouchPoint = touch.location(in: self.view)
            let previousTouchPoint = touch.previousLocation(in: self.view)

            let deltaX = currentTouchPoint.x - previousTouchPoint.x
            let deltaY = currentTouchPoint.y - previousTouchPoint.y

            if resizeRect.topTouch && resizeRect.leftTouch {
                if imageViewTopConstraint.constant   deltaY > 0 && imageViewLeadingConstraint.constant   deltaX > 0 {
                    imageViewTopConstraint.constant  = deltaY
                    imageViewLeadingConstraint.constant  = deltaX

                }
            }
            if resizeRect.topTouch && resizeRect.rightTouch {
                if imageViewTopConstraint.constant   deltaY > 0 && imageViewTrailingConstraint.constant - deltaX > 0 {
                    imageViewTopConstraint.constant  = deltaY
                    imageViewTrailingConstraint.constant -= deltaX
                }
            }
            if resizeRect.bottomTouch && resizeRect.leftTouch {
                if imageViewBottomConstraint.constant - deltaY > 0 && imageViewLeadingConstraint.constant   deltaX > 0 {
                    imageViewLeadingConstraint.constant  = deltaX
                    imageViewBottomConstraint.constant -= deltaY
                }
            }
            if resizeRect.bottomTouch && resizeRect.rightTouch {
                if imageViewBottomConstraint.constant - deltaY > 0 && imageViewTrailingConstraint.constant - deltaX > 0 {
                    imageViewTrailingConstraint.constant -= deltaX
                    imageViewBottomConstraint.constant -= deltaY
                }
            }

            UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveEaseIn) {
                self.view.layoutIfNeeded()
            }
        }
    } 

Edit 1: I've updated my code a bit. My app finally understands when I touch the imageView, when I touch a corner it can say which corner was touched but resizing doesn't work properly. Sometimes it works but it does the resizing very slowly. In the console it says that I have this error:

Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600000cf8910 H:|-(65)-[UIImageView:0x13a707300]   (active, names: '|':UIView:0x13c20f220 )>",
    "<NSLayoutConstraint:0x600000ce0640 H:|-(70)-[UIImageView:0x13a707300]   (active, names: '|':UIView:0x13c20f220 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600000ce0640 H:|-(70)-[UIImageView:0x13a707300]   (active, names: '|':UIView:0x13c20f220 )>

I try to update the constraints but for some reason it won't let me. I've cloned the github project from a person that wrote a solution. He made IBOutlets for constraints and from what I understand - constraints created from IBOutlets are some kind different from those that I have. How do I fix the constraint issue? And I would be very grateful if someone could notice what is wrong with my circle creation. Right now I only see 1 below the middle of the image...

CodePudding user response:

Several issues with your approach...

First, none of this:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    borderLayer.path = UIBezierPath(rect: imageView.bounds).cgPath
    addConstraintsForItems()
    createCircles()
}

should be there. viewDidLayoutSubviews() is called more than once, and if you are changing views/constraints it will be called many times.

Second, the you may have had trouble getting that GitHub project working with code setup (as opposed to Storyboard setup with @IBOutlet connections) is because the order of the constraints is reversed.

In your code, you're setting all 4 constraints on your imageView going from imageView -> view. The Storyboard setup can be replicated like this:

// top and leading constraints need to be imageView -> view
imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 180)
imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 70)
    
// trailing and bottom constraints need to be view -> imageView
imageViewTrailingConstraint = view.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 70)
imageViewBottomConstraint = view.bottomAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 180)
    

Third, you are using .frame in some places where you need to use .bounds.

Here's a modified version of your code. I don't know how you are deciding when to show/hide the corner circles, so I added a Double-Tap gesture to activate / deactivate "resizing" mode:

class ResizeViewViewController: UIViewController {

    private var topLeftCircleLayer: CAShapeLayer!
    private var topRightCircleLayer: CAShapeLayer!
    private var bottomLeftCircleLayer: CAShapeLayer!
    private var bottomRightCircleLayer: CAShapeLayer!

    private var borderLayer: CAShapeLayer!

    private var imageViewTopConstraint: NSLayoutConstraint!
    private var imageViewBottomConstraint: NSLayoutConstraint!
    private var imageViewLeadingConstraint: NSLayoutConstraint!
    private var imageViewTrailingConstraint: NSLayoutConstraint!

    private var originalImageFrame: CGRect = .zero
    private var resizeRect = ResizeRect()

    private var proxyFactor: CGFloat = 10.0

    private var imageView = UIImageView()
    
    private var isResizing: Bool = false {
        didSet {
            // update circle positions and
            //  show or hide the circles and border
            updateCircles()
            topLeftCircleLayer.isHidden = !isResizing
            topRightCircleLayer.isHidden = !isResizing
            bottomLeftCircleLayer.isHidden = !isResizing
            bottomRightCircleLayer.isHidden = !isResizing
            borderLayer.isHidden = !isResizing
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        
        setupView()

        addConstraintsForItems()
        
        createCircles()
        
        isResizing = false
        
        // let's use double-tap on the image view to
        //  toggle resizing
        let t = UITapGestureRecognizer(target: self, action: #selector(gotDoubleTap(_:)))
        t.numberOfTapsRequired = 2
        imageView.addGestureRecognizer(t)
        imageView.isUserInteractionEnabled = true

        // an instructions label at the top
        let v = UILabel()
        v.textAlignment = .center
        v.text = "Double-Tap image to toggle resizing mode."
        v.translatesAutoresizingMaskIntoConstraints = false
        // put it under the image view so it doesn't interfere
        view.insertSubview(v, at: 0)
        NSLayoutConstraint.activate([
            v.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
            v.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
        ])
        
    }
    
    @objc func gotDoubleTap(_ sender: Any?) {
        isResizing.toggle()
    }

    private func addConstraintsForItems() {

        // top and leading constraints need to be imageView -> view
        imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 180)
        imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 70)

        // trailing and bottom constraints need to be view -> imageView
        imageViewTrailingConstraint = view.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 70)
        imageViewBottomConstraint = view.bottomAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 180)
        
        NSLayoutConstraint.activate([
            imageViewTopConstraint, imageViewBottomConstraint, imageViewLeadingConstraint, imageViewTrailingConstraint
        ])
    }

    private func setupView() {

        // add the image view
        imageView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(imageView)

        imageView.backgroundColor = .systemYellow

        // let's use the "swift" SF Symbol image
        if let img = UIImage(systemName: "swift") {
            imageView.image = img
            imageView.tintColor = .systemBlue
        }

        borderLayer = CAShapeLayer()
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = UIColor.black.cgColor
        borderLayer.lineWidth = 2
        borderLayer.isHidden = true
        imageView.layer.addSublayer(borderLayer)
        
    }

    private func updateCircles() {
        
        // we need to disable CALayer internal animations when
        //  changing the positions of the layers
        CATransaction.begin()
        CATransaction.setDisableActions(true)

        let topLeft = CGPoint(x: imageView.bounds.minX, y: imageView.bounds.minY)
        topLeftCircleLayer.position = topLeft
        let topRight = CGPoint(x: imageView.bounds.maxX, y: imageView.bounds.minY)
        topRightCircleLayer.position = topRight
        let bottomLeft = CGPoint(x: imageView.bounds.minX, y: imageView.bounds.maxY)
        bottomLeftCircleLayer.position = bottomLeft
        let bottomRight = CGPoint(x: imageView.bounds.maxX, y: imageView.bounds.maxY)
        bottomRightCircleLayer.position = bottomRight
        
        borderLayer.path = UIBezierPath(rect: imageView.bounds).cgPath
        
        CATransaction.commit()
        
    }

    private func createCircles() {
        // no need to pass "positions" here... they will be set when we show / update them
        topLeftCircleLayer = createCircle()
        topRightCircleLayer = createCircle()
        bottomLeftCircleLayer = createCircle()
        bottomRightCircleLayer = createCircle()

        // add the layers here
        imageView.layer.addSublayer(topLeftCircleLayer)
        imageView.layer.addSublayer(topRightCircleLayer)
        imageView.layer.addSublayer(bottomLeftCircleLayer)
        imageView.layer.addSublayer(bottomRightCircleLayer)
    }

    private func createCircle() -> CAShapeLayer {
        let circle = CAShapeLayer()
        circle.path = UIBezierPath(arcCenter: .zero, radius: 10, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath
        circle.fillColor = UIColor.systemPink.cgColor
        circle.strokeColor = UIColor.white.cgColor
        circle.lineWidth = 6
        circle.isHidden = true
        return circle
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        if !isResizing { return() }
        
        if let touch = touches.first{
            
            let touchStart = touch.location(in: self.view)
            print(touchStart, imageView.frame)
            
            resizeRect.topTouch = false
            resizeRect.leftTouch = false
            resizeRect.rightTouch = false
            resizeRect.bottomTouch = false
            
            if touchStart.y > imageView.frame.maxY - proxyFactor &&  touchStart.y < imageView.frame.maxY   proxyFactor {
                resizeRect.bottomTouch = true
                print("bottom")
            }
            
            if touchStart.x > imageView.frame.maxX - proxyFactor && touchStart.x < imageView.frame.maxX   proxyFactor {
                resizeRect.rightTouch = true
                print("right")
            }
            
            if touchStart.x > imageView.frame.minX - proxyFactor &&  touchStart.x < imageView.frame.minX   proxyFactor {
                resizeRect.leftTouch = true
                print("left")
            }
            
            if touchStart.y > imageView.frame.minY - proxyFactor &&  touchStart.y < imageView.frame.minY   proxyFactor {
                resizeRect.topTouch = true
                print("top")
            }

        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        if !isResizing { return() }

        if let touch = touches.first{
            let currentTouchPoint = touch.location(in: self.view)
            let previousTouchPoint = touch.previousLocation(in: self.view)
            
            let deltaX = currentTouchPoint.x - previousTouchPoint.x
            let deltaY = currentTouchPoint.y - previousTouchPoint.y
            
            if resizeRect.topTouch {
                imageViewTopConstraint.constant  = deltaY
            }
            if resizeRect.leftTouch {
                imageViewLeadingConstraint.constant  = deltaX
            }
            if resizeRect.rightTouch {
                imageViewTrailingConstraint.constant -= deltaX
            }
            if resizeRect.bottomTouch {
                imageViewBottomConstraint.constant -= deltaY
            }

            // don't know why you would want to animate this?
            //UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveEaseIn, animations: {
            //  self.view.layoutIfNeeded()
            //}, completion: { (ended) in
            //
            //})

            // note: this can "lag" and not precisely match the image view frame
            
            self.updateCircles()
            
            //  we can dispatch it async to allow UIKit to set the
            //  image view's frame before we move the layers
            //  but we still get a little "lag"
            DispatchQueue.main.async {
                self.updateCircles()
            }

        }

    }

}

struct ResizeRect{
    var topTouch = false
    var leftTouch = false
    var rightTouch = false
    var bottomTouch = false
    var middelTouch = false
}

When you run that, you'll notice that there is some "lag" when dragging and resizing - see the comments in the code.

A similar approach that might find it more responsive, as well as easier to manage...

Instead of adding "circle layers" we'll add "circle views" as subviews of the image view. We can then set constraints to the image view corners, and let auto-layout handle all of the positioning.

Less code... takes advantage of auto-layout... more responsive...

Run the above version first, then give this one a try:

class CircleView: UIView {
    
    // this allows us to use the "base" layer as a shape layer
    //  instead of adding a sublayer
    lazy var shapeLayer: CAShapeLayer = self.layer as! CAShapeLayer
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        shapeLayer.fillColor = UIColor.systemPink.cgColor
        shapeLayer.strokeColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 6
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.path = UIBezierPath(ovalIn: bounds).cgPath
    }

}

class ResizeViewViewController: UIViewController {
    
    private var topLeftCircleView = CircleView()
    private var topRightCircleView = CircleView()
    private var bottomLeftCircleView = CircleView()
    private var bottomRightCircleView = CircleView()
    
    private var borderView = UIView()
    
    private var imageViewTopConstraint: NSLayoutConstraint!
    private var imageViewBottomConstraint: NSLayoutConstraint!
    private var imageViewLeadingConstraint: NSLayoutConstraint!
    private var imageViewTrailingConstraint: NSLayoutConstraint!
    
    private var originalImageFrame: CGRect = .zero
    private var resizeRect = ResizeRect()
    
    private var proxyFactor: CGFloat = 10.0
    
    private var imageView = UIImageView()
    
    private var isResizing: Bool = false {
        didSet {
            // show or hide the circles and image view border
            [topLeftCircleView, topRightCircleView, bottomLeftCircleView, bottomRightCircleView].forEach { v in
                v.isHidden = !isResizing
            }
            borderView.layer.borderWidth = isResizing ? 2.0 : 0.0
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        
        setupView()
        
        addConstraintsForItems()
        
        createCircles()
        
        isResizing = false

        // let's use double-tap on the image view to
        //  toggle resizing
        let t = UITapGestureRecognizer(target: self, action: #selector(gotDoubleTap(_:)))
        t.numberOfTapsRequired = 2
        imageView.addGestureRecognizer(t)
        imageView.isUserInteractionEnabled = true

        // an instructions label at the top
        let v = UILabel()
        v.textAlignment = .center
        v.text = "Double-Tap image to toggle resizing mode."
        v.font = .systemFont(ofSize: 15.0, weight: .light)
        v.translatesAutoresizingMaskIntoConstraints = false
        // put it under the image view so it doesn't interfere
        view.insertSubview(v, at: 0)
        NSLayoutConstraint.activate([
            v.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
            v.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
        ])
        
    }
    
    @objc func gotDoubleTap(_ sender: Any?) {
        isResizing.toggle()
    }
    
    private func addConstraintsForItems() {
        
        // top and leading constraints need to be imageView -> view
        imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 180)
        imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 70)
        
        // trailing and bottom constraints need to be view -> imageView
        imageViewTrailingConstraint = view.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 70)
        imageViewBottomConstraint = view.bottomAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 180)
        
        NSLayoutConstraint.activate([
            imageViewTopConstraint, imageViewBottomConstraint, imageViewLeadingConstraint, imageViewTrailingConstraint,

            // constrain all 4 sides of the borderView to the imageView
            borderView.topAnchor.constraint(equalTo: imageView.topAnchor),
            borderView.leadingAnchor.constraint(equalTo: imageView.leadingAnchor),
            borderView.trailingAnchor.constraint(equalTo: imageView.trailingAnchor),
            borderView.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),
        ])
    }
    
    private func setupView() {

        // add the image view
        imageView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(imageView)

        imageView.backgroundColor = .systemYellow

        // add the borderView to the image view
        //  we'd like to just use the image view's layer border, but
        //  setting its border width draws on top of the circle subviews
        borderView.translatesAutoresizingMaskIntoConstraints = false
        imageView.addSubview(borderView)
        
        // let's use the "swift" SF Symbol image
        if let img = UIImage(systemName: "swift") {
            imageView.image = img
            imageView.tintColor = .systemBlue
        }
        
        // border view layer border starts at width: 0 (not showing)
        borderView.layer.borderColor = UIColor.black.cgColor
        borderView.layer.borderWidth = 0
        
    }
    

    private func createCircles() {

        // add the circle views to the image view
        //  constraining width: 20 and height = width
        [topLeftCircleView, topRightCircleView, bottomLeftCircleView, bottomRightCircleView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            v.widthAnchor.constraint(equalToConstant: 20.0).isActive = true
            v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
            // they start hidden
            v.isHidden = true
            imageView.addSubview(v)
        }
        
        // constrain circleViews to the image view corners
        NSLayoutConstraint.activate([
            topLeftCircleView.centerXAnchor.constraint(equalTo: imageView.leadingAnchor),
            topLeftCircleView.centerYAnchor.constraint(equalTo: imageView.topAnchor),
            
            topRightCircleView.centerXAnchor.constraint(equalTo: imageView.trailingAnchor),
            topRightCircleView.centerYAnchor.constraint(equalTo: imageView.topAnchor),
            
            bottomLeftCircleView.centerXAnchor.constraint(equalTo: imageView.leadingAnchor),
            bottomLeftCircleView.centerYAnchor.constraint(equalTo: imageView.bottomAnchor),
            
            bottomRightCircleView.centerXAnchor.constraint(equalTo: imageView.trailingAnchor),
            bottomRightCircleView.centerYAnchor.constraint(equalTo: imageView.bottomAnchor),
        ])

    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        // only want to drag-resize if the corner circles are showing
        if !isResizing { return() }
        
        if let touch = touches.first{
            
            let touchStart = touch.location(in: self.view)
            print(touchStart, imageView.frame)
            
            resizeRect.topTouch = false
            resizeRect.leftTouch = false
            resizeRect.rightTouch = false
            resizeRect.bottomTouch = false
            
            if touchStart.y > imageView.frame.maxY - proxyFactor &&  touchStart.y < imageView.frame.maxY   proxyFactor {
                resizeRect.bottomTouch = true
                print("bottom")
            }
            
            if touchStart.x > imageView.frame.maxX - proxyFactor && touchStart.x < imageView.frame.maxX   proxyFactor {
                resizeRect.rightTouch = true
                print("right")
            }
            
            if touchStart.x > imageView.frame.minX - proxyFactor &&  touchStart.x < imageView.frame.minX   proxyFactor {
                resizeRect.leftTouch = true
                print("left")
            }
            
            if touchStart.y > imageView.frame.minY - proxyFactor &&  touchStart.y < imageView.frame.minY   proxyFactor {
                resizeRect.topTouch = true
                print("top")
            }
            
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        // only want to drag-resize if the corner circles are showing
        if !isResizing { return() }
        
        if let touch = touches.first{
            let currentTouchPoint = touch.location(in: self.view)
            let previousTouchPoint = touch.previousLocation(in: self.view)
            
            let deltaX = currentTouchPoint.x - previousTouchPoint.x
            let deltaY = currentTouchPoint.y - previousTouchPoint.y
            
            if resizeRect.topTouch {
                imageViewTopConstraint.constant  = deltaY
            }
            if resizeRect.leftTouch {
                imageViewLeadingConstraint.constant  = deltaX
            }
            if resizeRect.rightTouch {
                imageViewTrailingConstraint.constant -= deltaX
            }
            if resizeRect.bottomTouch {
                imageViewBottomConstraint.constant -= deltaY
            }
            
            // don't know why you would want to animate this?
            //UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveEaseIn, animations: {
            //  self.view.layoutIfNeeded()
            //}, completion: { (ended) in
            //
            //})

        }
        
    }
    
}

struct ResizeRect{
    var topTouch = false
    var leftTouch = false
    var rightTouch = false
    var bottomTouch = false
    var middelTouch = false
}
  • Related