Ensure the crop view is WYSIWIG.

// FREEBIE
This commit is contained in:
Matthew Chen 2017-09-05 12:20:49 -04:00 committed by Michael Kirk
parent 4e93bec230
commit 0d04cf251b

View file

@ -204,13 +204,16 @@ class CropScaleImageViewController: OWSViewController {
let maskingView = OWSBezierPathView()
contentView.addSubview(maskingView)
maskingView.configureShapeLayerBlock = { layer, bounds in
maskingView.configureShapeLayerBlock = { [weak self] layer, bounds in
guard let strongSelf = self else {
return
}
let path = UIBezierPath(rect: bounds)
let radius = min(bounds.size.width, bounds.size.height) * 0.5 - self.maskMargin
// Center the circle's bounding rectangle
let circleRect = CGRect(x: bounds.size.width * 0.5 - radius, y: bounds.size.height * 0.5 - radius, width: radius * 2, height: radius * 2)
let circleRect = strongSelf.cropFrame(forBounds:bounds)
let radius = circleRect.size.width * 0.5
let circlePath = UIBezierPath(roundedRect: circleRect, cornerRadius: radius)
path.append(circlePath)
path.usesEvenOddFillRule = true
@ -239,6 +242,15 @@ class CropScaleImageViewController: OWSViewController {
contentView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan(sender:))))
}
// Given the current bounds for the image view, return the frame of the
// crop region within that view.
private func cropFrame(forBounds bounds: CGRect) -> CGRect {
let radius = min(bounds.size.width, bounds.size.height) * 0.5 - self.maskMargin
// Center the circle's bounding rectangle
let circleRect = CGRect(x: bounds.size.width * 0.5 - radius, y: bounds.size.height * 0.5 - radius, width: radius * 2, height: radius * 2)
return circleRect
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
@ -294,11 +306,14 @@ class CropScaleImageViewController: OWSViewController {
return
}
let viewSizePoints = imageView.frame.size
// The size of the image view (should be full screen).
let imageViewSizePoints = imageView.frame.size
guard
(viewSizePoints.width > 0 && viewSizePoints.height > 0) else {
(imageViewSizePoints.width > 0 && imageViewSizePoints.height > 0) else {
return
}
// The frame of the crop circle within the image view.
let cropFrame = self.cropFrame(forBounds:CGRect(origin:CGPoint.zero, size: imageViewSizePoints))
// Normalize the scaling property.
imageScale = max(kMinImageScale, min(kMaxImageScale, imageScale))
@ -317,29 +332,29 @@ class CropScaleImageViewController: OWSViewController {
srcTranslation = CGPoint(x: max(minSrcTranslationPoints.x, min(maxSrcTranslationPoints.x, srcTranslation.x)),
y: max(minSrcTranslationPoints.y, min(maxSrcTranslationPoints.y, srcTranslation.y)))
var imageViewFrame = imageRenderRect(forDstSize: viewSizePoints)
// offset to vertically center image in view (aspect ratio?)
let srcToViewRatio = viewSizePoints.width / srcCropSizePoints.width / imageScale
let heightOfImageLayer = srcDefaultCropSizePoints.height * srcToViewRatio
let yOffset = imageView.frame.height * 0.5 - heightOfImageLayer * 0.5
imageViewFrame = imageViewFrame.offsetBy(dx: 0, dy: yOffset)
// inset frame by the with of the masking margin so the user can pan to the very edge of the image
imageViewFrame = CGRect(x: imageViewFrame.origin.x + self.maskMargin,
y: imageViewFrame.origin.y + self.maskMargin,
width: imageViewFrame.width - maskMargin * 2,
height: imageViewFrame.height - maskMargin * 2)
// The frame of the image layer in crop frame coordinates.
let rawImageLayerFrame = imageRenderRect(forDstSize: cropFrame.size)
// The frame of the image layer in image view coordinates.
let imageLayerFrame = CGRect(x: rawImageLayerFrame.origin.x + cropFrame.origin.x,
y: rawImageLayerFrame.origin.y + cropFrame.origin.y,
width: rawImageLayerFrame.size.width,
height: rawImageLayerFrame.size.height)
// Disable implicit animations for snappier panning/zooming.
CATransaction.begin()
CATransaction.setDisableActions(true)
imageLayer.frame = imageViewFrame
imageLayer.frame = imageLayerFrame
CATransaction.commit()
}
// Give the size of a given view or image context into which we
// will render the source image, return the frame (in that
// view/context's coordinate system) to render the source image.
//
// Gathering this logic in a single function ensures that the
// output will be WYSIWYG with the view state.
private func imageRenderRect(forDstSize dstSize: CGSize) -> CGRect {
let srcCropSizePoints = CGSize(width:srcDefaultCropSizePoints.width / imageScale,
@ -347,10 +362,10 @@ class CropScaleImageViewController: OWSViewController {
let srcToViewRatio = dstSize.width / srcCropSizePoints.width
return CGRect(origin: CGPoint(x: srcTranslation.x * -srcToViewRatio,
y: srcTranslation.y * -srcToViewRatio),
size: CGSize(width:srcImageSizePoints.width * +srcToViewRatio,
height:srcImageSizePoints.height * +srcToViewRatio
return CGRect(origin: CGPoint(x:srcTranslation.x * -srcToViewRatio,
y:srcTranslation.y * -srcToViewRatio),
size: CGSize(width:srcImageSizePoints.width * +srcToViewRatio,
height:srcImageSizePoints.height * +srcToViewRatio
))
}