私の個人アプリを作る流れを全部見せます その10:UndoとRedo
今回はUndoとRedoの機能をつけます。
UndoManager
残念ながら「Swift 4プログラミング入門」にはUndoに関する説明はありません。
そこでAppleの公式ドキュメントを見てみると以下のページが参考になりそうでした。
https://developer.apple.com/documentation/foundation/undomanager/2427208-registerundo
このregisterUndo(withTarget:handler:)
を使えば実装できそうです。
今回は以下のようにsetColor
というメソッドを用意し、今の色に戻す操作をUndoManagerに登録することで実現しました。
EditorView.swift
let dotsUndoManager = UndoManager()
// 該当インデックスの色を変更するメソッド
func setColor(_ color: UIColor, atIndex index: Int) {
guard index < dots.count else { return }
let prevColor = dots[index]
// 色が現在と違う場合更新し、Undoできるようにする
if color != prevColor {
dots[index] = color
dotsUndoManager.registerUndo(withTarget: self) { $0.setColor(prevColor, atIndex: index) }
}
}
あとはツールバーのボタンタップ時にundo()
とredo()
を呼び出してあげるだけでOKです。
ボタンとメソッドをつなぐ手順は「Chapter 12 主なUIパーツ」など様々な箇所で紹介しています。
canUndo
UndoやRedoできない状態のときにボタンが押せるのは不親切です。 ボタンの状態切り替えなどに使えるように、UndoManagerにはcanUndo、canRedoというプロパティが用意されています。
以下のようにdotsDidChange
が呼ばれたタイミングで各ボタンの状態を更新するようにしました。
この書き方は「Section 3.8 クロージャー」で説明しています。
EditorView.swift
var dotsDidChange: () -> Void = {}
func addDot(_ touches: Set<UITouch>) {
// `i`を求める処理を省略
setColor(color, atIndex: i)
setNeedsDisplay()
dotsDidChange()
}
func undo() {
dotsUndoManager.undo()
setNeedsDisplay()
dotsDidChange()
}
func redo() {
dotsUndoManager.redo()
setNeedsDisplay()
dotsDidChange()
}
EditViewController.swift
editorView.dotsDidChange = {
self.undoItem.isEnabled = self.editorView.canUndo
self.redoItem.isEnabled = self.editorView.canRedo
}
これで操作が戻せるようになりました 早速試してみます。
はみ出したのでUndoします。
(この状態ではまだRedoボタンが無効です)
今度は戻しすぎたのでRedo…。
無事、円が描けました。
1ドットずつ戻す仕様なので改善の余地ありですが、そこはリリース後の課題として、このまま進めたいと思います。
この状態のプロジェクトのダウンロードはこちらから。
https://github.com/tnantoka/PixelArtPocket/releases/tag/my-app-dev-flow-10