session-ios/SignalMessaging/Views/ImageEditor/ImageEditorBrushViewController.swift

225 lines
7.2 KiB
Swift
Raw Normal View History

2019-02-28 19:13:20 +01:00
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import UIKit
@objc
public protocol ImageEditorBrushViewControllerDelegate: class {
func brushDidComplete()
}
// MARK: -
public class ImageEditorBrushViewController: OWSViewController {
private weak var delegate: ImageEditorBrushViewControllerDelegate?
private let model: ImageEditorModel
private let canvasView: ImageEditorCanvasView
2019-02-28 20:23:11 +01:00
private let paletteView: ImageEditorPaletteView
2019-02-28 19:13:20 +01:00
private var brushGestureRecognizer: ImageEditorPanGestureRecognizer?
init(delegate: ImageEditorBrushViewControllerDelegate,
2019-02-28 20:23:11 +01:00
model: ImageEditorModel,
currentColor: ImageEditorColor) {
2019-02-28 19:13:20 +01:00
self.delegate = delegate
self.model = model
self.canvasView = ImageEditorCanvasView(model: model)
2019-02-28 20:23:11 +01:00
self.paletteView = ImageEditorPaletteView(currentColor: currentColor)
2019-02-28 19:13:20 +01:00
super.init(nibName: nil, bundle: nil)
model.add(observer: self)
}
@available(*, unavailable, message: "use other init() instead.")
required public init?(coder aDecoder: NSCoder) {
notImplemented()
}
// MARK: - View Lifecycle
public override func loadView() {
self.view = UIView()
self.view.backgroundColor = .black
2019-02-28 20:23:11 +01:00
self.view.isOpaque = true
2019-02-28 19:13:20 +01:00
canvasView.configureSubviews()
self.view.addSubview(canvasView)
canvasView.autoPinEdgesToSuperviewEdges()
paletteView.delegate = self
self.view.addSubview(paletteView)
paletteView.autoVCenterInSuperview()
2019-02-28 20:23:11 +01:00
paletteView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20)
2019-02-28 19:13:20 +01:00
self.view.isUserInteractionEnabled = true
let brushGestureRecognizer = ImageEditorPanGestureRecognizer(target: self, action: #selector(handleBrushGesture(_:)))
brushGestureRecognizer.maximumNumberOfTouches = 1
brushGestureRecognizer.referenceView = canvasView.gestureReferenceView
self.view.addGestureRecognizer(brushGestureRecognizer)
self.brushGestureRecognizer = brushGestureRecognizer
updateNavigationBar()
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutSubviews()
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.view.layoutSubviews()
}
public func updateNavigationBar() {
let undoButton = navigationBarButton(imageName: "image_editor_undo",
selector: #selector(didTapUndo(sender:)))
let doneButton = navigationBarButton(imageName: "image_editor_checkmark_full",
selector: #selector(didTapDone(sender:)))
var navigationBarItems = [UIView]()
if model.canUndo() {
navigationBarItems = [undoButton, doneButton]
} else {
navigationBarItems = [doneButton]
}
updateNavigationBar(navigationBarItems: navigationBarItems)
}
// MARK: - Actions
@objc func didTapUndo(sender: UIButton) {
Logger.verbose("")
guard model.canUndo() else {
owsFailDebug("Can't undo.")
return
}
model.undo()
}
@objc func didTapDone(sender: UIButton) {
Logger.verbose("")
completeAndDismiss()
}
private func completeAndDismiss() {
self.delegate?.brushDidComplete()
self.dismiss(animated: false) {
// Do nothing.
}
}
// MARK: - Brush
// These properties are non-empty while drawing a stroke.
private var currentStroke: ImageEditorStrokeItem?
private var currentStrokeSamples = [ImageEditorStrokeItem.StrokeSample]()
@objc
public func handleBrushGesture(_ gestureRecognizer: ImageEditorPanGestureRecognizer) {
2019-02-28 19:13:20 +01:00
AssertIsOnMainThread()
let removeCurrentStroke = {
if let stroke = self.currentStroke {
self.model.remove(item: stroke)
}
self.currentStroke = nil
self.currentStrokeSamples.removeAll()
}
let tryToAppendStrokeSample = { (locationInView: CGPoint) in
2019-02-28 19:13:20 +01:00
let view = self.canvasView.gestureReferenceView
let viewBounds = view.bounds
let newSample = ImageEditorCanvasView.locationImageUnit(forLocationInView: locationInView,
viewBounds: viewBounds,
model: self.model,
transform: self.model.currentTransform())
if let prevSample = self.currentStrokeSamples.last,
prevSample == newSample {
// Ignore duplicate samples.
return
}
self.currentStrokeSamples.append(newSample)
}
2019-02-28 20:23:11 +01:00
let strokeColor = paletteView.selectedValue.color
2019-02-28 19:13:20 +01:00
// TODO: Tune stroke width.
let unitStrokeWidth = ImageEditorStrokeItem.defaultUnitStrokeWidth()
switch gestureRecognizer.state {
case .began:
removeCurrentStroke()
// Apply the location history of the gesture so that the stroke reflects
// the touch's movement before the gesture recognized.
for location in gestureRecognizer.locations {
tryToAppendStrokeSample(location)
}
let locationInView = gestureRecognizer.location(in: canvasView.gestureReferenceView)
tryToAppendStrokeSample(locationInView)
2019-02-28 19:13:20 +01:00
let stroke = ImageEditorStrokeItem(color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth)
model.append(item: stroke)
currentStroke = stroke
case .changed, .ended:
let locationInView = gestureRecognizer.location(in: canvasView.gestureReferenceView)
tryToAppendStrokeSample(locationInView)
2019-02-28 19:13:20 +01:00
guard let lastStroke = self.currentStroke else {
owsFailDebug("Missing last stroke.")
removeCurrentStroke()
return
}
// Model items are immutable; we _replace_ the
// stroke item rather than modify it.
let stroke = ImageEditorStrokeItem(itemId: lastStroke.itemId, color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth)
model.replace(item: stroke, suppressUndo: true)
if gestureRecognizer.state == .ended {
currentStroke = nil
currentStrokeSamples.removeAll()
} else {
currentStroke = stroke
}
default:
removeCurrentStroke()
}
}
}
// MARK: -
extension ImageEditorBrushViewController: ImageEditorModelObserver {
public func imageEditorModelDidChange(before: ImageEditorContents,
after: ImageEditorContents) {
updateNavigationBar()
}
public func imageEditorModelDidChange(changedItemIds: [String]) {
updateNavigationBar()
}
}
// MARK: -
extension ImageEditorBrushViewController: ImageEditorPaletteViewDelegate {
public func selectedColorDidChange() {
// TODO:
}
}