絵を描くアプリで線はundo,redoできるのですがimageviewがundo,redoできません。

投稿者: Anonymous

スクロールビューの下にimageViewを配置してあります。

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var canvasView: UIImageView!
var saveImageArray = [UIImage]()   //Undo/Redo用にUIImage保存用

// タップされた座標にflowerを追加する(buttonをクリックした時の処理)
let flower = UIImageView(image: UIImage(named: "flower"))
flower.center = (sender as AnyObject).location(in: self.view)

**canvasViewの拡大を可能にしたい為、addSubView(flower)にしたら簡単にタップ位置にimageの大きさで表示されかつ拡大出来たので良かったのですが、結果としてSubViewとcanvasViewでは格納場所が違うようで以降のundo処理が出来ない状態です。
描いた線,画像の拡大,縮小が出来てundo,redo処理が出来る様に考えています。
現在の記述で描いた線,画像の拡大,縮小は出来るのだが,画像のredo,undoが出来ないです。
(この処理の中には線の記述はないです)
canvasView.addSubview(flower)

//配列にcanvasView.imageを保存
currentDrawNumber += 1
saveImageArray.append(canvasView.image!) 

//保存している直前のimageに置き換える (undoボタンをクリツクした時の処理)
@IBAction func pressUndoButton(_ sender: Any) {
    if currentDrawNumber <= 0 {return}      

    self.canvasView.image = saveImageArray[currentDrawNumber - 1]   
    currentDrawNumber -= 1
}

この処理で画像を書き換えられるはずなのですが、上手くいきません。
教えて戴けませんか ?

解決

はじめまして。@OOPerさんが仰るとおり、クラス定義からクラス定義終了まで全てのコードが記述されていないので確実とは言えませんが、上のソースには

タップされた座標にflowerを追加する(buttonをクリックした時の処理)

で、flowerという画像ファイルをcanvasViewaddSubviewしています。
つまり、canvasViewにオーバーレイしたViewを作成し、そこにflowerという名前の画像を表示している様に見えます。

しかし、

配列にcanvasView.imageを保存

saveImageArrayにバックアップしているcanvasView.imageは、上に記述されたソース断片ではなにもセットしていないので、空のイメージの可能性が高く
更に

保存している直前のimageに置き換える (undoボタンをクリツクした時の処理)

以降で保存された画像をセットし直しているのは、canvasViewimageプロパティを操作しているように見えます。
そうすると、

—- flowerという最初にセットした画像 ——————— = addされたSubview
—– バックアップした空のimageをUndoで再セットした画像 —– = canvasView

と言うことが起きているのではないでしょうか
このため、undoボタンをクリックしたときの処理は実際に行われていても、
addされたSubviewの下に隠れて見えていない事が予想されます。
なので、

a. タップされた座標に〜 の部分で、addSubviewせず、canvasViewimageに画像をセットする

let flower = UIImage(named: "flower")
// センタリング処理省略
canvasView.image = flower

b. Undoアクション の部分で、addSubviewしたviewimageに画像をセットする(バックアップするイメージの取得元もaddSubviewしたviewimageプロパティにする)

// nはaddSubviewしたflowerのsubViewsから取得される順番
self.canvasView.subViews[n].image = saveImageArray[currentDrawNumber - 1] 

または

// flowerが例示されたソースの通り、クラスのメンバー変数であれば
// イメージのバックアップも書き戻しもflowerに対して行えばよい
    // 配列にcanvasView.imageを保存の部分
saveImageArray.append(flower.image!)
    // 保存している直前のimageに置き換えるの部分
flower.image = saveImageArray[currentDrawNumber - 1]

ただし、この場合、let flowerは適切な変数名ではないので、クラス内全体を通してflowerこそが実質的にキャンバスであると言うことが解る変数名に書き替えた方が間違いが少なくなると思います

 以上より、a,bで例示したどちらか片方に統一すれば良いような気がします。
bの例示は2つ示しましたが、どちらか片方だけが必要で、両方の修正を行うと、また挙動がわからなくなります。

 ただしbの解決方法は、「ではIBOUtletで宣言しているcanvasViewというImageViewは何のために存在しているか?」がわからない、必要のないオブジェクトになってしまうので、勝手な予測ながら「よくわからないけど、こうすれば動いた」以上の意味は無いのでは無いかと思います。
 このため、a.で示した、IBOutlet宣言したcanvasViewに極力余計なViewをaddしない方針でプログラムの修正をし、どうしても質問に記述されていない部分が理由でaddSubviewする必要でない限り、b.の方針でプログラムを修正すべきではない様に思えます。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *