2012-04-04 13 views
7

Sto implementando un semplice solitario per iOS che consente all'utente di trascinare le carte nel solito modo. Le carte sono rappresentate con la sottoclasse UIViewCardView. Tutte le schede di visualizzazione sono fratelli, che sono subviews di SolitaireView. Il seguente frammento cerca di "portare una carta al fronte" in modo che sia al di sopra tutte le altre viste mentre viene trascinato:UIView bringSubviewToFront: fa * not * porta in primo piano

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    UITouch *touch = [touches anyObject]; 
    if (touch.view.tag == CARD_TAG) { 
     CardView *cardView = (CardView*) touch.view; 
     ... 
     [self bringSubviewToFront:cardView]; 
     ... 
    } 
} 

Sfortunatamente, ordine z della carta rimane invariato durante il trascinamento. Nelle immagini qui sotto, sto trascinando il re. Notate come è correttamente sulla parte superiore dei Nove nell'immagine a sinistra, ma è in modo non corretto sotto le due (sotto l'intero stack in realtà) nell'immagine a destra:

enter image description hereenter image description here

Ho provato anche alterare la proprietà layer.zPosition come inutilmente. Come posso portare la vista della carta in primo piano durante il trascinamento? Sono confuso.

+0

ho scoperto che 'bringSubviewToFront:' innesca la bandiera della vista 'needsLayout' da impostare. Il mio metodo 'layoutSubviews' stava anche chiamando' bringSubviewToFront: 'ripristinando così tutti gli ordini z view al loro stato iniziale! – wcochran

+0

Allora qual è la soluzione per questo? Ho anche lo stesso tipo di requisiti, dove dalla lista delle carte, se seleziono una qualsiasi delle carte di ordine medio, dovrebbe venire, quindi venire in primo piano. ma la mia animazione non funziona a causa di bringSubviewToFront: metodo – Mrunal

+0

Se continui a utilizzare sottoclassi UIView, devi contrassegnare la scheda trascinata in modo che layoutSubviews possa posizionarla sopra. In realtà ho deciso di utilizzare CALayer come indicato di seguito. – wcochran

risposta

8

Confermato. bringSubviewToFront: da invocare layoutSubview. Poiché la mia versione di layoutSubviews imposta gli ordini z su tutte le viste, questo annullava l'ordine z che stavo impostando nel codice touchesBegan:withEvent sopra. Apple dovrebbe menzionare questo effetto collaterale nella documentazione bringSubviewToFront.

Invece di utilizzare una sottoclasse UIView, ho creato una sottoclasse CALayer denominata CardLayer. Gestisco il tocco nella sottoclasse KlondikeView come elencato di seguito. topZPosition è una variante di istanza che tiene traccia della più alta zPosizione di tutte le carte. Si noti che la modifica del zPosition è di solito animata - mi rivolgo questo fuori nel seguente codice:

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { 
    UITouch *touch = [touches anyObject]; 
    CGPoint touchPoint = [touch locationInView:self]; 
    CGPoint hitTestPoint = [self.layer convertPoint:touchPoint 
              toLayer:self.layer.superlayer]; 
    CALayer *layer = [self.layer hitTest:hitTestPoint]; 

    if (layer == nil) return; 

    if ([layer.name isEqual:@"card"]) { 
    CardLayer *cardLayer = (CardLayer*) layer; 
    Card *card = cardLayer.card; 

    if ([self.solitaire isCardFaceUp:card]) { 
     //...                            
     [CATransaction begin]; // disable animation of z change                 
     [CATransaction setValue:(id)kCFBooleanTrue 
         forKey:kCATransactionDisableActions];    
     cardLayer.zPosition = topZPosition++; // bring to highest z 

     // ... if card fan, bring whole fan to top 

     [CATransaction commit]; 
     //...                            
    } 
    // ...                            
    } 

} 
+4

Penso che l'utilizzo di 'UIView' per le carte da gioco potrebbe essere stato una cattiva scelta di design - non penso che fossero davvero destinati per questo. Penso che farò un passo indietro e usare invece "CALAYER". – wcochran