175 lines
7.0 KiB
Swift
175 lines
7.0 KiB
Swift
|
//
|
||
|
// ComposeDrawingViewController.swift
|
||
|
// Tusker
|
||
|
//
|
||
|
// Created by Shadowfacts on 5/7/20.
|
||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||
|
//
|
||
|
|
||
|
import UIKit
|
||
|
import PencilKit
|
||
|
|
||
|
protocol ComposeDrawingViewControllerDelegate: class {
|
||
|
func composeDrawingViewControllerClose(_ drawingController: ComposeDrawingViewController)
|
||
|
func composeDrawingViewController(_ drawingController: ComposeDrawingViewController, saveDrawing drawing: PKDrawing)
|
||
|
}
|
||
|
|
||
|
class ComposeDrawingViewController: UIViewController {
|
||
|
|
||
|
weak var delegate: ComposeDrawingViewControllerDelegate?
|
||
|
|
||
|
private(set) var canvasView: PKCanvasView!
|
||
|
private(set) var cancelBarButtonItem: UIBarButtonItem!
|
||
|
private(set) var undoBarButtonItem: UIBarButtonItem!
|
||
|
private(set) var redoBarButtonItem: UIBarButtonItem!
|
||
|
|
||
|
private var initialDrawing: PKDrawing?
|
||
|
|
||
|
init() {
|
||
|
super.init(nibName: nil, bundle: nil)
|
||
|
}
|
||
|
|
||
|
convenience init(editing initialDrawing: PKDrawing) {
|
||
|
self.init()
|
||
|
|
||
|
self.initialDrawing = initialDrawing
|
||
|
}
|
||
|
|
||
|
required init?(coder: NSCoder) {
|
||
|
fatalError("init(coder:) has not been implemented")
|
||
|
}
|
||
|
|
||
|
override func viewDidLoad() {
|
||
|
super.viewDidLoad()
|
||
|
|
||
|
overrideUserInterfaceStyle = .light
|
||
|
|
||
|
navigationItem.title = NSLocalizedString("Draw", comment: "compose drawing screen title")
|
||
|
cancelBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelPressed))
|
||
|
undoBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "arrow.uturn.left.circle"), style: .plain, target: self, action: #selector(undoPressed))
|
||
|
undoBarButtonItem.isEnabled = false
|
||
|
redoBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "arrow.uturn.right.circle"), style: .plain, target: self, action: #selector(redoPressed))
|
||
|
redoBarButtonItem.isEnabled = false
|
||
|
navigationItem.leftBarButtonItems = [cancelBarButtonItem]
|
||
|
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(savePressed))
|
||
|
|
||
|
canvasView = PKCanvasView()
|
||
|
if let initialDrawing = initialDrawing {
|
||
|
canvasView.drawing = initialDrawing
|
||
|
}
|
||
|
canvasView.delegate = self
|
||
|
canvasView.allowsFingerDrawing = true
|
||
|
canvasView.minimumZoomScale = 0.5
|
||
|
canvasView.maximumZoomScale = 2
|
||
|
canvasView.backgroundColor = .systemBackground
|
||
|
canvasView.translatesAutoresizingMaskIntoConstraints = false
|
||
|
view.addSubview(canvasView)
|
||
|
NSLayoutConstraint.activate([
|
||
|
canvasView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||
|
canvasView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||
|
canvasView.topAnchor.constraint(equalTo: view.topAnchor),
|
||
|
canvasView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||
|
])
|
||
|
}
|
||
|
|
||
|
override func viewWillAppear(_ animated: Bool) {
|
||
|
super.viewWillAppear(animated)
|
||
|
|
||
|
if let window = parent?.view.window, let toolPicker = PKToolPicker.shared(for: window) {
|
||
|
toolPicker.setVisible(true, forFirstResponder: canvasView)
|
||
|
toolPicker.addObserver(canvasView)
|
||
|
toolPicker.addObserver(self)
|
||
|
|
||
|
updateLayout(for: toolPicker)
|
||
|
canvasView.becomeFirstResponder()
|
||
|
|
||
|
// wait until the next run loop iteration so that the canvas view has become first responder and it's undo manager exists
|
||
|
DispatchQueue.main.async {
|
||
|
NotificationCenter.default.addObserver(self, selector: #selector(self.updateUndoRedoButtonState), name: .NSUndoManagerDidUndoChange, object: self.undoManager!)
|
||
|
NotificationCenter.default.addObserver(self, selector: #selector(self.updateUndoRedoButtonState), name: .NSUndoManagerDidRedoChange, object: self.undoManager!)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func updateLayout(for toolPicker: PKToolPicker) {
|
||
|
let obscuredFrame = toolPicker.frameObscured(in: view)
|
||
|
|
||
|
// if there is no obscured frame, the tool picker is floating
|
||
|
// which means we don't need to change inset or show undo/redo
|
||
|
if obscuredFrame.isNull {
|
||
|
navigationItem.leftBarButtonItems = [cancelBarButtonItem]
|
||
|
canvasView.contentInset = .zero
|
||
|
canvasView.automaticallyAdjustsScrollIndicatorInsets = true
|
||
|
} else {
|
||
|
navigationItem.leftBarButtonItems = [cancelBarButtonItem, undoBarButtonItem, redoBarButtonItem]
|
||
|
canvasView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: obscuredFrame.height, right: 0)
|
||
|
canvasView.automaticallyAdjustsScrollIndicatorInsets = false
|
||
|
// we don't care about the botom safe area inset becaused the tool picker obscured frame includes the safe area
|
||
|
canvasView.scrollIndicatorInsets = UIEdgeInsets(top: view.safeAreaInsets.top, left: view.safeAreaInsets.left, bottom: obscuredFrame.height, right: view.safeAreaInsets.right)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func updateContentSizeForDrawing() {
|
||
|
let overscrollAmount: CGFloat = 100
|
||
|
let drawingBounds = canvasView.drawing.bounds
|
||
|
let newSize: CGSize
|
||
|
if !drawingBounds.isNull {
|
||
|
let width = max(canvasView.bounds.width, (drawingBounds.maxX + overscrollAmount) * canvasView.zoomScale)
|
||
|
let height = max(canvasView.bounds.height, (drawingBounds.maxY + overscrollAmount) * canvasView.zoomScale)
|
||
|
newSize = CGSize(width: width, height: height)
|
||
|
} else {
|
||
|
newSize = canvasView.bounds.size
|
||
|
}
|
||
|
canvasView.contentSize = newSize
|
||
|
}
|
||
|
|
||
|
@objc func updateUndoRedoButtonState() {
|
||
|
undoBarButtonItem.isEnabled = undoManager!.canUndo
|
||
|
redoBarButtonItem.isEnabled = undoManager!.canRedo
|
||
|
}
|
||
|
|
||
|
// MARK: Interaction
|
||
|
|
||
|
@objc func cancelPressed() {
|
||
|
delegate?.composeDrawingViewControllerClose(self)
|
||
|
}
|
||
|
|
||
|
@objc func savePressed() {
|
||
|
delegate?.composeDrawingViewController(self, saveDrawing: canvasView.drawing)
|
||
|
}
|
||
|
|
||
|
@objc func undoPressed() {
|
||
|
undoManager!.undo()
|
||
|
}
|
||
|
|
||
|
@objc func redoPressed() {
|
||
|
undoManager!.redo()
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
extension ComposeDrawingViewController: PKCanvasViewDelegate {
|
||
|
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
|
||
|
if !undoManager!.isUndoing && !undoManager!.isRedoing {
|
||
|
updateUndoRedoButtonState()
|
||
|
}
|
||
|
updateContentSizeForDrawing()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension ComposeDrawingViewController: PKToolPickerObserver {
|
||
|
func toolPickerFramesObscuredDidChange(_ toolPicker: PKToolPicker) {
|
||
|
updateLayout(for: toolPicker)
|
||
|
}
|
||
|
|
||
|
func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker) {
|
||
|
updateLayout(for: toolPicker)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension ComposeDrawingViewController: UIGestureRecognizerDelegate {
|
||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||
|
return otherGestureRecognizer == canvasView.drawingGestureRecognizer
|
||
|
}
|
||
|
}
|