Merge branch 'charlesmchen/imageEditorCrop3'

pull/2/head
Matthew Chen 6 years ago
commit 7caa29e471

@ -1324,12 +1324,19 @@ extension AttachmentPrepViewController: UIScrollViewDelegate {
// MARK: - // MARK: -
extension AttachmentPrepViewController: ImageEditorViewDelegate { extension AttachmentPrepViewController: ImageEditorViewDelegate {
public func imageEditor(presentFullScreenOverlay viewController: UIViewController) { public func imageEditor(presentFullScreenOverlay viewController: UIViewController,
withNavigation: Bool) {
let navigationController = OWSNavigationController(rootViewController: viewController)
navigationController.modalPresentationStyle = .overFullScreen if withNavigation {
self.present(navigationController, animated: true) { let navigationController = OWSNavigationController(rootViewController: viewController)
// Do nothing. navigationController.modalPresentationStyle = .overFullScreen
self.present(navigationController, animated: true) {
// Do nothing.
}
} else {
self.present(viewController, animated: true) {
// Do nothing.
}
} }
} }
} }

@ -91,7 +91,7 @@ public class ImageEditorCanvasView: UIView {
strongSelf.updateAllContent() strongSelf.updateAllContent()
} }
clipView.addSubview(contentView) clipView.addSubview(contentView)
contentView.ows_autoPinToSuperviewEdges() contentView.autoPinEdgesToSuperviewEdges()
updateLayout() updateLayout()

@ -36,12 +36,12 @@ class ImageEditorCropViewController: OWSViewController {
case topLeft, topRight, bottomLeft, bottomRight case topLeft, topRight, bottomLeft, bottomRight
} }
private class CropCornerView: UIView { private class CropCornerView: OWSLayerView {
let cropRegion: CropRegion let cropRegion: CropRegion
init(cropRegion: CropRegion) { init(cropRegion: CropRegion) {
self.cropRegion = cropRegion self.cropRegion = cropRegion
super.init(frame: .zero) super.init()
} }
@available(*, unavailable, message: "use other init() instead.") @available(*, unavailable, message: "use other init() instead.")
@ -81,35 +81,44 @@ class ImageEditorCropViewController: OWSViewController {
override func loadView() { override func loadView() {
self.view = UIView() self.view = UIView()
if (UIAccessibilityIsReduceTransparencyEnabled()) { self.view.backgroundColor = .black
self.view.backgroundColor = UIColor(white: 0.5, alpha: 0.5)
} else { // MARK: - Buttons
let alpha = OWSNavigationBar.backgroundBlurMutingFactor
self.view.backgroundColor = UIColor(white: 0.5, alpha: alpha)
let blurEffectView = UIVisualEffectView(effect: Theme.barBlurEffect) // TODO: Apply icons.
blurEffectView.layer.zPosition = -1 let doneButton = OWSButton(title: "Done") { [weak self] in
self.view.addSubview(blurEffectView) self?.didTapBackButton()
blurEffectView.autoPinEdgesToSuperviewEdges() }
let rotate90Button = OWSButton(title: "Rotate 90°") { [weak self] in
self?.rotate90ButtonPressed()
}
let rotate45Button = OWSButton(title: "Rotate 45°") { [weak self] in
self?.rotate45ButtonPressed()
}
let resetButton = OWSButton(title: "Reset") { [weak self] in
self?.resetButtonPressed()
}
let zoom2xButton = OWSButton(title: "Zoom 2x") { [weak self] in
self?.zoom2xButtonPressed()
} }
let stackView = UIStackView() // MARK: - Header
stackView.axis = .vertical
stackView.alignment = .fill let header = UIStackView(arrangedSubviews: [
stackView.spacing = 16 UIView.hStretchingSpacer(),
stackView.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20) resetButton,
stackView.isLayoutMarginsRelativeArrangement = true doneButton
self.view.addSubview(stackView) ])
stackView.ows_autoPinToSuperviewEdges() header.axis = .horizontal
header.spacing = 16
header.backgroundColor = .clear
header.isOpaque = false
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, // MARK: - Canvas & Wrapper
target: self,
action: #selector(didTapBackButton))
let wrapperView = UIView.container() let wrapperView = UIView.container()
wrapperView.backgroundColor = .clear wrapperView.backgroundColor = .clear
wrapperView.isOpaque = false wrapperView.isOpaque = false
stackView.addArrangedSubview(wrapperView)
// TODO: We could mask the clipped region with a semi-transparent overlay like WA. // TODO: We could mask the clipped region with a semi-transparent overlay like WA.
clipView.clipsToBounds = true clipView.clipsToBounds = true
@ -135,35 +144,37 @@ class ImageEditorCropViewController: OWSViewController {
strongSelf.updateContent() strongSelf.updateContent()
} }
clipView.addSubview(contentView) clipView.addSubview(contentView)
contentView.ows_autoPinToSuperviewEdges() contentView.autoPinEdgesToSuperviewEdges()
let rotate90Button = OWSButton() // MARK: - Footer
rotate90Button.setTitle(NSLocalizedString("IMAGE_EDITOR_ROTATE_90_BUTTON", comment: "Label for button that rotates image 90 degrees."),
for: .normal)
rotate90Button.block = { [weak self] in
self?.rotate90ButtonPressed()
}
let rotate45Button = OWSButton() let footer = UIStackView(arrangedSubviews: [
rotate45Button.setTitle(NSLocalizedString("IMAGE_EDITOR_ROTATE_45_BUTTON", comment: "Label for button that rotates image 45 degrees."), rotate90Button,
for: .normal) rotate45Button,
rotate45Button.block = { [weak self] in UIView.hStretchingSpacer(),
self?.rotate45ButtonPressed() zoom2xButton
} ])
footer.axis = .horizontal
footer.spacing = 16
footer.backgroundColor = .clear
footer.isOpaque = false
let resetButton = OWSButton() let stackView = UIStackView(arrangedSubviews: [
resetButton.setTitle(NSLocalizedString("IMAGE_EDITOR_RESET_BUTTON", comment: "Label for button that resets crop & rotation state."), header,
for: .normal) wrapperView,
resetButton.block = { [weak self] in footer
self?.resetButtonPressed() ])
} stackView.axis = .vertical
stackView.alignment = .fill
stackView.spacing = 24
stackView.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20)
stackView.isLayoutMarginsRelativeArrangement = true
self.view.addSubview(stackView)
stackView.autoPinEdgesToSuperviewEdges()
let zoom2xButton = OWSButton() // MARK: - Crop View
zoom2xButton.setTitle("Zoom 2x",
for: .normal) // Add crop view last so that it appears in front of the content.
zoom2xButton.block = { [weak self] in
self?.zoom2xButtonPressed()
}
cropView.setContentHuggingLow() cropView.setContentHuggingLow()
cropView.setCompressionResistanceLow() cropView.setCompressionResistanceLow()
@ -176,8 +187,8 @@ class ImageEditorCropViewController: OWSViewController {
cropCornerView.autoPinEdge(toSuperviewEdge: .left) cropCornerView.autoPinEdge(toSuperviewEdge: .left)
case .topRight, .bottomRight: case .topRight, .bottomRight:
cropCornerView.autoPinEdge(toSuperviewEdge: .right) cropCornerView.autoPinEdge(toSuperviewEdge: .right)
default: default:
owsFailDebug("Invalid crop region: \(cropRegion)") owsFailDebug("Invalid crop region: \(cropRegion)")
} }
switch cropCornerView.cropRegion { switch cropCornerView.cropRegion {
case .topLeft, .topRight: case .topLeft, .topRight:
@ -189,19 +200,14 @@ class ImageEditorCropViewController: OWSViewController {
} }
} }
let footer = UIStackView(arrangedSubviews: [rotate90Button, rotate45Button, resetButton, zoom2xButton]) setCropViewAppearance()
footer.axis = .horizontal
footer.spacing = 16
footer.backgroundColor = .clear
footer.isOpaque = false
stackView.addArrangedSubview(footer)
updateClipViewLayout() updateClipViewLayout()
configureGestures() configureGestures()
} }
private static let desiredCornerSize: CGFloat = 30 private static let desiredCornerSize: CGFloat = 24
private static let minCropSize: CGFloat = desiredCornerSize * 2 private static let minCropSize: CGFloat = desiredCornerSize * 2
private var cornerSize = CGSize.zero private var cornerSize = CGSize.zero
@ -219,6 +225,72 @@ class ImageEditorCropViewController: OWSViewController {
private var cropViewConstraints = [NSLayoutConstraint]() private var cropViewConstraints = [NSLayoutConstraint]()
private func setCropViewAppearance() {
// TODO: Tune the size.
let cornerSize = CGSize(width: min(clipView.width() * 0.5, ImageEditorCropViewController.desiredCornerSize),
height: min(clipView.height() * 0.5, ImageEditorCropViewController.desiredCornerSize))
self.cornerSize = cornerSize
for cropCornerView in cropCornerViews {
let cornerThickness: CGFloat = 2
let shapeLayer = CAShapeLayer()
cropCornerView.layer.addSublayer(shapeLayer)
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = nil
cropCornerView.layoutCallback = { (view) in
let shapeFrame = view.bounds.insetBy(dx: -cornerThickness, dy: -cornerThickness)
shapeLayer.frame = shapeFrame
let bezierPath = UIBezierPath()
switch cropCornerView.cropRegion {
case .topLeft:
bezierPath.addRegion(withPoints: [
CGPoint.zero,
CGPoint(x: shapeFrame.width - cornerThickness, y: 0),
CGPoint(x: shapeFrame.width - cornerThickness, y: cornerThickness),
CGPoint(x: cornerThickness, y: cornerThickness),
CGPoint(x: cornerThickness, y: shapeFrame.height - cornerThickness),
CGPoint(x: 0, y: shapeFrame.height - cornerThickness)
])
case .topRight:
bezierPath.addRegion(withPoints: [
CGPoint(x: shapeFrame.width, y: 0),
CGPoint(x: shapeFrame.width, y: shapeFrame.height - cornerThickness),
CGPoint(x: shapeFrame.width - cornerThickness, y: shapeFrame.height - cornerThickness),
CGPoint(x: shapeFrame.width - cornerThickness, y: cornerThickness),
CGPoint(x: cornerThickness, y: cornerThickness),
CGPoint(x: cornerThickness, y: 0)
])
case .bottomLeft:
bezierPath.addRegion(withPoints: [
CGPoint(x: 0, y: shapeFrame.height),
CGPoint(x: 0, y: cornerThickness),
CGPoint(x: cornerThickness, y: cornerThickness),
CGPoint(x: cornerThickness, y: shapeFrame.height - cornerThickness),
CGPoint(x: shapeFrame.width - cornerThickness, y: shapeFrame.height - cornerThickness),
CGPoint(x: shapeFrame.width - cornerThickness, y: shapeFrame.height)
])
case .bottomRight:
bezierPath.addRegion(withPoints: [
CGPoint(x: shapeFrame.width, y: shapeFrame.height),
CGPoint(x: cornerThickness, y: shapeFrame.height),
CGPoint(x: cornerThickness, y: shapeFrame.height - cornerThickness),
CGPoint(x: shapeFrame.width - cornerThickness, y: shapeFrame.height - cornerThickness),
CGPoint(x: shapeFrame.width - cornerThickness, y: cornerThickness),
CGPoint(x: shapeFrame.width, y: cornerThickness)
])
default:
owsFailDebug("Invalid crop region: \(cropCornerView.cropRegion)")
}
shapeLayer.path = bezierPath.cgPath
}
}
cropView.addBorder(with: .white)
}
private func updateCropViewLayout() { private func updateCropViewLayout() {
NSLayoutConstraint.deactivate(cropViewConstraints) NSLayoutConstraint.deactivate(cropViewConstraints)
cropViewConstraints.removeAll() cropViewConstraints.removeAll()
@ -229,9 +301,6 @@ class ImageEditorCropViewController: OWSViewController {
self.cornerSize = cornerSize self.cornerSize = cornerSize
for cropCornerView in cropCornerViews { for cropCornerView in cropCornerViews {
cropViewConstraints.append(contentsOf: cropCornerView.autoSetDimensions(to: cornerSize)) cropViewConstraints.append(contentsOf: cropCornerView.autoSetDimensions(to: cornerSize))
cropCornerView.addRedBorder()
cropView.addRedBorder()
} }
if !isCropGestureActive { if !isCropGestureActive {
@ -268,8 +337,6 @@ class ImageEditorCropViewController: OWSViewController {
} }
private func applyTransform() { private func applyTransform() {
Logger.verbose("")
let viewSize = contentView.bounds.size let viewSize = contentView.bounds.size
contentView.layer.setAffineTransform(transform.affineTransform(viewSize: viewSize)) contentView.layer.setAffineTransform(transform.affineTransform(viewSize: viewSize))
} }

@ -6,7 +6,8 @@ import UIKit
@objc @objc
public protocol ImageEditorViewDelegate: class { public protocol ImageEditorViewDelegate: class {
func imageEditor(presentFullScreenOverlay viewController: UIViewController) func imageEditor(presentFullScreenOverlay viewController: UIViewController,
withNavigation: Bool)
} }
// MARK: - // MARK: -
@ -68,7 +69,7 @@ public class ImageEditorView: UIView {
return false return false
} }
self.addSubview(canvasView) self.addSubview(canvasView)
canvasView.ows_autoPinToSuperviewEdges() canvasView.autoPinEdgesToSuperviewEdges()
self.isUserInteractionEnabled = true self.isUserInteractionEnabled = true
@ -571,7 +572,8 @@ public class ImageEditorView: UIView {
// let maxTextWidthPoints = canvasView.imageView.width() * ImageEditorTextItem.kDefaultUnitWidth // let maxTextWidthPoints = canvasView.imageView.width() * ImageEditorTextItem.kDefaultUnitWidth
let textEditor = ImageEditorTextViewController(delegate: self, textItem: textItem, maxTextWidthPoints: maxTextWidthPoints) let textEditor = ImageEditorTextViewController(delegate: self, textItem: textItem, maxTextWidthPoints: maxTextWidthPoints)
self.delegate?.imageEditor(presentFullScreenOverlay: textEditor) self.delegate?.imageEditor(presentFullScreenOverlay: textEditor,
withNavigation: true)
} }
// MARK: - Crop Tool // MARK: - Crop Tool
@ -596,7 +598,8 @@ public class ImageEditorView: UIView {
} }
let cropTool = ImageEditorCropViewController(delegate: self, model: model, srcImage: srcImage, previewImage: previewImage) let cropTool = ImageEditorCropViewController(delegate: self, model: model, srcImage: srcImage, previewImage: previewImage)
self.delegate?.imageEditor(presentFullScreenOverlay: cropTool) self.delegate?.imageEditor(presentFullScreenOverlay: cropTool,
withNavigation: false)
}} }}
// MARK: - // MARK: -

@ -1,5 +1,5 @@
// //
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// //
import UIKit import UIKit
@ -15,8 +15,18 @@ public class OWSButton: UIButton {
@objc @objc
init(block: @escaping () -> Void = { }) { init(block: @escaping () -> Void = { }) {
super.init(frame: .zero) super.init(frame: .zero)
self.block = block
addTarget(self, action: #selector(didTap), for: .touchUpInside)
}
@objc
init(title: String, block: @escaping () -> Void = { }) {
super.init(frame: .zero)
self.block = block self.block = block
self.addTarget(self, action: #selector(didTap), for: .touchUpInside) addTarget(self, action: #selector(didTap), for: .touchUpInside)
setTitle(title, for: .normal)
} }
public required init?(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {

@ -238,3 +238,17 @@ public extension CGAffineTransform {
return rotated(by: angleRadians) return rotated(by: angleRadians)
} }
} }
public extension UIBezierPath {
public func addRegion(withPoints points: [CGPoint]) {
guard let first = points.first else {
owsFailDebug("No points.")
return
}
move(to: first)
for point in points.dropFirst() {
addLine(to: point)
}
addLine(to: first)
}
}

Loading…
Cancel
Save