diff --git a/Classes/WDCropBorderView.swift b/Classes/WDCropBorderView.swift index ef985c1..3425b78 100644 --- a/Classes/WDCropBorderView.swift +++ b/Classes/WDCropBorderView.swift @@ -9,61 +9,60 @@ import UIKit internal class WDCropBorderView: UIView { - private let kNumberOfBorderHandles: CGFloat = 8 - private let kHandleDiameter: CGFloat = 24 - - override init(frame: CGRect) { - super.init(frame: frame) - - self.backgroundColor = UIColor.clearColor() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - - self.backgroundColor = UIColor.clearColor() - } - - override func drawRect(rect: CGRect) { - let context = UIGraphicsGetCurrentContext() - - CGContextSetStrokeColorWithColor(context, - UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).CGColor) - CGContextSetLineWidth(context, 1.5) - CGContextAddRect(context, CGRectMake(kHandleDiameter / 2, kHandleDiameter / 2, - rect.size.width - kHandleDiameter, rect.size.height - kHandleDiameter)) - CGContextStrokePath(context) - - CGContextSetRGBFillColor(context, 1, 1, 1, 0.95) - for handleRect in calculateAllNeededHandleRects() { - CGContextFillEllipseInRect(context, handleRect) - } - } - - private func calculateAllNeededHandleRects() -> [CGRect] { - - let width = self.frame.width - let height = self.frame.height - - let leftColX: CGFloat = 0 - let rightColX = width - kHandleDiameter - let centerColX = rightColX / 2 - - let topRowY: CGFloat = 0 - let bottomRowY = height - kHandleDiameter - let middleRowY = bottomRowY / 2 - - //starting with the upper left corner and then following clockwise - let topLeft = CGRectMake(leftColX, topRowY, kHandleDiameter, kHandleDiameter) - let topCenter = CGRectMake(centerColX, topRowY, kHandleDiameter, kHandleDiameter) - let topRight = CGRectMake(rightColX, topRowY, kHandleDiameter, kHandleDiameter) - let middleRight = CGRectMake(rightColX, middleRowY, kHandleDiameter, kHandleDiameter) - let bottomRight = CGRectMake(rightColX, bottomRowY, kHandleDiameter, kHandleDiameter) - let bottomCenter = CGRectMake(centerColX, bottomRowY, kHandleDiameter, kHandleDiameter) - let bottomLeft = CGRectMake(leftColX, bottomRowY, kHandleDiameter, kHandleDiameter) - let middleLeft = CGRectMake(leftColX, middleRowY, kHandleDiameter, kHandleDiameter) - - return [topLeft, topCenter, topRight, middleRight, bottomRight, bottomCenter, bottomLeft, - middleLeft] - } -} \ No newline at end of file + private let kNumberOfBorderHandles: CGFloat = 8 + private let kHandleDiameter: CGFloat = 24 + + override init(frame: CGRect) { + super.init(frame: frame) + + self.backgroundColor = UIColor.clear + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + self.backgroundColor = UIColor.clear + } + + override func draw(_ rect: CGRect) { + if let context = UIGraphicsGetCurrentContext() { + context.setStrokeColor(UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor) + context.setLineWidth(1.5) + context.addRect(CGRect(x: kHandleDiameter / 2, y: kHandleDiameter / 2, + width: rect.size.width - kHandleDiameter, height: rect.size.height - kHandleDiameter)) + context.strokePath() + + context.setFillColor(red: 1, green: 1, blue: 1, alpha: 0.95) + for handleRect in calculateAllNeededHandleRects() { + context.fillEllipse(in: handleRect) + } + } + } + + private func calculateAllNeededHandleRects() -> [CGRect] { + + let width = self.frame.width + let height = self.frame.height + + let leftColX: CGFloat = 0 + let rightColX = width - kHandleDiameter + let centerColX = rightColX / 2 + + let topRowY: CGFloat = 0 + let bottomRowY = height - kHandleDiameter + let middleRowY = bottomRowY / 2 + + //starting with the upper left corner and then following clockwise + let topLeft = CGRect(x: leftColX, y: topRowY, width: kHandleDiameter, height: kHandleDiameter) + let topCenter = CGRect(x: centerColX, y: topRowY, width: kHandleDiameter, height: kHandleDiameter) + let topRight = CGRect(x: rightColX, y: topRowY, width: kHandleDiameter, height: kHandleDiameter) + let middleRight = CGRect(x: rightColX, y: middleRowY, width: kHandleDiameter, height: kHandleDiameter) + let bottomRight = CGRect(x: rightColX, y: bottomRowY, width: kHandleDiameter, height: kHandleDiameter) + let bottomCenter = CGRect(x: centerColX, y: bottomRowY, width: kHandleDiameter, height: kHandleDiameter) + let bottomLeft = CGRect(x: leftColX, y: bottomRowY, width: kHandleDiameter, height: kHandleDiameter) + let middleLeft = CGRect(x: leftColX, y: middleRowY, width: kHandleDiameter, height: kHandleDiameter) + + return [topLeft, topCenter, topRight, middleRight, bottomRight, bottomCenter, bottomLeft, + middleLeft] + } +} diff --git a/Classes/WDImageCropOverlayView.swift b/Classes/WDImageCropOverlayView.swift index eebf0f2..cd07b56 100644 --- a/Classes/WDImageCropOverlayView.swift +++ b/Classes/WDImageCropOverlayView.swift @@ -9,45 +9,45 @@ import UIKit internal class WDImageCropOverlayView: UIView { - - var cropSize: CGSize! - var toolbar: UIToolbar! - - override init(frame: CGRect) { - super.init(frame: frame) - - self.backgroundColor = UIColor.clearColor() - self.userInteractionEnabled = true - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - - self.backgroundColor = UIColor.clearColor() - self.userInteractionEnabled = true - } - - override func drawRect(rect: CGRect) { - - let toolbarSize = CGFloat(UIDevice.currentDevice().userInterfaceIdiom == .Pad ? 0 : 54) - - let width = CGRectGetWidth(self.frame) - let height = CGRectGetHeight(self.frame) - toolbarSize - - let heightSpan = floor(height / 2 - self.cropSize.height / 2) - let widthSpan = floor(width / 2 - self.cropSize.width / 2) - - // fill outer rect - UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).set() - UIRectFill(self.bounds) - - // fill inner border - UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).set() - UIRectFrame(CGRectMake(widthSpan - 2, heightSpan - 2, self.cropSize.width + 4, - self.cropSize.height + 4)) - - // fill inner rect - UIColor.clearColor().set() - UIRectFill(CGRectMake(widthSpan, heightSpan, self.cropSize.width, self.cropSize.height)) - } + + var cropSize: CGSize! + var toolbar: UIToolbar! + + override init(frame: CGRect) { + super.init(frame: frame) + + self.backgroundColor = UIColor.clear + self.isUserInteractionEnabled = true + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + self.backgroundColor = UIColor.clear + self.isUserInteractionEnabled = true + } + + override func draw(_ rect: CGRect) { + + let toolbarSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 0 : 54) + + let width = self.frame.width + let height = self.frame.height - toolbarSize + + let heightSpan = floor(height / 2 - self.cropSize.height / 2) + let widthSpan = floor(width / 2 - self.cropSize.width / 2) + + // fill outer rect + UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).set() + UIRectFill(self.bounds) + + // fill inner border + UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).set() + UIRectFrame(CGRect(x: widthSpan - 2, y: heightSpan - 2, width: self.cropSize.width + 4, + height: self.cropSize.height + 4)) + + // fill inner rect + UIColor.clear.set() + UIRectFill(CGRect(x: widthSpan, y: heightSpan, width: self.cropSize.width, height: self.cropSize.height)) + } } diff --git a/Classes/WDImageCropView.swift b/Classes/WDImageCropView.swift index 7158dfe..2ccd527 100644 --- a/Classes/WDImageCropView.swift +++ b/Classes/WDImageCropView.swift @@ -10,250 +10,246 @@ import UIKit import QuartzCore private class ScrollView: UIScrollView { - private override func layoutSubviews() { - super.layoutSubviews() - - if let zoomView = self.delegate?.viewForZoomingInScrollView?(self) { - let boundsSize = self.bounds.size - var frameToCenter = zoomView.frame - - // center horizontally - if frameToCenter.size.width < boundsSize.width { - frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2 - } else { - frameToCenter.origin.x = 0 - } - - // center vertically - if frameToCenter.size.height < boundsSize.height { - frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2 - } else { - frameToCenter.origin.y = 0 - } - - zoomView.frame = frameToCenter - } - } + fileprivate override func layoutSubviews() { + super.layoutSubviews() + + if let zoomView = self.delegate?.viewForZooming?(in: self) { + let boundsSize = self.bounds.size + var frameToCenter = zoomView.frame + + // center horizontally + if frameToCenter.size.width < boundsSize.width { + frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2 + } else { + frameToCenter.origin.x = 0 + } + + // center vertically + if frameToCenter.size.height < boundsSize.height { + frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2 + } else { + frameToCenter.origin.y = 0 + } + + zoomView.frame = frameToCenter + } + } } internal class WDImageCropView: UIView, UIScrollViewDelegate { - var resizableCropArea = false - - private var scrollView: UIScrollView! - private var imageView: UIImageView! - private var cropOverlayView: WDImageCropOverlayView! - private var xOffset: CGFloat! - private var yOffset: CGFloat! - - private static func scaleRect(rect: CGRect, scale: CGFloat) -> CGRect { - return CGRectMake( - rect.origin.x * scale, - rect.origin.y * scale, - rect.size.width * scale, - rect.size.height * scale) - } - - var imageToCrop: UIImage? { - get { - return self.imageView.image - } - set { - self.imageView.image = newValue - } - } - - var cropSize: CGSize { - get { - return self.cropOverlayView.cropSize - } - set { - if let view = self.cropOverlayView { - view.cropSize = newValue - } else { - if self.resizableCropArea { - self.cropOverlayView = WDResizableCropOverlayView(frame: self.bounds, - initialContentSize: CGSizeMake(newValue.width, newValue.height)) - } else { - self.cropOverlayView = WDImageCropOverlayView(frame: self.bounds) - } - self.cropOverlayView.cropSize = newValue - self.addSubview(self.cropOverlayView) - } - } - } - - override init(frame: CGRect) { - super.init(frame: frame) - - self.userInteractionEnabled = true - self.backgroundColor = UIColor.blackColor() - self.scrollView = ScrollView(frame: frame) - self.scrollView.showsHorizontalScrollIndicator = false - self.scrollView.showsVerticalScrollIndicator = false - self.scrollView.delegate = self - self.scrollView.clipsToBounds = false - self.scrollView.decelerationRate = 0 - self.scrollView.backgroundColor = UIColor.clearColor() - self.addSubview(self.scrollView) - - self.imageView = UIImageView(frame: self.scrollView.frame) - self.imageView.contentMode = .ScaleAspectFit - self.imageView.backgroundColor = UIColor.blackColor() - self.scrollView.addSubview(self.imageView) - - self.scrollView.minimumZoomScale = - CGRectGetWidth(self.scrollView.frame) / CGRectGetHeight(self.scrollView.frame) - self.scrollView.maximumZoomScale = 20 - self.scrollView.setZoomScale(1.0, animated: false) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { - if !resizableCropArea { - return self.scrollView - } - - let resizableCropView = cropOverlayView as! WDResizableCropOverlayView - let outerFrame = CGRectInset(resizableCropView.cropBorderView.frame, -10, -10) - - if CGRectContainsPoint(outerFrame, point) { - if resizableCropView.cropBorderView.frame.size.width < 60 || - resizableCropView.cropBorderView.frame.size.height < 60 { - return super.hitTest(point, withEvent: event) - } - - let innerTouchFrame = CGRectInset(resizableCropView.cropBorderView.frame, 30, 30) - if CGRectContainsPoint(innerTouchFrame, point) { - return self.scrollView - } - - let outBorderTouchFrame = CGRectInset(resizableCropView.cropBorderView.frame, -10, -10) - if CGRectContainsPoint(outBorderTouchFrame, point) { - return super.hitTest(point, withEvent: event) - } - - return super.hitTest(point, withEvent: event) - } - - return self.scrollView - } - - override func layoutSubviews() { - super.layoutSubviews() - - let size = self.cropSize; - let toolbarSize = CGFloat(UIDevice.currentDevice().userInterfaceIdiom == .Pad ? 0 : 54) - self.xOffset = floor((CGRectGetWidth(self.bounds) - size.width) * 0.5) - self.yOffset = floor((CGRectGetHeight(self.bounds) - toolbarSize - size.height) * 0.5) - - let height = self.imageToCrop!.size.height - let width = self.imageToCrop!.size.width - - var factor: CGFloat = 0 - var factoredHeight: CGFloat = 0 - var factoredWidth: CGFloat = 0 - - if width > height { - factor = width / size.width - factoredWidth = size.width - factoredHeight = height / factor - } else { - factor = height / size.height - factoredWidth = width / factor - factoredHeight = size.height - } - - self.cropOverlayView.frame = self.bounds - self.scrollView.frame = CGRectMake(xOffset, yOffset, size.width, size.height) - self.scrollView.contentSize = CGSizeMake(size.width, size.height) - self.imageView.frame = CGRectMake(0, floor((size.height - factoredHeight) * 0.5), - factoredWidth, factoredHeight) - } - - func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { - return self.imageView - } - - func croppedImage() -> UIImage { - // Calculate rect that needs to be cropped - var visibleRect = resizableCropArea ? - calcVisibleRectForResizeableCropArea() : calcVisibleRectForCropArea() - - // transform visible rect to image orientation - let rectTransform = orientationTransformedRectOfImage(imageToCrop!) - visibleRect = CGRectApplyAffineTransform(visibleRect, rectTransform); - - // finally crop image - let imageRef = CGImageCreateWithImageInRect(imageToCrop!.CGImage, visibleRect) - let result = UIImage(CGImage: imageRef!, scale: imageToCrop!.scale, - orientation: imageToCrop!.imageOrientation) - - return result - } - - private func calcVisibleRectForResizeableCropArea() -> CGRect { - let resizableView = cropOverlayView as! WDResizableCropOverlayView - - // first of all, get the size scale by taking a look at the real image dimensions. Here it - // doesn't matter if you take the width or the hight of the image, because it will always - // be scaled in the exact same proportion of the real image - var sizeScale = self.imageView.image!.size.width / self.imageView.frame.size.width - sizeScale *= self.scrollView.zoomScale - - // then get the postion of the cropping rect inside the image - var visibleRect = resizableView.contentView.convertRect(resizableView.contentView.bounds, - toView: imageView) - visibleRect = WDImageCropView.scaleRect(visibleRect, scale: sizeScale) - - return visibleRect - } - - private func calcVisibleRectForCropArea() -> CGRect { - // scaled width/height in regards of real width to crop width - let scaleWidth = imageToCrop!.size.width / cropSize.width - let scaleHeight = imageToCrop!.size.height / cropSize.height - var scale: CGFloat = 0 - - if cropSize.width == cropSize.height { - scale = max(scaleWidth, scaleHeight) - } else if cropSize.width > cropSize.height { - scale = imageToCrop!.size.width < imageToCrop!.size.height ? - max(scaleWidth, scaleHeight) : - min(scaleWidth, scaleHeight) - } else { - scale = imageToCrop!.size.width < imageToCrop!.size.height ? - min(scaleWidth, scaleHeight) : - max(scaleWidth, scaleHeight) - } - - // extract visible rect from scrollview and scale it - var visibleRect = scrollView.convertRect(scrollView.bounds, toView:imageView) - visibleRect = WDImageCropView.scaleRect(visibleRect, scale: scale) - - return visibleRect - } - - private func orientationTransformedRectOfImage(image: UIImage) -> CGAffineTransform { - var rectTransform: CGAffineTransform! - - switch image.imageOrientation { - case .Left: - rectTransform = CGAffineTransformTranslate( - CGAffineTransformMakeRotation(CGFloat(M_PI_2)), 0, -image.size.height) - case .Right: - rectTransform = CGAffineTransformTranslate( - CGAffineTransformMakeRotation(CGFloat(-M_PI_2)),-image.size.width, 0) - case .Down: - rectTransform = CGAffineTransformTranslate( - CGAffineTransformMakeRotation(CGFloat(-M_PI)), - -image.size.width, -image.size.height) - default: - rectTransform = CGAffineTransformIdentity - } - - return CGAffineTransformScale(rectTransform, image.scale, image.scale) - } -} \ No newline at end of file + var resizableCropArea = false + + private var scrollView: UIScrollView! + private var imageView: UIImageView! + private var cropOverlayView: WDImageCropOverlayView! + private var xOffset: CGFloat! + private var yOffset: CGFloat! + + private static func scaleRect(rect: CGRect, scale: CGFloat) -> CGRect { + return CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, + width: rect.size.width * scale, height: rect.size.height * scale) + } + + var imageToCrop: UIImage? { + get { + return self.imageView.image + } + set { + self.imageView.image = newValue + } + } + + var cropSize: CGSize { + get { + return self.cropOverlayView.cropSize + } + set { + if let view = self.cropOverlayView { + view.cropSize = newValue + } else { + if self.resizableCropArea { + self.cropOverlayView = WDResizableCropOverlayView(frame: self.bounds, + initialContentSize: CGSize(width: newValue.width, height: newValue.height)) + } else { + self.cropOverlayView = WDImageCropOverlayView(frame: self.bounds) + } + self.cropOverlayView.cropSize = newValue + self.addSubview(self.cropOverlayView) + } + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + + self.isUserInteractionEnabled = true + self.backgroundColor = UIColor.black + self.scrollView = ScrollView(frame: frame) + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.delegate = self + self.scrollView.clipsToBounds = false + self.scrollView.decelerationRate = 0 + self.scrollView.backgroundColor = UIColor.clear + self.addSubview(self.scrollView) + + self.imageView = UIImageView(frame: self.scrollView.frame) + self.imageView.contentMode = .scaleAspectFit + self.imageView.backgroundColor = UIColor.black + self.scrollView.addSubview(self.imageView) + + self.scrollView.minimumZoomScale = + self.scrollView.frame.width / self.scrollView.frame.height + self.scrollView.maximumZoomScale = 20 + self.scrollView.setZoomScale(1.0, animated: false) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !resizableCropArea { + return self.scrollView + } + + let resizableCropView = cropOverlayView as! WDResizableCropOverlayView + let outerFrame = resizableCropView.cropBorderView.frame.insetBy(dx: -10, dy: -10) + + if outerFrame.contains(point) { + if resizableCropView.cropBorderView.frame.size.width < 60 || + resizableCropView.cropBorderView.frame.size.height < 60 { + return super.hitTest(point, with: event) + } + + let innerTouchFrame = resizableCropView.cropBorderView.frame.insetBy(dx: 30, dy: 30) + if innerTouchFrame.contains(point) { + return self.scrollView + } + + let outBorderTouchFrame = resizableCropView.cropBorderView.frame.insetBy(dx: -10, dy: -10) + if outBorderTouchFrame.contains(point) { + return super.hitTest(point, with: event) + } + + return super.hitTest(point, with: event) + } + + return self.scrollView + } + + override func layoutSubviews() { + super.layoutSubviews() + + let size = self.cropSize; + let toolbarSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 0 : 54) + self.xOffset = floor((self.bounds.width - size.width) * 0.5) + self.yOffset = floor((self.bounds.height - toolbarSize - size.height) * 0.5) + + let height = self.imageToCrop!.size.height + let width = self.imageToCrop!.size.width + + var factor: CGFloat = 0 + var factoredHeight: CGFloat = 0 + var factoredWidth: CGFloat = 0 + + if width > height { + factor = width / size.width + factoredWidth = size.width + factoredHeight = height / factor + } else { + factor = height / size.height + factoredWidth = width / factor + factoredHeight = size.height + } + + self.cropOverlayView.frame = self.bounds + self.scrollView.frame = CGRect(x: xOffset, y: yOffset, width: size.width, height: size.height) + self.scrollView.contentSize = CGSize(width: size.width, height: size.height) + self.imageView.frame = CGRect(x: 0, y: floor((size.height - factoredHeight) * 0.5), + width: factoredWidth, height: factoredHeight) + } + + func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { + return self.imageView + } + + func croppedImage() -> UIImage { + // Calculate rect that needs to be cropped + var visibleRect = resizableCropArea ? + calcVisibleRectForResizeableCropArea() : calcVisibleRectForCropArea() + + // transform visible rect to image orientation + let rectTransform = orientationTransformedRectOfImage(image: imageToCrop!) + visibleRect = visibleRect.applying(rectTransform); + + // finally crop image + let imageRef = imageToCrop!.cgImage!.cropping(to: visibleRect) + let result = UIImage(cgImage: imageRef!, scale: imageToCrop!.scale, + orientation: imageToCrop!.imageOrientation) + + return result + } + + private func calcVisibleRectForResizeableCropArea() -> CGRect { + let resizableView = cropOverlayView as! WDResizableCropOverlayView + + // first of all, get the size scale by taking a look at the real image dimensions. Here it + // doesn't matter if you take the width or the hight of the image, because it will always + // be scaled in the exact same proportion of the real image + var sizeScale = self.imageView.image!.size.width / self.imageView.frame.size.width + sizeScale *= self.scrollView.zoomScale + + // then get the postion of the cropping rect inside the image + var visibleRect = resizableView.contentView.convert(resizableView.contentView.bounds, + to: imageView) + visibleRect = WDImageCropView.scaleRect(rect: visibleRect, scale: sizeScale) + + return visibleRect + } + + private func calcVisibleRectForCropArea() -> CGRect { + // scaled width/height in regards of real width to crop width + let scaleWidth = imageToCrop!.size.width / cropSize.width + let scaleHeight = imageToCrop!.size.height / cropSize.height + var scale: CGFloat = 0 + + if cropSize.width == cropSize.height { + scale = max(scaleWidth, scaleHeight) + } else if cropSize.width > cropSize.height { + scale = imageToCrop!.size.width < imageToCrop!.size.height ? + max(scaleWidth, scaleHeight) : + min(scaleWidth, scaleHeight) + } else { + scale = imageToCrop!.size.width < imageToCrop!.size.height ? + min(scaleWidth, scaleHeight) : + max(scaleWidth, scaleHeight) + } + + // extract visible rect from scrollview and scale it + var visibleRect = scrollView.convert(scrollView.bounds, to:imageView) + visibleRect = WDImageCropView.scaleRect(rect: visibleRect, scale: scale) + + return visibleRect + } + + private func orientationTransformedRectOfImage(image: UIImage) -> CGAffineTransform { + var rectTransform: CGAffineTransform! + + switch image.imageOrientation { + case .left: + rectTransform = CGAffineTransform(rotationAngle: CGFloat(M_PI_2)).translatedBy( + x: 0, y: -image.size.height) + case .right: + rectTransform = CGAffineTransform(rotationAngle: CGFloat(-M_PI_2)).translatedBy( + x: -image.size.width, y: 0) + case .down: + rectTransform = CGAffineTransform(rotationAngle: CGFloat(-M_PI)).translatedBy( + x: -image.size.width, y: -image.size.height) + default: + rectTransform = CGAffineTransform.identity + } + + return rectTransform.scaledBy(x: image.scale, y: image.scale) + } +} diff --git a/Classes/WDImageCropViewController.swift b/Classes/WDImageCropViewController.swift index b244791..953a82c 100644 --- a/Classes/WDImageCropViewController.swift +++ b/Classes/WDImageCropViewController.swift @@ -10,137 +10,137 @@ import UIKit import CoreGraphics internal protocol WDImageCropControllerDelegate { - func imageCropController(imageCropController: WDImageCropViewController, didFinishWithCroppedImage croppedImage: UIImage) + func imageCropController(imageCropController: WDImageCropViewController, didFinishWithCroppedImage croppedImage: UIImage) } internal class WDImageCropViewController: UIViewController { - var sourceImage: UIImage! - var delegate: WDImageCropControllerDelegate? - var cropSize: CGSize! - var resizableCropArea = false - - private var croppedImage: UIImage! - - private var imageCropView: WDImageCropView! - private var toolbar: UIToolbar! - private var useButton: UIButton! - private var cancelButton: UIButton! - - override func viewDidLoad() { - super.viewDidLoad() - - self.automaticallyAdjustsScrollViewInsets = false - - self.title = "Choose Photo" - - self.setupNavigationBar() - self.setupCropView() - self.setupToolbar() - - if UIDevice.currentDevice().userInterfaceIdiom == .Phone { - self.navigationController?.navigationBarHidden = true - } else { - self.navigationController?.navigationBarHidden = false - } - } - - override func viewWillLayoutSubviews() { - super.viewWillLayoutSubviews() - - self.imageCropView.frame = self.view.bounds - self.toolbar?.frame = CGRectMake(0, CGRectGetHeight(self.view.frame) - 54, - self.view.frame.size.width, 54) - } - - func actionCancel(sender: AnyObject) { - self.navigationController?.popViewControllerAnimated(true) - } - - func actionUse(sender: AnyObject) { - croppedImage = self.imageCropView.croppedImage() - self.delegate?.imageCropController(self, didFinishWithCroppedImage: croppedImage) - } - - private func setupNavigationBar() { - self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Cancel, - target: self, action: #selector(actionCancel)) - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Use", style: .Plain, - target: self, action: #selector(actionUse)) - } - - private func setupCropView() { - self.imageCropView = WDImageCropView(frame: self.view.bounds) - self.imageCropView.imageToCrop = sourceImage - self.imageCropView.resizableCropArea = self.resizableCropArea - self.imageCropView.cropSize = cropSize - self.view.addSubview(self.imageCropView) - } - - private func setupCancelButton() { - self.cancelButton = UIButton() - self.cancelButton.titleLabel?.font = UIFont.boldSystemFontOfSize(16) - self.cancelButton.titleLabel?.shadowOffset = CGSizeMake(0, -1) - self.cancelButton.frame = CGRectMake(0, 0, 58, 30) - self.cancelButton.setTitle("Cancel", forState: .Normal) - self.cancelButton.setTitleShadowColor( - UIColor(red: 0.118, green: 0.247, blue: 0.455, alpha: 1), forState: .Normal) - self.cancelButton.addTarget(self, action: #selector(actionCancel), forControlEvents: .TouchUpInside) - } - - private func setupUseButton() { - self.useButton = UIButton() - self.useButton.titleLabel?.font = UIFont.boldSystemFontOfSize(16) - self.useButton.titleLabel?.shadowOffset = CGSizeMake(0, -1) - self.useButton.frame = CGRectMake(0, 0, 58, 30) - self.useButton.setTitle("Use", forState: .Normal) - self.useButton.setTitleShadowColor( - UIColor(red: 0.118, green: 0.247, blue: 0.455, alpha: 1), forState: .Normal) - self.useButton.addTarget(self, action: #selector(actionUse), forControlEvents: .TouchUpInside) - } - - private func toolbarBackgroundImage() -> UIImage { - let components: [CGFloat] = [1, 1, 1, 1, 123.0 / 255.0, 125.0 / 255.0, 132.0 / 255.0, 1] - - UIGraphicsBeginImageContextWithOptions(CGSizeMake(320, 54), true, 0) - - let context = UIGraphicsGetCurrentContext() - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradientCreateWithColorComponents(colorSpace, components, nil, 2) - - CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(0, 54), []) - - let viewImage = UIGraphicsGetImageFromCurrentImageContext() - - UIGraphicsEndImageContext() - - return viewImage - } - - private func setupToolbar() { - if UIDevice.currentDevice().userInterfaceIdiom == .Phone { - self.toolbar = UIToolbar(frame: CGRectZero) - self.toolbar.translucent = true - self.toolbar.barStyle = .Black - self.view.addSubview(self.toolbar) - - self.setupCancelButton() - self.setupUseButton() - - let info = UILabel(frame: CGRectZero) - info.text = "" - info.textColor = UIColor(red: 0.173, green: 0.173, blue: 0.173, alpha: 1) - info.backgroundColor = UIColor.clearColor() - info.shadowColor = UIColor(red: 0.827, green: 0.731, blue: 0.839, alpha: 1) - info.shadowOffset = CGSizeMake(0, 1) - info.font = UIFont.boldSystemFontOfSize(18) - info.sizeToFit() - - let cancel = UIBarButtonItem(customView: self.cancelButton) - let flex = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil) - let label = UIBarButtonItem(customView: info) - let use = UIBarButtonItem(customView: self.useButton) - - self.toolbar.setItems([cancel, flex, label, flex, use], animated: false) - } - } + var sourceImage: UIImage! + var delegate: WDImageCropControllerDelegate? + var cropSize: CGSize! + var resizableCropArea = false + + private var croppedImage: UIImage! + + private var imageCropView: WDImageCropView! + private var toolbar: UIToolbar! + private var useButton: UIButton! + private var cancelButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + + self.automaticallyAdjustsScrollViewInsets = false + self.title = "Choose Photo" + + self.setupNavigationBar() + self.setupCropView() + self.setupToolbar() + + if UIDevice.current.userInterfaceIdiom == .phone { + self.navigationController?.isNavigationBarHidden = true + } else { + self.navigationController?.isNavigationBarHidden = false + } + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + + self.imageCropView.frame = self.view.bounds + self.toolbar?.frame = CGRect(x: 0, y: self.view.frame.height - 54, + width: self.view.frame.size.width, height: 54) + } + + func actionCancel(sender: AnyObject) { + self.navigationController?.popViewController(animated: true) + } + + func actionUse(sender: AnyObject) { + croppedImage = self.imageCropView.croppedImage() + self.delegate?.imageCropController(imageCropController: self, didFinishWithCroppedImage: croppedImage) + } + + private func setupNavigationBar() { + self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, + target: self, action: #selector(actionCancel)) + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Use", style: .plain, + target: self, action: #selector(actionUse)) + } + + private func setupCropView() { + self.imageCropView = WDImageCropView(frame: self.view.bounds) + self.imageCropView.imageToCrop = sourceImage + self.imageCropView.resizableCropArea = self.resizableCropArea + self.imageCropView.cropSize = cropSize + self.view.addSubview(self.imageCropView) + } + + private func setupCancelButton() { + self.cancelButton = UIButton() + self.cancelButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) + self.cancelButton.titleLabel?.shadowOffset = CGSize(width: 0, height: -1) + self.cancelButton.frame = CGRect(x: 0, y: 0, width: 58, height: 30) + self.cancelButton.setTitle("Cancel", for: .normal) + self.cancelButton.setTitleShadowColor( + UIColor(red: 0.118, green: 0.247, blue: 0.455, alpha: 1), for: .normal) + self.cancelButton.addTarget(self, action: #selector(actionCancel), for: .touchUpInside) + } + + private func setupUseButton() { + self.useButton = UIButton() + self.useButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) + self.useButton.titleLabel?.shadowOffset = CGSize(width: 0, height: -1) + self.useButton.frame = CGRect(x: 0, y: 0, width: 58, height: 30) + self.useButton.setTitle("Use", for: .normal) + self.useButton.setTitleShadowColor( + UIColor(red: 0.118, green: 0.247, blue: 0.455, alpha: 1), for: .normal) + self.useButton.addTarget(self, action: #selector(actionUse), for: .touchUpInside) + } + + private func toolbarBackgroundImage() -> UIImage { + let components: [CGFloat] = [1, 1, 1, 1, 123.0 / 255.0, 125.0 / 255.0, 132.0 / 255.0, 1] + + UIGraphicsBeginImageContextWithOptions(CGSize(width: 320, height: 54), true, 0) + + let context = UIGraphicsGetCurrentContext() + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorSpace: colorSpace, colorComponents: components, locations: nil, count: 2) + + context!.drawLinearGradient(gradient!, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: 54), options: []) + + let viewImage = UIGraphicsGetImageFromCurrentImageContext() + + UIGraphicsEndImageContext() + + return viewImage! + } + + private func setupToolbar() { + if UIDevice.current.userInterfaceIdiom == .phone { + self.toolbar = UIToolbar(frame: CGRect.zero) + self.toolbar.isTranslucent = true + self.toolbar.barStyle = .black + self.view.addSubview(self.toolbar) + + self.setupCancelButton() + self.setupUseButton() + + let info = UILabel(frame: CGRect.zero) + info.text = "" + info.textColor = UIColor(red: 0.173, green: 0.173, blue: 0.173, alpha: 1) + info.backgroundColor = UIColor.clear + info.shadowColor = UIColor(red: 0.827, green: 0.731, blue: 0.839, alpha: 1) + info.shadowOffset = CGSize(width: 0, height: 1) + info.font = UIFont.boldSystemFont(ofSize: 18) + info.sizeToFit() + + + let cancel = UIBarButtonItem(customView: self.cancelButton) + let flex = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let label = UIBarButtonItem(customView: info) + let use = UIBarButtonItem(customView: self.useButton) + + self.toolbar.setItems([cancel, flex, label, flex, use], animated: false) + } + } } diff --git a/Classes/WDImagePicker.swift b/Classes/WDImagePicker.swift index 70129de..7dc206b 100644 --- a/Classes/WDImagePicker.swift +++ b/Classes/WDImagePicker.swift @@ -9,52 +9,52 @@ import UIKit @objc public protocol WDImagePickerDelegate { - optional func imagePicker(imagePicker: WDImagePicker, pickedImage: UIImage) - optional func imagePickerDidCancel(imagePicker: WDImagePicker) + @objc optional func imagePicker(imagePicker: WDImagePicker, pickedImage: UIImage) + @objc optional func imagePickerDidCancel(imagePicker: WDImagePicker) } @objc public class WDImagePicker: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate, WDImageCropControllerDelegate { - public var delegate: WDImagePickerDelegate? - public var cropSize: CGSize! - public var resizableCropArea = false - - private var _imagePickerController: UIImagePickerController! - - public var imagePickerController: UIImagePickerController { - return _imagePickerController - } - - override public init() { - super.init() - - self.cropSize = CGSizeMake(320, 320) - _imagePickerController = UIImagePickerController() - _imagePickerController.delegate = self - _imagePickerController.sourceType = .PhotoLibrary - } - - private func hideController() { - self._imagePickerController.dismissViewControllerAnimated(true, completion: nil) - } - - public func imagePickerControllerDidCancel(picker: UIImagePickerController) { - if self.delegate?.imagePickerDidCancel != nil { - self.delegate?.imagePickerDidCancel!(self) - } else { - self.hideController() - } - } - - public func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { - let cropController = WDImageCropViewController() - cropController.sourceImage = info[UIImagePickerControllerOriginalImage] as! UIImage - cropController.resizableCropArea = self.resizableCropArea - cropController.cropSize = self.cropSize - cropController.delegate = self - picker.pushViewController(cropController, animated: true) - } - - func imageCropController(imageCropController: WDImageCropViewController, didFinishWithCroppedImage croppedImage: UIImage) { - self.delegate?.imagePicker?(self, pickedImage: croppedImage) - } + public var delegate: WDImagePickerDelegate? + public var cropSize: CGSize! + public var resizableCropArea = false + + private var _imagePickerController: UIImagePickerController! + + public var imagePickerController: UIImagePickerController { + return _imagePickerController + } + + override public init() { + super.init() + + self.cropSize = CGSize(width: 320, height: 320) + _imagePickerController = UIImagePickerController() + _imagePickerController.delegate = self + _imagePickerController.sourceType = .photoLibrary + } + + private func hideController() { + self._imagePickerController.dismiss(animated: true, completion: nil) + } + + public func imagePickerControllerDidCancel(picker: UIImagePickerController) { + if self.delegate?.imagePickerDidCancel != nil { + self.delegate?.imagePickerDidCancel!(imagePicker: self) + } else { + self.hideController() + } + } + + public func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { + let cropController = WDImageCropViewController() + cropController.sourceImage = info[UIImagePickerControllerOriginalImage] as! UIImage + cropController.resizableCropArea = self.resizableCropArea + cropController.cropSize = self.cropSize + cropController.delegate = self + picker.pushViewController(cropController, animated: true) + } + + func imageCropController(imageCropController: WDImageCropViewController, didFinishWithCroppedImage croppedImage: UIImage) { + self.delegate?.imagePicker?(imagePicker: self, pickedImage: croppedImage) + } } diff --git a/Classes/WDResizableCropOverlayView.swift b/Classes/WDResizableCropOverlayView.swift index b877235..45fe08a 100644 --- a/Classes/WDResizableCropOverlayView.swift +++ b/Classes/WDResizableCropOverlayView.swift @@ -9,237 +9,233 @@ import UIKit private struct WDResizableViewBorderMultiplyer { - var widthMultiplyer: CGFloat! - var heightMultiplyer: CGFloat! - var xMultiplyer: CGFloat! - var yMultiplyer: CGFloat! + var widthMultiplyer: CGFloat! + var heightMultiplyer: CGFloat! + var xMultiplyer: CGFloat! + var yMultiplyer: CGFloat! } internal class WDResizableCropOverlayView: WDImageCropOverlayView { - private let kBorderCorrectionValue: CGFloat = 12 - - var contentView: UIView! - var cropBorderView: WDCropBorderView! - - private var initialContentSize = CGSize(width: 0, height: 0) - private var resizingEnabled: Bool! - private var anchor: CGPoint! - private var startPoint: CGPoint! - private var resizeMultiplyer = WDResizableViewBorderMultiplyer() - - override var frame: CGRect { - get { - return super.frame - } - set { - super.frame = newValue - - let toolbarSize = CGFloat(UIDevice.currentDevice().userInterfaceIdiom == .Pad ? 0 : 54) - let width = self.bounds.size.width - let height = self.bounds.size.height - - contentView?.frame = CGRectMake(( - width - initialContentSize.width) / 2, - (height - toolbarSize - initialContentSize.height) / 2, - initialContentSize.width, - initialContentSize.height) - - cropBorderView?.frame = CGRectMake( - (width - initialContentSize.width) / 2 - kBorderCorrectionValue, - (height - toolbarSize - initialContentSize.height) / 2 - kBorderCorrectionValue, - initialContentSize.width + kBorderCorrectionValue * 2, - initialContentSize.height + kBorderCorrectionValue * 2) - } - } - - init(frame: CGRect, initialContentSize: CGSize) { - super.init(frame: frame) - - self.initialContentSize = initialContentSize - self.addContentViews() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override init(frame: CGRect) { - super.init(frame: frame) - } - - override func touchesBegan(touches: Set, withEvent event: UIEvent?) { - if let touch = touches.first { - let touchPoint = touch.locationInView(cropBorderView) - - anchor = self.calculateAnchorBorder(touchPoint) - fillMultiplyer() - resizingEnabled = true - startPoint = touch.locationInView(self.superview) - } - } - - override func touchesMoved(touches: Set, withEvent event: UIEvent?) { - if let touch = touches.first { - if resizingEnabled! { - self.resizeWithTouchPoint(touch.locationInView(self.superview)) - } - } - } - - override func drawRect(rect: CGRect) { - //fill outer rect - UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).set() - UIRectFill(self.bounds) - - //fill inner rect - UIColor.clearColor().set() - UIRectFill(self.contentView.frame) - } - - private func addContentViews() { - let toolbarSize = CGFloat(UIDevice.currentDevice().userInterfaceIdiom == .Pad ? 0 : 54) - let width = self.bounds.size.width - let height = self.bounds.size.height - - contentView = UIView(frame: CGRectMake(( - width - initialContentSize.width) / 2, - (height - toolbarSize - initialContentSize.height) / 2, - initialContentSize.width, - initialContentSize.height)) - contentView.backgroundColor = UIColor.clearColor() - self.cropSize = contentView.frame.size - self.addSubview(contentView) - - cropBorderView = WDCropBorderView(frame: CGRectMake( - (width - initialContentSize.width) / 2 - kBorderCorrectionValue, - (height - toolbarSize - initialContentSize.height) / 2 - kBorderCorrectionValue, - initialContentSize.width + kBorderCorrectionValue * 2, - initialContentSize.height + kBorderCorrectionValue * 2)) - self.addSubview(cropBorderView) - } - - private func calculateAnchorBorder(anchorPoint: CGPoint) -> CGPoint { - let allHandles = getAllCurrentHandlePositions() - var closest: CGFloat = 3000 - var anchor: CGPoint! - - for handlePoint in allHandles { - // Pythagoras is watching you :-) - let xDist = handlePoint.x - anchorPoint.x - let yDist = handlePoint.y - anchorPoint.y - let dist = sqrt(xDist * xDist + yDist * yDist) - - closest = dist < closest ? dist : closest - anchor = closest == dist ? handlePoint : anchor - } - - return anchor - } - - private func getAllCurrentHandlePositions() -> [CGPoint] { - let leftX: CGFloat = 0 - let rightX = cropBorderView.bounds.size.width - let centerX = leftX + (rightX - leftX) / 2 - - let topY: CGFloat = 0 - let bottomY = cropBorderView.bounds.size.height - let middleY = topY + (bottomY - topY) / 2 - - // starting with the upper left corner and then following the rect clockwise - let topLeft = CGPointMake(leftX, topY) - let topCenter = CGPointMake(centerX, topY) - let topRight = CGPointMake(rightX, topY) - let middleRight = CGPointMake(rightX, middleY) - let bottomRight = CGPointMake(rightX, bottomY) - let bottomCenter = CGPointMake(centerX, bottomY) - let bottomLeft = CGPointMake(leftX, bottomY) - let middleLeft = CGPointMake(leftX, middleY) - - return [topLeft, topCenter, topRight, middleRight, bottomRight, bottomCenter, bottomLeft, - middleLeft] - } - - private func resizeWithTouchPoint(point: CGPoint) { - // This is the place where all the magic happends - // prevent goint offscreen... - let border = kBorderCorrectionValue * 2 - var pointX = point.x < border ? border : point.x - var pointY = point.y < border ? border : point.y - pointX = pointX > self.superview!.bounds.size.width - border ? - self.superview!.bounds.size.width - border : pointX - pointY = pointY > self.superview!.bounds.size.height - border ? - self.superview!.bounds.size.height - border : pointY - - let heightChange = (pointY - startPoint.y) * resizeMultiplyer.heightMultiplyer - let widthChange = (startPoint.x - pointX) * resizeMultiplyer.widthMultiplyer - let xChange = -1 * widthChange * resizeMultiplyer.xMultiplyer - let yChange = -1 * heightChange * resizeMultiplyer.yMultiplyer - - var newFrame = CGRectMake( - cropBorderView.frame.origin.x + xChange, - cropBorderView.frame.origin.y + yChange, - cropBorderView.frame.size.width + widthChange, - cropBorderView.frame.size.height + heightChange); - newFrame = self.preventBorderFrameFromGettingTooSmallOrTooBig(newFrame) - self.resetFrame(to: newFrame) - startPoint = CGPointMake(pointX, pointY) - } - - private func preventBorderFrameFromGettingTooSmallOrTooBig(frame: CGRect) -> CGRect { - let toolbarSize = CGFloat(UIDevice.currentDevice().userInterfaceIdiom == .Pad ? 0 : 54) - var newFrame = frame - - if newFrame.size.width < 64 { - newFrame.size.width = cropBorderView.frame.size.width - newFrame.origin.x = cropBorderView.frame.origin.x - } - if newFrame.size.height < 64 { - newFrame.size.height = cropBorderView.frame.size.height - newFrame.origin.y = cropBorderView.frame.origin.y - } - - if newFrame.origin.x < 0 { - newFrame.size.width = cropBorderView.frame.size.width + - (cropBorderView.frame.origin.x - self.superview!.bounds.origin.x) - newFrame.origin.x = 0 - } - - if newFrame.origin.y < 0 { - newFrame.size.height = cropBorderView.frame.size.height + - (cropBorderView.frame.origin.y - self.superview!.bounds.origin.y) - newFrame.origin.y = 0 - } - - if newFrame.size.width + newFrame.origin.x > self.frame.size.width { - newFrame.size.width = self.frame.size.width - cropBorderView.frame.origin.x - } - - if newFrame.size.height + newFrame.origin.y > self.frame.size.height - toolbarSize { - newFrame.size.height = self.frame.size.height - - cropBorderView.frame.origin.y - toolbarSize - } - - return newFrame - } - - private func resetFrame(to frame: CGRect) { - cropBorderView.frame = frame - contentView.frame = CGRectInset(frame, kBorderCorrectionValue, kBorderCorrectionValue) - cropSize = contentView.frame.size - self.setNeedsDisplay() - cropBorderView.setNeedsDisplay() - } - - private func fillMultiplyer() { - // -1 left, 0 middle, 1 right - resizeMultiplyer.heightMultiplyer = anchor.y == 0 ? - -1 : anchor.y == cropBorderView.bounds.size.height ? 1 : 0 - // -1 up, 0 middle, 1 down - resizeMultiplyer.widthMultiplyer = anchor.x == 0 ? - 1 : anchor.x == cropBorderView.bounds.size.width ? -1 : 0 - // 1 left, 0 middle, 0 right - resizeMultiplyer.xMultiplyer = anchor.x == 0 ? 1 : 0 - // 1 up, 0 middle, 0 down - resizeMultiplyer.yMultiplyer = anchor.y == 0 ? 1 : 0 - } -} \ No newline at end of file + private let kBorderCorrectionValue: CGFloat = 12 + + var contentView: UIView! + var cropBorderView: WDCropBorderView! + + private var initialContentSize = CGSize(width: 0, height: 0) + private var resizingEnabled: Bool! + private var anchor: CGPoint! + private var startPoint: CGPoint! + private var resizeMultiplyer = WDResizableViewBorderMultiplyer() + + override var frame: CGRect { + get { + return super.frame + } + set { + super.frame = newValue + + let toolbarSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 0 : 54) + let width = self.bounds.size.width + let height = self.bounds.size.height + + contentView?.frame = CGRect(x: (width - initialContentSize.width) / 2, + y: (height - toolbarSize - initialContentSize.height) / 2, + width: initialContentSize.width, + height: initialContentSize.height) + + cropBorderView?.frame = CGRect(x: (width - initialContentSize.width) / 2 - kBorderCorrectionValue, + y: (height - toolbarSize - initialContentSize.height) / 2 - kBorderCorrectionValue, + width: initialContentSize.width + kBorderCorrectionValue * 2, + height: initialContentSize.height + kBorderCorrectionValue * 2) + } + } + + init(frame: CGRect, initialContentSize: CGSize) { + super.init(frame: frame) + + self.initialContentSize = initialContentSize + self.addContentViews() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override init(frame: CGRect) { + super.init(frame: frame) + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + if let touch = touches.first { + let touchPoint = touch.location(in: cropBorderView) + + anchor = self.calculateAnchorBorder(anchorPoint: touchPoint) + fillMultiplyer() + resizingEnabled = true + startPoint = touch.location(in: self.superview) + } + } + + override func touchesMoved(_ touches: Set, with event: UIEvent?) { + if let touch = touches.first { + if resizingEnabled! { + self.resizeWithTouchPoint(point: touch.location(in: self.superview)) + } + } + } + + override func draw(_ rect: CGRect) { + //fill outer rect + UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).set() + UIRectFill(self.bounds) + + //fill inner rect + UIColor.clear.set() + UIRectFill(self.contentView.frame) + } + + private func addContentViews() { + let toolbarSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 0 : 54) + let width = self.bounds.size.width + let height = self.bounds.size.height + + contentView = UIView(frame: CGRect(x: (width - initialContentSize.width) / 2, + y: (height - toolbarSize - initialContentSize.height) / 2, + width: initialContentSize.width, + height: initialContentSize.height)) + contentView.backgroundColor = UIColor.clear + self.cropSize = contentView.frame.size + self.addSubview(contentView) + + cropBorderView = WDCropBorderView(frame: CGRect(x: (width - initialContentSize.width) / 2 - kBorderCorrectionValue, + y: (height - toolbarSize - initialContentSize.height) / 2 - kBorderCorrectionValue, + width: initialContentSize.width + kBorderCorrectionValue * 2, + height: initialContentSize.height + kBorderCorrectionValue * 2)) + self.addSubview(cropBorderView) + } + + private func calculateAnchorBorder(anchorPoint: CGPoint) -> CGPoint { + let allHandles = getAllCurrentHandlePositions() + var closest: CGFloat = 3000 + var anchor: CGPoint! + + for handlePoint in allHandles { + // Pythagoras is watching you :-) + let xDist = handlePoint.x - anchorPoint.x + let yDist = handlePoint.y - anchorPoint.y + let dist = sqrt(xDist * xDist + yDist * yDist) + + closest = dist < closest ? dist : closest + anchor = closest == dist ? handlePoint : anchor + } + + return anchor + } + + private func getAllCurrentHandlePositions() -> [CGPoint] { + let leftX: CGFloat = 0 + let rightX = cropBorderView.bounds.size.width + let centerX = leftX + (rightX - leftX) / 2 + + let topY: CGFloat = 0 + let bottomY = cropBorderView.bounds.size.height + let middleY = topY + (bottomY - topY) / 2 + + // starting with the upper left corner and then following the rect clockwise + let topLeft = CGPoint(x: leftX, y: topY) + let topCenter = CGPoint(x: centerX, y: topY) + let topRight = CGPoint(x: rightX, y: topY) + let middleRight = CGPoint(x: rightX, y: middleY) + let bottomRight = CGPoint(x: rightX, y: bottomY) + let bottomCenter = CGPoint(x: centerX, y: bottomY) + let bottomLeft = CGPoint(x: leftX, y: bottomY) + let middleLeft = CGPoint(x: leftX, y: middleY) + + return [topLeft, topCenter, topRight, middleRight, bottomRight, bottomCenter, bottomLeft, + middleLeft] + } + + private func resizeWithTouchPoint(point: CGPoint) { + // This is the place where all the magic happends + // prevent goint offscreen... + let border = kBorderCorrectionValue * 2 + var pointX = point.x < border ? border : point.x + var pointY = point.y < border ? border : point.y + pointX = pointX > self.superview!.bounds.size.width - border ? + self.superview!.bounds.size.width - border : pointX + pointY = pointY > self.superview!.bounds.size.height - border ? + self.superview!.bounds.size.height - border : pointY + + let heightChange = (pointY - startPoint.y) * resizeMultiplyer.heightMultiplyer + let widthChange = (startPoint.x - pointX) * resizeMultiplyer.widthMultiplyer + let xChange = -1 * widthChange * resizeMultiplyer.xMultiplyer + let yChange = -1 * heightChange * resizeMultiplyer.yMultiplyer + + var newFrame = CGRect( + x: cropBorderView.frame.origin.x + xChange, + y: cropBorderView.frame.origin.y + yChange, + width: cropBorderView.frame.size.width + widthChange, + height: cropBorderView.frame.size.height + heightChange); + newFrame = self.preventBorderFrameFromGettingTooSmallOrTooBig(frame: newFrame) + self.resetFrame(to: newFrame) + startPoint = CGPoint(x: pointX, y: pointY) + } + + private func preventBorderFrameFromGettingTooSmallOrTooBig(frame: CGRect) -> CGRect { + let toolbarSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 0 : 54) + var newFrame = frame + + if newFrame.size.width < 64 { + newFrame.size.width = cropBorderView.frame.size.width + newFrame.origin.x = cropBorderView.frame.origin.x + } + if newFrame.size.height < 64 { + newFrame.size.height = cropBorderView.frame.size.height + newFrame.origin.y = cropBorderView.frame.origin.y + } + + if newFrame.origin.x < 0 { + newFrame.size.width = cropBorderView.frame.size.width + + (cropBorderView.frame.origin.x - self.superview!.bounds.origin.x) + newFrame.origin.x = 0 + } + + if newFrame.origin.y < 0 { + newFrame.size.height = cropBorderView.frame.size.height + + (cropBorderView.frame.origin.y - self.superview!.bounds.origin.y) + newFrame.origin.y = 0 + } + + if newFrame.size.width + newFrame.origin.x > self.frame.size.width { + newFrame.size.width = self.frame.size.width - cropBorderView.frame.origin.x + } + + if newFrame.size.height + newFrame.origin.y > self.frame.size.height - toolbarSize { + newFrame.size.height = self.frame.size.height - + cropBorderView.frame.origin.y - toolbarSize + } + + return newFrame + } + + private func resetFrame(to frame: CGRect) { + cropBorderView.frame = frame + contentView.frame = frame.insetBy(dx: kBorderCorrectionValue, dy: kBorderCorrectionValue) + cropSize = contentView.frame.size + self.setNeedsDisplay() + cropBorderView.setNeedsDisplay() + } + + private func fillMultiplyer() { + // -1 left, 0 middle, 1 right + resizeMultiplyer.heightMultiplyer = anchor.y == 0 ? + -1 : anchor.y == cropBorderView.bounds.size.height ? 1 : 0 + // -1 up, 0 middle, 1 down + resizeMultiplyer.widthMultiplyer = anchor.x == 0 ? + 1 : anchor.x == cropBorderView.bounds.size.width ? -1 : 0 + // 1 left, 0 middle, 0 right + resizeMultiplyer.xMultiplyer = anchor.x == 0 ? 1 : 0 + // 1 up, 0 middle, 0 down + resizeMultiplyer.yMultiplyer = anchor.y == 0 ? 1 : 0 + } +}