2013-02-01 13 views
5

Desidero eseguire le operazioni di annullamento sul contesto grafico nella mia app di disegno. Quando si preme Annulla, voglio passare al contesto precedente che contiene il vecchio disegno.Come eseguire l'operazione di annullamento in iOS per l'archiviazione di UIGraphicsContext?

Per esempio:

Ho un rettangolo nel contesto. Durante il trascinamento, sposto il rettangolo in una nuova posizione e lo ridisegno. Ora quando premo il pulsante Annulla, voglio spostare il rettangolo nella posizione precedente. Come posso fare questo?

Ho solo un'idea di base su NSUndoManager.

Please Help!

Grazie.

+0

stai usando drawrect. Se sì, stai usando il percorso benzier o la linea normale.Ho bisogno di un po 'di ulteriori informazioni –

+0

sto usando drawrect e linee normali, nessun percorso benzier. durante il trascinamento sposto il rettangolo in una nuova posizione e lo ridisegno. ora quando premo il pulsante Annulla voglio passare alla posizione precedente del rettangolo. –

+0

Penso che usando questa funzione puoi "UIGraphicsPopContext" – Exploring

risposta

5

La funzionalità che stai inferendo non esiste realmente. Metti diversamente "I contesti grafici non funzionano in questo modo (gratuitamente)." Potresti aver visto le funzioni UIGraphicsPushContext e UIGraphicsPopContext, ma non fanno quello di cui stai parlando qui. Le operazioni "push" e "pop" che rappresentano operano sugli aspetti non ancora sottoposti al rendering del contesto grafico: i colori di riempimento e di traccia correnti, il rettangolo di ritaglio e la matrice di trasformazione, ad esempio. Una volta qualcosa viene reso nel contesto - cioè un percorso/retto/ecc. è stato accarezzato o riempito, o un'immagine è stata composta nel contesto - è resa per sempre. L'unico modo per "tornare indietro" è in qualche modo ricreare lo stato precedente. Sfortunatamente, non esiste una magia incorporata in UIGraphicsContext che lo farà accadere per te.

Ci sono diversi modi per farlo. Come menzionato da un altro utente, puoi catturare lo stato del contesto dopo ogni azione dell'utente. In breve, ciò significa che il tuo documento "modello" è uno stack bitmap. Questo richiederà molto tempo per la memoria molto rapidamente - le bitmap sono pesanti per la memoria. Ci sono certamente delle ottimizzazioni che puoi fare con questo approccio per ottenere più chilometri da esso, come solo salvare la regione che è cambiata tra ogni frame, comprimere le bitmap, scambiarle su disco, ecc. Esistono app reali e utilizzabili nel App Store che funziona con questo approccio, ma l'approccio è intrinsecamente limitato e finirai per spendere sforzi non banali nell'ottimizzazione e nella gestione del tuo stack salvato di stati di annullamento.

Ci sono, naturalmente, altri approcci che vale la pena considerare.Il modo più semplice è di avere il modello di documento reale come una pila di piccole strutture di dati (cioè non bitmap) che descrivono le operazioni necessarie per ricreare lo stato del contesto. Quando l'utente annulla, è sufficiente rimuovere l'operazione in cima allo stack e ricreare il contesto grafico riproducendo lo stack rimanente. Questo è un approccio decente per le applicazioni di tipo "additivo" (si pensi ai "Pennelli") ma inizia a cadere anche nel semplice scenario che descrivi di spostare una forma su una tela. Inoltre definitiva soffre di alcuni degli stessi problemi approccio pila bitmap - più operazioni hai, più il tempo di ricreare lo stato, quindi si finisce inevitabilmente per fare periodicamente istantanee bitmap, ecc

per oggetto -setti scenari su tela come hai descritto tu (l'operazione 'move shape'), ci sono anche approcci multipli. Un approccio classico sarebbe quello di fare in modo che il modello del documento sia un insieme di strutture di dati più piccole e più specifiche che descrivono lo stato corrente della tela. Pensa a una classe Shape e così via. Nel caso più semplice, quando un utente aggiunge un rettangolo all'area di disegno, un'istanza Shape viene aggiunta a un array ordinato di z di forme. Ogni volta che l'elenco delle forme cambia, il contesto viene rigenerato disegnando ogni forma nell'ordine Z. Per ottenere la funzionalità di annullamento, ogni volta che si modifica la matrice di forme, si utilizza anche lo NSUndoManager per registrare una chiamata che, quando viene riprodotta, causerebbe l'operazione inversa.

Se l'operazione di forma-add si presentava così:

[shapeArray insertObject: newShape atIndex: 5]; 

poi si sarebbe, allo stesso tempo, farlo con il NSUndoManager:

[[undoManager prepareInvocationWithTarget: shapeArray] removeObjectAtIndex: 5]; 

Quando l'utente fa clic undo, il NSUndoManager riproduce tale chiamata e lo shapeArray viene restituito come stato precedente. Questo è il classico motivo NSUndoManager. Funziona bene, ma ha anche alcuni svantaggi. Ad esempio, non è necessariamente semplice mantenere lo stack di annullamento attraverso le terminazioni delle app. Poiché le terminazioni delle app sono comuni su iOS, e in genere gli utenti si aspettano che un'app ripristini perfettamente lo stato tra le terminazioni, un approccio in cui lo stack di annullamento non sopravviverà alle terminazioni delle app potrebbe non essere di partenza, a seconda delle esigenze. Esistono altri approcci più complessi, ma sono principalmente variazioni su uno di questi temi. Un classico che vale la pena leggere è lo Command Pattern dello Gang of Four book, Design Patterns.

Indipendentemente dall'approccio scelto, questo tipo di applicazione sarà una sfida da sviluppare. Questa funzionalità di annullamento grafico non è semplicemente integrata in UIGraphicsContext per "gratuito". Hai chiesto più volte nei commenti per un esempio. Mi dispiace dirlo, ma questo è un concetto abbastanza complesso che è improbabile che qualcuno possa fornire un esempio funzionante entro i limiti di una risposta StackOverflow. Speriamo che queste idee e questi suggerimenti siano utili. Esistono sicuramente anche un numero illimitato di applicazioni di disegno open source da cui puoi trarre ispirazione (anche se non sono personalmente a conoscenza di alcuna applicazione di disegno open source per iOS.)

2

UIGraphicsContext non ha il proprio stack di annullamento. Devi archiviare ogni elemento di ciò che stai disegnando in una pila e rimuovere e aggiungere elementi da quella pila per annullare e ripetere. La classe NSUndoManager può aiutarti a gestire la logica per le operazioni di annullamento e ripetizione, ma è tua responsabilità scrivere il codice che salva le azioni di disegno in uno stack e quindi legge da esso per ricreare il disegno in -drawRect:.

+0

Potete fornirmi un esempio di esempio Per favore! –

1

Prima impostare l'oggetto undomanager e inizializzarlo per l'utilizzo.

NSUndoManager *undoObject; 
undoObject =[[NSUndoManager alloc] init]; 

registrare l'oggetto di annullamento, con la funzione di destinazione per il salvataggio del contesto uigraphics, ogni volta che il contesto è cambiato.

[[undoObject prepareWithInvocationTarget:self] performUndoWithObject:currentContext withLastpoint:lastPoint andFirstPoint:firstPoint]; 

Scrivi la definizione della funzione definita dall'utente

-(void)performUndoWithObject:(CGContextRef)context withLastpoint:(CGPoint)previousLastPoint andFirstPoint:(CGPoint)previousFirstPoint 
{ 
    // necessary steps for undoing operation 
} 

Specificare azione quando un pulsante Annulla viene cliccato.

-(void)undoButtonClicked 
{ 
    if([undoObject canUndo]) 
    { 
     [undoObject undo]; 
    } 
} 
1

Come eseguire l'operazione di annullamento e ripetizione nell'obiettivo c per memorizzare l'array di valori in iOS. e ho creato la piccola demo in Undo e Redo Button funzionano con valori stringa sostituiti. creare le due NSMutableArray in ViewController.h file

@interface ViewController : UIViewController<UITextFieldDelegate> 
{ 
    NSMutableArray *arrUndo, *arrRedo; 
} 
@property (weak, nonatomic) IBOutlet UIButton *btnUndo; 
@property (weak, nonatomic) IBOutlet UIButton *btnRedo; 
@property (weak, nonatomic) IBOutlet UITextField *txtEnterValue; 
@property (weak, nonatomic) IBOutlet UILabel *lblShowUndoOrRedoValue; 
@property (weak, nonatomic) IBOutlet UIButton *btnOK; 
- (IBAction)btnUndo:(id)sender; 
- (IBAction)btnRedo:(id)sender; 
- (IBAction)btnOK:(id)sender; 
@end 

implementare i file della ViewController.m per aggiungere il metodo di azione di due tasti in annullare o ripetere.

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.txtEnterValue.delegate = self; 
    arrUndo = [[NSMutableArray alloc] init]; 
    arrRedo = [[NSMutableArray alloc] init]; 
} 
-(BOOL)canBecomeFirstResponder { 
    return YES; 
} 
- (IBAction)btnOK:(id)sender { 

    NSString *str = [NSString stringWithFormat:@"%@", [self.txtEnterValue.text description]]; 
    self.txtEnterValue.text = nil; 
    self.lblShowUndoOrRedoValue.text = str; 
    [arrUndo addObject:str]; 
    NSLog(@"Text Value : %@", [arrUndo description]); 
} 
- (IBAction)btnUndo:(id)sender { 

    if ([arrUndo lastObject] == nil) { 
     NSLog(@"EMPTY"); 
    } 
    else 
    { 
    [arrRedo addObject:[arrUndo lastObject]]; 
    NSLog(@"ArrAdd %@", [arrUndo description]); 

    [arrUndo removeLastObject]; 
    for (NSObject *obj in arrUndo) { 
     if ([arrUndo lastObject] == obj) { 

      self.lblShowUndoOrRedoValue.text = obj; 
     } 
    } 
    NSLog(@"ArrText %@", [arrUndo description]); 
    } 
} 
- (IBAction)btnRedo:(id)sender { 

    if ([arrRedo lastObject] == nil) { 
     NSLog(@"EMPTY"); 
    } 
    else 
    { 
    [arrUndo addObject:[arrRedo lastObject]]; 
    for (NSObject *obj in arrRedo) { 
     if ([arrRedo lastObject] == obj) { 

      self.lblShowUndoOrRedoValue.text = obj; 

     } 
    } 
    } 
    [arrRedo removeLastObject]; 
}