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:
And when user moves for example the bottom-right corner by dragging everything is working fine:
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...
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
}
}