2015-10-20 9 views
12

Ho due UILabels incorporati in un UIStackView. L'etichetta superiore rimane visibile costantemente, ma l'etichetta inferiore viene attivata e disattivata tramite la proprietà hidden. Ho voluto questo effetto di essere animato, così ho bloccato in un blocco di animazione:La proprietà nascosta non può essere modificata all'interno di un blocco di animazione

private func toggleResultLabel(value:Double) { 
    if value == 0 { 
     UIView.animateWithDuration(0.25) {() -> Void in 
      self.resultLabel.hidden = true 
     } 
    } else { 
     UIView.animateWithDuration(0.25) {() -> Void in 
      // Something weird is happening. I had to add 3 of the same statements to get 
      // the hidden flag to be false 
      self.resultLabel.hidden = false 
      self.resultLabel.hidden = false 
      self.resultLabel.hidden = false 
     } 
    } 
} 

Il problema è che la proprietà nascosto non cambierà a meno che ripeto la dichiarazione più e più volte (3 volte in questo caso). Ho trovato questo mentre irrompevo nella chiusura dell'animazione e vedevo che la proprietà non sarebbe cambiata al suo compito. Ora sto notando lo stesso problema che si verifica di nuovo in modo apparentemente casuale. Il valore predefinito della seconda etichetta è true, se pertinente.

C'è qualcosa che mi manca qui, o si tratta di un bug?

Aggiornamento: Per quel che vale, ho ottenuto lavorando con l'aggiunta di removeArrangedSubview() e addArrangedSubview():

if value == 0 { 
    UIView.animateWithDuration(0.25) {() -> Void in 
     self.resultLabel.hidden = true 
     self.heroStackView.removeArrangedSubview(self.resultLabel) 
    } 
} else { 
    UIView.animateWithDuration(0.25) {() -> Void in 
     self.heroStackView.addArrangedSubview(self.resultLabel) 
     self.resultLabel.hidden = false 
    } 
} 
+1

Il valore che non cambia è strano, ma ad ogni modo per animare l'etichetta è necessario modificare il valore 'alpha' della vista e non' hidden'. AFAIK, 'hidden' non è animabile. –

+0

Grazie a @GuillaumeAlgis! Potrei provare a cambiare l'alfa, ma non penso che riorganizzerà lo StackView poiché è solo invisibile e rimosso. La proprietà nascosta è animabile e funziona il 95% delle volte. Per riferimento ho usato la sezione in fondo alla pagina: [https://developer.apple.com/library/prerelease/tvos/documentation/UIKit/Reference/UIStackView_Class_Reference/index.html](https://developer.apple .com/library/prerelease/tvos/documentation/UIKit/Reference/UIStackView_Class_Reference/index.html) – Alex

+0

Hmm se ho capito bene il documento, questo è un comportamento specifico di 'UIStackView'. Al di fuori di questo caso speciale 'hidden' non sarà animato. Stai usando un 'UIStackView'? –

risposta

15

Su iOS 11 e precedente, quando si nasconde un arrangedSubview di un UIStackView utilizzando l'API di animazione UIView più volte, la proprietà nascosta "stack" valori, e richiede l'impostazione nascosta a false più volte prima che il valore effettivamente cambia.

Al lavoro abbiamo deciso di utilizzare un'estensione UIView con un metodo di soluzione temporanea che imposta nascosta solo una volta per un determinato valore.

extension UIView { 

    // Workaround for the UIStackView bug where setting hidden to true with animation 
    // mulptiple times requires setting hidden to false multiple times to show the view. 
    public func workaround_nonRepeatingSetHidden(hidden: Bool) { 
     if self.hidden != hidden { 
      self.hidden = hidden 
     } 
    } 
} 

questo è sicuramente un bug in UIKit, controlla la sample project che riproduce chiaramente.

enter image description here

+1

Wow grazie per aver notato questo! Risolto il problema –

+1

luglio 2017, il bug è ancora lì, appena riprodotto. grazie per la risposta esauriente – Kappe

+0

Sta accadendo anche in iOS 11. –

7

Sembra che ci sia correlazione sulla bandiera quante volte nascosto è impostato su stesso stato e quanti volte deve essere impostato su uno stato diverso prima di essere effettivamente modificato. Nel mio caso, avevo il flag nascosto già impostato su SÌ prima che fosse nuovamente impostato su SÌ nel blocco di animazione e questo ha causato il problema in cui dovevo chiamare hidden = NO due volte nel mio altro blocco di animazione per renderlo nuovamente visibile. Se avessi aggiunto più linee nascoste = SÌ nel primo blocco di animazione per la stessa vista, dovevo avere anche più linee nascoste = NO nel secondo blocco di animazione. Questo potrebbe essere un bug nell'osservazione KVO di UIStackView per il flag nascosto che non controlla se il valore è effettivamente cambiato o meno prima di modificare alcuni stati interni che portano a questo problema.

Per risolvere temporaneamente il problema (fino a quando Apple lo risolve), ho creato una categoria per UIView e swizzled setHidden: metodo per una versione che prima controlla il valore originale e imposta il nuovo valore solo se è diverso dall'originale. Questo sembra funzionare senza effetti negativi.

@implementation UIView (MethodSwizzling) 

+ (void)load 
{ 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     Class class = [self class]; 

     SEL originalSelector = @selector(setHidden:); 
     SEL swizzledSelector = @selector(UIStackViewFix_setHidden:); 

     Method originalMethod = class_getInstanceMethod(class, originalSelector); 
     Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 

     BOOL didAddMethod = 
     class_addMethod(class, 
         originalSelector, 
         method_getImplementation(swizzledMethod), 
         method_getTypeEncoding(swizzledMethod)); 

     if (didAddMethod) { 
      class_replaceMethod(class, 
           swizzledSelector, 
           method_getImplementation(originalMethod), 
           method_getTypeEncoding(originalMethod)); 
     } else { 
      method_exchangeImplementations(originalMethod, swizzledMethod); 
     } 
    }); 
} 

- (void)UIStackViewFix_setHidden:(BOOL)hidden 
{ 
    if (hidden != self.hidden) { 
     [self UIStackViewFix_setHidden:hidden]; 
    } 
} 

@end 
4

Sembra essere un bug di Apple con UIStackView. Vedere il seguente ...

UIStackView: toggleing nascosto con animazioni rimane incastrato nel nascosti modalità http://www.openradar.me/22819594

La mia soluzione, anche se non è l'ideale, è stato quello di nascondere l'UIStackView senza animazione.

5

Nel considerare l'errore UIStackView, decido di controllare la proprietà nascosta.

if myView.hidden != hidden { 
    myView.hidden = hidden 
} 

Non è la soluzione più elegante ma funziona per me.

1

Come per la risposta di Raz0, che ha scoperto che solo l'impostazione isHidden quando necessario risolve il problema, ecco una soluzione rapida che ha funzionato per me.Sto evitando metodo swizzling perché è intrinsecamente insicuro, in favore di un approccio show/hide che non dovrebbe pasticciare con i metodi originali:

extension UIView { 
    func show() { 
     guard isHidden else { 
      return 
     } 
     isHidden = false 
    } 

    func hide() { 
     guard !isHidden else { 
      return 
     } 
     isHidden = true 
    } 
} 

Utilizzare in questo modo:

view.show() 
view.hide() 
0

Quello che ha funzionato per me è quello di impostare la proprietà nascosta al di fuori dell'animazione e poi animare layoutIfNeeded(), proprio come si farebbe con i vincoli che animano:

label.isHidden = true 
UIView.animate(withDuration: 3) { 
    self.view.layoutIfNeeded() 
} 

dove Label è un organizzato sottoview di UIStackView.

Problemi correlati