Tusker/Tusker/Screens/Compose/ComposeDrawingViewControlle...

170 lines
6.8 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.drawingPolicy = .anyInput
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)
// todo: should the PKToolPicker be owned by this VC or something else?
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)
}
}