Subviews are not moving with parent UIView

245 Views Asked by At

I'm trying to make an app based on image editing and I'm founding some problems coding a resizing by corners dragging crop area.

I have croppingView.swift where the crop rectangle is drawn when launched:

override func viewDidAppear(_ animated: Bool) {
        let imagePoint = CGPoint(x: contentView.center.x, y: contentView.center.y)

        let imageSize = CGSize(width: cropImageView.image!.size.width, height: cropImageView.image!.size.height)
        let widthScale = cropImageView.frame.width / imageSize.width
        let heightScale = cropImageView.frame.height / imageSize.height
        let scale = min(widthScale, heightScale)
        let width = imageSize.width * scale
        let height = imageSize.height * scale
        let size = CGSize(width: width, height: height)

        let originViewFrame = CGPoint(x: imagePoint.x - width/2.0, y: imagePoint.y - height/2.0)

        let cropSize = CGSize(width: width * 0.7, height: height * 0.7)
        let cropViewFrame = CGRect(origin: originViewFrame, size: cropSize)

        cropView = ShapeView(frame: cropViewFrame)
        cropView.backgroundColor = UIColor(red: 224.0/255.0, green: 224.0/255.0, blue: 224.0/255.0, alpha: 0.3)
        cropView.layer.borderColor = UIColor(red: 97.0/255.0, green: 97.0/255.0, blue: 97.0/255.0, alpha: 1.0).cgColor
        cropView.layer.borderWidth = 1.0
        cropView.center = imagePoint
        self.view.addSubview(cropView)
}

The ShapeView.swift to draw the cropping area is as follows:

class ShapeView: UIView {

    var previousLocation = CGPoint.zero

    var topLeft = DragHandle(fillColor:UIColor(red: 0.0/255.0, green: 150.0/255.0, blue: 136.0/255.0, alpha: 1.0),
                             strokeColor: UIColor.white)
    var topRight = DragHandle(fillColor:UIColor(red: 0.0/255.0, green: 150.0/255.0, blue: 136.0/255.0, alpha: 1.0),
                              strokeColor: UIColor.white)
    var bottomLeft = DragHandle(fillColor:UIColor(red: 0.0/255.0, green: 150.0/255.0, blue: 136.0/255.0, alpha: 1.0),
                                strokeColor: UIColor.white)
    var bottomRight = DragHandle(fillColor:UIColor(red: 0.0/255.0, green: 150.0/255.0, blue: 136.0/255.0, alpha: 1.0),
                                 strokeColor: UIColor.white)

    override func didMoveToSuperview() {
        superview?.addSubview(topLeft)
        superview?.addSubview(topRight)
        superview?.addSubview(bottomLeft)
        superview?.addSubview(bottomRight)

        var pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
        topLeft.addGestureRecognizer(pan)
        pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
        topRight.addGestureRecognizer(pan)
        pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
        bottomLeft.addGestureRecognizer(pan)
        pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
        bottomRight.addGestureRecognizer(pan)
        pan = UIPanGestureRecognizer(target: self, action: #selector(handleMove))
        self.addGestureRecognizer(pan)

        self.updateDragHandles()
    }

    func updateDragHandles() {
        topLeft.center = self.transformedTopLeft()
        topRight.center = self.transformedTopRight()
        bottomLeft.center = self.transformedBottomLeft()
        bottomRight.center = self.transformedBottomRight()
    }

    //Gesture Methods
    @IBAction func handleMove(gesture:UIPanGestureRecognizer) {
        let translation = gesture.translation(in: self.superview!)
        var center = self.center
        center.x += translation.x
        center.y += translation.y
        self.center = center
        gesture.setTranslation(CGPoint.zero, in: self.superview!)
        updateDragHandles()
    }

    @IBAction func handlePan(gesture:UIPanGestureRecognizer) {
        let translation = gesture.translation(in: self)
        switch gesture.view! {
        case topLeft:
            if gesture.state == .began {
                self.setAnchorPoint(anchorPoint: CGPoint(x: 1, y: 1))
            }
            self.bounds.size.width -= translation.x
            self.bounds.size.height -= translation.y
        case topRight:
            if gesture.state == .began {
                self.setAnchorPoint(anchorPoint: CGPoint(x: 0, y: 1))
            }
            self.bounds.size.width += translation.x
            self.bounds.size.height -= translation.y

        case bottomLeft:
            if gesture.state == .began {
                self.setAnchorPoint(anchorPoint: CGPoint(x: 1, y: 0))
            }
            self.bounds.size.width -= translation.x
            self.bounds.size.height += translation.y
        case bottomRight:
            if gesture.state == .began {
                self.setAnchorPoint(anchorPoint: CGPoint.zero)
            }
            self.bounds.size.width += translation.x
            self.bounds.size.height += translation.y
        default:()
        }

        gesture.setTranslation(CGPoint.zero, in: self)
        updateDragHandles()
        if gesture.state == .ended {
            self.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 0.5))
        }
    }
}

As you can see I'm adding one circle in each corner of the rectangle. These circles are used to resize the cropping area by dragging. Here the key is the updateDragHandles()function which updates the positions of all other circle keeping them on the corners.

So when launching the cropping area appears like:

enter image description here

And when user moves for example the bottom-right corner by dragging everything is working fine:

enter image description here

Now the problem is when I click on the portrait button to change cropping area orientation. With the next code into croppingView.swift the grey rectangle correctly changes the size but the circles remain on the their position:

@IBAction func portButton(_ sender: Any) {
        portButton.tintColor = UIColor.systemBlue
        landButton.tintColor = UIColor.systemGray
        isPortait = 1
        let inWidth = cropView.frame.size.width
        let inHeight = cropView.frame.size.height
        if inWidth > inHeight {
            let scaleW = cropView.frame.size.height / cropView.frame.size.width
            let scaleH = cropView.frame.size.width / cropView.frame.size.height
            let fixPoint = CGPoint(x: 0.5, y: 0.5)
            cropView.setAnchorPoint(anchorPoint: fixPoint)
            cropView.transform = cropView.transform.scaledBy(x: scaleW, y: scaleH)
            let vc = ShapeView()
            vc.updateDragHandles()
        }
    }

It's like updateDragHandles is not working from this ViewController...

enter image description here

Probably it's a basic mistake but I can't find out the solution.

Any suggestion?

Thank you in advance!

DragHandle.swift

let diameter:CGFloat = 30

import UIKit

class DragHandle: UIView {

  var fillColor = UIColor.darkGray
  var strokeColor = UIColor.lightGray
  var strokeWidth: CGFloat = 1.0

  required init(coder aDecoder: NSCoder) {
    fatalError("Use init(fillColor:, strokeColor:)")
  }

  init(fillColor: UIColor, strokeColor: UIColor, strokeWidth width: CGFloat = 1.0) {
    super.init(frame: CGRect(x: 0, y: 0, width: diameter, height: diameter))
    self.fillColor = fillColor
    self.strokeColor = strokeColor
    self.strokeWidth = width
    self.backgroundColor = UIColor.clear
  }

    override func draw(_ rect: CGRect)
    {
        super.draw(rect)
        let handlePath = UIBezierPath(ovalIn: rect.insetBy(dx: 10 + strokeWidth, dy: 10 + strokeWidth))
        fillColor.setFill()
        handlePath.fill()
        strokeColor.setStroke()
        handlePath.lineWidth = strokeWidth
        handlePath.stroke()
    }
}

UIViewExtensions.swift

import Foundation
import UIKit

extension UIView {
    func offsetPointToParentCoordinates(point: CGPoint) -> CGPoint {
      return CGPoint(x: point.x + self.center.x, y: point.y + self.center.y)
    }

    func pointInViewCenterTerms(point:CGPoint) -> CGPoint {
      return CGPoint(x: point.x - self.center.x, y: point.y - self.center.y)
    }

    func pointInTransformedView(point: CGPoint) -> CGPoint {
      let offsetItem = self.pointInViewCenterTerms(point: point)
      let updatedItem = offsetItem.applying(self.transform)
      let finalItem = self.offsetPointToParentCoordinates(point: updatedItem)
      return finalItem
    }

    func originalFrame() -> CGRect {
      let currentTransform = self.transform
      self.transform = .identity
      let originalFrame = self.frame
      self.transform = currentTransform
      return originalFrame
    }

    func transformedTopLeft() -> CGPoint {
      let frame = self.originalFrame()
      let point = frame.origin
      return self.pointInTransformedView(point: point)
    }

    func transformedTopRight() -> CGPoint {
      let frame = self.originalFrame()
      var point = frame.origin
      point.x += frame.size.width
      return self.pointInTransformedView(point: point)
    }

    func transformedBottomRight() -> CGPoint {
      let frame = self.originalFrame()
      var point = frame.origin
      point.x += frame.size.width
      point.y += frame.size.height
      return self.pointInTransformedView(point: point)
    }

    func transformedBottomLeft() -> CGPoint {
      let frame = self.originalFrame()
      var point = frame.origin
      point.y += frame.size.height
      return self.pointInTransformedView(point: point)
    }

    func setAnchorPoint(anchorPoint: CGPoint) {
      var newPoint = CGPoint(x: self.bounds.size.width * anchorPoint.x, y: self.bounds.size.height * anchorPoint.y)
      var oldPoint = CGPoint(x: self.bounds.size.width * self.layer.anchorPoint.x, y: self.bounds.size.height * self.layer.anchorPoint.y)

      newPoint = newPoint.applying(self.transform)
      oldPoint = oldPoint.applying(self.transform)

      var position = self.layer.position
      position.x -= oldPoint.x
      position.x += newPoint.x
      position.y -= oldPoint.y
      position.y += newPoint.y

      self.layer.position = position
      self.layer.anchorPoint = anchorPoint
    }
}
0

There are 0 best solutions below