2012-06-13 4 views
5

Sto usando pan, pizzico e ruotare UIGestureRecognizers per consentire all'utente di inserire determinati elementi dell'interfaccia utente esattamente dove li desiderano. Usando il codice da qui http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more (o un codice simile da qui http://mobile.tutsplus.com/tutorials/iphone/uigesturerecognizer/) entrambi mi forniscono ciò di cui ho bisogno affinché l'utente inserisca questi elementi dell'interfaccia utente come preferiscono.Caricamento di UIView e centro dalle impostazioni fornisce una diversa posizione

Quando l'utente esce modalità "layout dell'interfaccia utente", risparmio del trasformare e centro come modo UIView:

NSString *transformString = NSStringFromCGAffineTransform(self.transform); 
[[NSUserDefaults standardUserDefaults] setObject:transformString forKey:@"UItransform", suffix]]; 

NSString *centerString = NSStringFromCGPoint(self.center); 
[[NSUserDefaults standardUserDefaults] setObject:centerString forKey:@"UIcenter"]; 

Quando ricarichiamo l'app, ho letto del trasformare e centro come modo UIView:

NSString *centerString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UIcenter"]; 
if(centerString != nil) 
    self.center = CGPointFromString(centerString); 

NSString *transformString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UItransform"]; 

if(transformString != nil) 
    self.transform = CGAffineTransformFromString(transformString); 

E l'UIView finisce ruotato e ridimensionato correttamente, ma nel posto sbagliato. Inoltre, entrando di nuovo in modalità "Layout UI", non riesco sempre ad afferrare la vista con i vari gesti (come se la vista visualizzata non fosse la vista come intesa dal riconoscitore di gesti?)

Ho anche un pulsante di ripristino che imposta l'identità transform di UIView e il suo center in qualsiasi momento quando viene caricato dal NIB. Ma dopo aver caricato il centro UIView modificato e trasformato, anche il reset non funziona. La posizione di UIView è errata.

mio primo pensiero era che, poiché tali esempi di codice gesto alterano center, che devono essere rotazioni avvengono centri intorno diverse (supponendo una sequenza imprevedibile di movimenti, rotazioni e bilancia). Poiché non desidero salvare l'intera sequenza di modifiche (sebbene ciò possa essere utile se si desidera disporre di alcune funzionalità di annullamento nella modalità di layout), ho modificato il gestore di UIPanGestureRecognizer per utilizzare la trasformazione per spostarlo. Una volta che ho funzionato, ho pensato che salvare la trasformazione mi avrebbe portato la posizione corrente e l'orientamento, indipendentemente dall'ordine in cui sono avvenute le cose. Ma nessuna fortuna. Ho ancora una posizione strana in questo modo.

Quindi sono in perdita. Se un UIView è stato spostato e ruotato in una nuova posizione, come posso salvare quella posizione e l'orientamento in un modo in cui posso caricarlo in seguito e riportare l'UIView dove dovrebbe essere?

Mi scuso in anticipo se non ho taggato questo diritto o non lo ho disposto correttamente o ho commesso qualche altro peccato di stackoverflow. È la prima volta che scrivo qui.

EDIT

sto cercando le due proposte finora. Penso che siano effettivamente la stessa cosa (si suggerisce di salvare la cornice e l'altra suggerisce di salvare l'origine, che penso sia il frame.origin).

Così ora il salvataggio/caricamento dal codice prefs include quanto segue.

Save:

NSString *originString = NSStringFromCGPoint(self.frame.origin); 
[[NSUserDefaults standardUserDefaults] setObject:originString forKey:@"UIorigin"]; 

carico (prima caricare la trasformazione):

NSString *originString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UIorigin"]; 
if(originString) { 
    CGPoint origin = CGPointFromString(originString); 
    self.frame = CGRectMake(origin.x, origin.y, self.frame.size.width, self.frame.size.height); 
} 

ottengo lo stesso (o simile - è difficile dire) risultato. Infatti, ho aggiunto un pulsante per ricaricare solo i prefs, e una volta ruotata la vista, quel pulsante "ricarica" ​​sposterà ripetutamente l'UIView di qualche offset (come se il frame o la trasformazione fossero relativi a se stesso - che sono sicuro è un indizio, ma non sono sicuro di cosa stia puntando).

EDIT # 2

Questo mi rende chiedo a seconda del punto di vista di frame. Da Apple http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW6 (sottolineatura mia):

Il valore nella proprietà centro è sempre valida, anche se sono stati aggiunti fattori di scala o di rotazione per trasformare della vista. Lo stesso non vale per il valore nella proprietà frame, che è considerato non valido se la trasformazione della vista non è uguale alla trasformazione dell'identità.

EDIT # 3

Okay, quando sto caricando le preferenze in, tutto sembra a posto. Il rect bounds del pannello dell'interfaccia utente è {{0, 0}, {506, 254}}. Alla fine del metodo viewDidLoad di VC, tutto sembra ancora a posto. Ma nel momento in cui le cose vengono effettivamente visualizzate, bounds è un'altra cosa. Ad esempio: {{0, 0}, {488.321, 435.981}} (che assomiglia a quanto è grande nella sua superview una volta ruotato e ridimensionato). Se resetto i limiti a ciò che dovrebbe essere, si rimette in posizione.

È abbastanza facile reimpostare i limiti su ciò che dovrebbero essere programmaticamente, ma in realtà non sono sicuro di quando farlo! Avrei pensato di farlo alla fine di viewDidLoad, ma bounds è ancora corretto a quel punto.

EDIT # 4

ho provato catturando self.bounds a initWithCoder (come è proveniente da un NIB), e poi in layoutSubviews, reset self.bounds a quello catturato CGRect. E quello funziona.

Ma sembra orribilmente hacky e irto di pericoli. Questo non può essere davvero il modo giusto per farlo. (Può?) La risposta di skram qui sotto sembra così semplice, ma non funziona per me quando l'app si ricarica.

+0

Grande prima domanda! Benvenuto in SO! – jrturton

+0

Grazie, jrturton :) SO è davvero qualcos'altro. Il fatto di avere due risposte in pochi minuti (anche se non ci sono ancora riuscito) è semplicemente spettacolare! – sbkp

+0

Presumibilmente stai usando una combinazione di trasformazioni di scala e rotazione per ruotare/zoom e semplicemente spostando il centro per la panoramica? – jrturton

risposta

0

Grazie a tutti coloro che hanno fornito le risposte! La somma di tutti mi ha portato a quanto segue:

Il problema sembra essere stato il ripristino del CGRect bounds dopo aver caricato la trasformazione dalle preferenze all'avvio, ma non durante l'aggiornamento delle preferenze durante la modifica in tempo reale.

Penso che ci siano due soluzioni. Uno sarebbe quello di caricare prima le preferenze da layoutSubviews anziché da viewDidLoad. Nulla sembra accadere a bounds dopo che è stato chiamato layoutSubviews.

Per altri motivi nella mia app, tuttavia, è più comodo caricare le preferenze dal controller della vista viewDidLoad. Quindi la soluzione che sto usando è questa:

// UserTransformableView.h 
@interface UserTransformableView : UIView { 
    CGRect defaultBounds; 
} 

// UserTransformableView.m 
- (id)initWithCoder:(NSCoder *)aDecoder { 
    self = [super initWithCoder:aDecoder]; 
    if(self) { 
     defaultBounds = self.bounds; 
    } 
    return self; 
} 

- (void)layoutSubviews { 
    self.bounds = defaultBounds; 
} 
2

Si salva anche la proprietà frame. È possibile utilizzare NSStringFromCGRect() e CGRectFromString().

Durante il caricamento, impostare la cornice, quindi applicare la trasformazione. Ecco come lo faccio in una delle mie app.

Spero che questo aiuti.

UPDATE: Nel mio caso, ho Draggable UIViews a cui è possibile applicare la rotazione e il ridimensionamento. Io uso NSCoding per salvare e caricare i miei oggetti, esempio di seguito.

//encoding 
.... 
[coder encodeCGRect:self.frame forKey:@"rect"]; 
// you can save with NSStringFromCGRect(self.frame); 
[coder encodeObject:NSStringFromCGAffineTransform(self.transform) forKey:@"savedTransform"]; 

//init-coder 
CGRect frame = [coder decodeCGRectForKey:@"rect"]; 
// you can use frame = CGRectFromString(/*load string*/); 
[self setFrame:frame]; 
self.transform = CGAffineTransformFromString([coder decodeObjectForKey:@"savedTransform"]); 

Ciò che fa è salvare il mio frame e trasformarlo e caricarli quando necessario. Lo stesso metodo può essere applicato con NSStringFromCGRect() e CGRectFromString().

UPDATE 2: Nel tuo caso. Si potrebbe fare qualcosa di simile ..

[self setFrame:CGRectFromString([[NSUserDefaults standardUserDefaults] valueForKey:@"UIFrame"])]; 
self.transform = CGAffineTransformFromString([[NSUserDefaults standardUserDefaults] valueForKey:@"transform"]); 

Supponendo che si sta risparmiando per NSUserDefaults con UIFrame e transform chiavi.

+0

Grazie, skram. Penso che il tuo suggerimento sia essenzialmente lo stesso di Dianz, vero? Ha suggerito di salvare l'origine della vista (presumibilmente quella del frame). Sia con il suo suggerimento o il tuo (come ho capito), ottengo ancora un risultato stupendo. Se non è chiedere troppo, potresti pubblicare uno snippet di codice per spingermi nella giusta direzione? – sbkp

+0

aggiunto un esempio su come io uso lo stesso approccio. – skram

+0

Grazie! Sto ancora ottenendo gli stessi risultati. Ma fammi controllare qualcosa qui. Sto usando NSUserDefaults senza NSCoding per questo. Quindi il mio codice di salvataggio (ad esempio) differisce dal tuo, in questo modo: '[[NSUserDefaults standardUserDefaults] NSStringFromCGRect (self.frame) forKey: @" UIframe "]; [[NSUserDefaults standardUserDefaults] NSStringFromCGAffineTransform (self.transform) forKey: @ "UItransform"]; ' Questo sembra avere lo stesso risultato di quello che stai facendo (no?)? – sbkp

1

Si dovrebbe anche salvare la posizione della UIView,

CGPoint position = CGPointMake(self.view.origin.x, self.view.origin.y) 

NSString _position = NSStringFromCGPoint(position); 

// Do the saving 
+0

Grazie, dianz. Sfortunatamente, non l'ho fatto funzionare. Ho messo la mia interpretazione di ciò che intendevi nel mio post (dopo "EDIT"). Saresti così gentile da guardare se ho capito bene? (Salvando self.frame.origin) o se non è chiedere troppo, potresti pubblicare uno snippet di codice per spingermi nella giusta direzione? – sbkp

+0

Questo non viene nemmeno compilato e non è diverso dalla proprietà del centro. – jrturton

0

non sono sicuro di tutto ciò che sta succedendo, ma qui ci sono alcune idee che possono aiutare.

1- La soluzione di skram sembra plausibile, ma è lo bounds che si desidera salvare, non lo frame. (.. Si noti che, se non c'è stata la rotazione, il centro e limiti definiscono la cornice Così, impostando i due è la stessa impostazione del telaio) Dalla, il View Programming Guide for IOS si è collegato a:

Importante Se un la proprietà di trasformazione della vista non è la trasformazione di identità , il valore della proprietà della struttura di quella vista non è definito e deve essere ignorato. Quando si applicano le trasformazioni a una vista, è necessario utilizzare i limiti della vista e le proprietà del centro della vista per ottenere la dimensione e la posizione della vista . I rettangoli dei riquadri di qualsiasi subview sono ancora validi perché sono relativi ai limiti della vista.

2- Un'altra idea. Quando si ricarica l'app, è possibile provare quanto segue:

  • Prima di tutto, impostare la trasformazione della vista sulla trasformazione dell'identità.
  • Quindi, imposta i limiti e il centro della vista ai valori salvati.
  • Infine, imposta la trasformazione della vista sulla trasformazione salvata.

A seconda di dove si sta riavviando la tua app, potrebbe ricominciare il backup con parte della vecchia geometria. Veramente non penso che questo cambierà nulla, ma è abbastanza facile provarlo.
Aggiornamento: Dopo alcuni test, sembra davvero che questo non avrebbe alcun effetto. Cambiare la trasformazione non sembra cambiare i limiti o il centro (anche se cambia il fotogramma.)

3- Infine, è possibile risparmiare alcuni problemi riscrivendo il riconoscimento pizzico gesto per operare su bounds anziché su transform. (Ancora, usare bounds, non frame, perché una rotazione precedente avrebbe potuto rendere non valido lo frame). In questo modo, la trasformazione viene utilizzata solo per le rotazioni, che, penso, non possono essere eseguite in altro modo senza ridisegno.

Dalla stessa guida, la raccomandazione di Apple è:

In genere modifica la proprietà trasformata di vista quando si desidera implementare animazioni. Ad esempio, è possibile utilizzare questa proprietà su creando un'animazione della vista ruotando attorno al suo punto centrale. L'utente non utilizzerà questa proprietà per apportare modifiche permanenti alla vista, , ad esempio modificando la sua posizione o la dimensione di una vista all'interno dello spazio di coordinate della superview. Per quel tipo di modifica, dovresti invece modificare il rettangolo della cornice della tua vista.

2

Ho problemi a riprodurre il problema.Ho usato il seguente codice che esegue le seguenti operazioni:

  • Aggiunge una vista
  • mosse cambiando centro
  • l'adatta con una trasformazione
  • Ruota con un'altra transform, concatenato sulla prima
  • Salva il trasformare e centrale per le stringhe
  • aggiunge un altro vista e si applica il centro e trasformare dalla stringa

Questo si traduce in due punti di vista esattamente nello stesso posto e la posizione:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 
    view1.layer.borderWidth = 5.0; 
    view1.layer.borderColor = [UIColor blackColor].CGColor; 
    [self.view addSubview:view1]; 
    view1.center = CGPointMake(150,150); 
    view1.transform = CGAffineTransformMakeScale(1.3, 1.3); 
    view1.transform = CGAffineTransformRotate(view1.transform, 0.5); 

    NSString *savedCentre = NSStringFromCGPoint(view1.center); 
    NSString *savedTransform = NSStringFromCGAffineTransform(view1.transform); 

    UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 
    view2.layer.borderWidth = 2.0; 
    view2.layer.borderColor = [UIColor greenColor].CGColor; 
    [self.view addSubview:view2]; 
    view2.center = CGPointFromString(savedCentre); 
    view2.transform = CGAffineTransformFromString(savedTransform); 

} 

Dare:

enter image description here

Questa lega con quello che ci si aspetterebbe dalla documentazione, in quanto tutte le trasformazioni accade intorno al punto centrale e quindi non è mai interessato. L'unico modo in cui posso immaginare di non essere in grado di ripristinare gli elementi nel loro stato precedente è se in qualche modo la superview fosse diversa, o con una propria trasformazione o una cornice diversa o una vista diversa del tutto. Ma non posso dirlo dalla tua domanda.

In sintesi, il codice originale nella tua domanda dovrebbe funzionare, quindi c'è qualcos'altro in corso! Spero che questa risposta ti aiuti a restringere il campo.

+0

Mi è capitato di provare qualcosa di simile: salvare la trasformazione della vista, centrarla e ricaricarla. La cosa non si muove (riproducendo i risultati). Ma quando riavvio l'app, è da qualche altra parte. Quindi, seguendo il tuo pensiero sul fatto che la superview sia il problema, forse quando sto leggendo le preferenze all'avvio, alcuni stati nella superview non sono ancora impostati. Scoprirò ciò e riferirò. Grazie mille! – sbkp

+0

Ok, quindi quando sto caricando i prefs, tutto sembra a posto. Il limite 'bounds' del pannello UI è {{0, 0}, {506, 254}}. Alla fine del metodo viewDidLoad di VC, tutto sembra ancora a posto. Ma nel momento in cui le cose vengono effettivamente visualizzate, il rect 'bounds 'è qualcos'altro. Ad esempio: {{0, 0}, {488.321, 435.981}}. Se resetto i limiti a ciò che dovrebbe essere, si rimette in posizione. Quindi è abbastanza facile farlo programmaticamente, ma in realtà non sono sicuro di quando farlo! Avrei pensato di farlo alla fine di viewDidLoad, ma "bounds" è ancora corretto a quel punto. – sbkp

+2

La geometria (frame, limiti, ecc.) Non è impostata completamente quando viene chiamato viewDidLoad, ma è _is_ impostato prima di viewWillAppear. Quindi, sarebbe il posto giusto per controllare la geometria e apportare eventuali modifiche. (Tuttavia, resettare i limiti senza capire cosa potrebbe averlo modificato potrebbe avere altre conseguenze.) Una maschera autoresizing potrebbe cambiare il frame/bounds/center prima che appaia la vista. –