私が取り組んでいるアプリでは、ユーザーがいくつかのアセットを管理できます。ユーザーは、画面上でアセットを作成/削除/編集/分割/移動できます。ユーザーは、これらすべてのステップを元に戻すことができる必要があります。
アセットはコア データで管理されます (はい、undoManager
インスタンス化されます)。
これらのアクションごとに、次のペアで元に戻すグループを作成します。
beginUndoGrouping ... endUndoGrouping
Here's a simple example (sequence 1):
// SPLIT
- (void) menuSplitPiece: (id) sender
{
[self.managedObjectContext.undoManager beginUndoGrouping];
[self.managedObjectContext.undoManager setActionName:@"Split"];
//... do the split
[self.managedObjectContext.undoManager endUndoGrouping];
// if the user cancels the split action, call [self.managedObjectContext.undoManager undo] here;
}
edit についても同じことを行います。ユーザーが編集をキャンセルした場合は、 の直後に undo を呼び出しますendUndoGrouping
。
1 つの例外を除いて、すべてが正常に機能します。私が作成したグループの他に、Core Data によって作成された、私が制御できない他のグループがあります。これが私が意味することです:
次のように NSUndoManagerDidCloseUndoGroupNotification 通知を受け取るように登録しました。
- (void) registerUndoListener
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didCloseUndoGroup:)
name:NSUndoManagerDidCloseUndoGroupNotification object:nil];
...}
これらの通知を使用して元に戻すボタンを更新し、何らかのアクションの結果として元に戻されているアクションの名前を表示します: Undo Split など
それでも、didCloseUndoGroup
上記のアクションごとに 2 回呼び出されたり、通知されたりします (例: endUndoGrouping の後のセクション 1):
最初の通知の時点では、self.managedObjectContext.undoManager.undoActionName
私が設定した元に戻すアクションの名前が含まれていますが、2 回目undoActionName
は空の文字列です。
回避策として、名前が空の操作を単純に元に戻して (それらが私のものではなく、必要がないと仮定して)、何か不足していないかどうかを確認しようとしました。
さて、didCloseUndoGroup
こんな感じ
- (void) didCloseUndoGroup: (NSNotification *) notification
{
...
if ([self.managedObjectContext.undoManager.undoActionName isEqualToString:@""]){
[self.managedObjectContext.undoManager undo];
}
[self refreshUndoButton]; // this method displays the name of the undo action on the button
...
}
そして、魔法のように機能します。「元に戻す」を使用して、任意のコマンド、任意の数のレイヤーを元に戻すことができます。しかし、これはうまくいくはずの方法ではありません...
その前に私が試した他のいくつかのこと:
- [self.managedObjectContext processPendingChanges] グループを開く前に。まだ2つの通知を送信していました。
- 私が試した別のことは、disableUndoRegistration / enableUndoRegistration でした。これは例外を生成しました:「無効な状態です。ネストされた元に戻すグループが多すぎる状態で元に戻すが呼び出されました」
上記のどれも、前に述べた謎のグループを「分離」するのに役立ちませんでした。
NSUndoManagerDidCloseUndoGroupNotification 通知を 2 回受け取るべきではありません。または、私はすべきですか?この状況に対処するためのより良い方法はありますか?
更新 これが最終的に機能したものです。以前は、通知を受け取るとすぐに名前のないグループを自動的に取り消していました。これが問題の原因です。ここで、ターゲット グループに到達するまですべてを元に戻し、その後、そのグループの最後の元に戻します。
「undoManagerHelper」は、スタックにプッシュされるコマンドごとに一意の ID を生成する単なるスタック管理システムです。この一意の ID を使用して、グループに名前を付けます。
- (BOOL) undoLastAction
{
NSString *lastActionID = [self.undoManagerHelper pop]; // the command I'm looking for
if (lastActionID == nil) return false;
//... undo until there is nothing to undo or self.managedObjectContext.undoManager.undoActionName equals lastActionID
//the actual undo here
if ([currentActionID isEqualToString: lastActionID] && [self.managedObjectContext.undoManager canUndo]){
[self.managedObjectContext.undoManager undo];
}
return true;
}
- (void) beginUndoGroupingWithName: (NSString *) name
{
[self.managedObjectContext processPendingChanges];
[self.managedObjectContext.undoManager beginUndoGrouping];
NSString *actionID = [self.undoManagerHelper push: name];
[self.managedObjectContext.undoManager setActionName:actionID];
}
- (void) closeLastUndoGrouping
{
[self.managedObjectContext.undoManager endUndoGrouping];
[self.managedObjectContext processPendingChanges];
}