2015-07-17 10 views
5

Utworzyłem interaktywne przejście. Mój func animateTransition(transitionContext: UIViewControllerContextTransitioning) jest całkiem normalny, dostaję pojemnik UIView, dodaję dwa UIViewControllers, a następnie robię zmiany animacji w UIView.animateWithDuration(duration, animations, completion).UIPercentDrivenInteractiveTransition nie przechodzi do zakończenia animacji szybkim gestem

Dodaję UIScreenEdgePanGestureRecognizer do mojej z UIViewController. Działa dobrze, z wyjątkiem sytuacji, gdy wykonuję bardzo szybką patelnię.

W tym ostatnim scenariuszu aplikacja nie reaguje, wciąż jest na tym samym UIViewController (przejście prawdopodobnie nie zadziałało), ale uruchamiane są zadania w tle. Po uruchomieniu Debug View Hierarchy widzę nowy UIViewController zamiast poprzedniego, a poprzedni (przynajmniej jego UIView) stoi tam, gdzie powinien stanąć na końcu przejścia.

Zrobiłem kilka wydrukować i sprawdzić punkty i od tego, co mogę powiedzieć, że kiedy pojawia się problem, zakończenie animacji za (jeden w moim sposobie animateTransition) nie zostanie osiągnięta, więc nie mogę wywołać metodę transitionContext.completeTransition aby zakończyć lub nie przejść.

Widziałem również, że patelnia czasami przechodzi od UIGestureRecognizerState.Began prosto do UIGestureRecognizerState.Ended bez przechodzenia przez UIGestureRecognizerState.Changed.

Kiedy przechodzi UIGestureRecognizerState.Changed, zarównotranslationivelocity pozostają takie same dla każdego UIGestureRecognizerState.Changed stanach.

EDIT:

Oto kod:

animateTransition metoda

func animateTransition(transitionContext: UIViewControllerContextTransitioning) { 
    self.transitionContext = transitionContext 

    let containerView = transitionContext.containerView() 
    let screens: (from: UIViewController, to: UIViewController) = (transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!, transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!) 

    let parentViewController = presenting ? screens.from : screens.to 
    let childViewController = presenting ? screens.to : screens.from 

    let parentView = parentViewController.view 
    let childView = childViewController.view 

    // positionning the "to" viewController's view for the animation 
    if presenting { 
     offStageChildViewController(childView) 
    } 

    containerView.addSubview(parentView)  
    containerView.addSubview(childView) 

    let duration = transitionDuration(transitionContext) 

    UIView.animateWithDuration(duration, animations: { 

     if self.presenting { 
      self.onStageViewController(childView) 
      self.offStageParentViewController(parentView) 
     } else { 
      self.onStageViewController(parentView) 
      self.offStageChildViewController(childView) 
     }}, completion: { finished in 
      if transitionContext.transitionWasCancelled() { 
       transitionContext.completeTransition(false) 
      } else { 
       transitionContext.completeTransition(true) 
      } 
    }) 
} 

gest gest i obsługi:

weak var fromViewController: UIViewController! { 
    didSet { 
     let screenEdgePanRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: "presentingViewController:") 
     screenEdgePanRecognizer.edges = edge 
     fromViewController.view.addGestureRecognizer(screenEdgePanRecognizer) 
    } 
} 

func presentingViewController(pan: UIPanGestureRecognizer) { 
    let percentage = getPercentage(pan) 

    switch pan.state { 

    case UIGestureRecognizerState.Began: 
     interactive = true 
     presentViewController(pan) 

    case UIGestureRecognizerState.Changed: 
     updateInteractiveTransition(percentage) 

    case UIGestureRecognizerState.Ended: 
     interactive = false 

     if finishPresenting(pan, percentage: percentage) { 
      finishInteractiveTransition() 
     } else { 
      cancelInteractiveTransition() 
     } 

    default: 
     break 
    } 
} 

pojęcia, co może się zdarzyć?

EDIT 2:

Oto nieujawnione metody:

override func getPercentage(pan: UIPanGestureRecognizer) -> CGFloat { 
    let translation = pan.translationInView(pan.view!) 
    return abs(translation.x/pan.view!.bounds.width) 
} 

override func onStageViewController(view: UIView) { 
    view.transform = CGAffineTransformIdentity 
} 

override func offStageParentViewController(view: UIView) { 
    view.transform = CGAffineTransformMakeTranslation(-view.bounds.width/2, 0) 
} 

override func offStageChildViewController(view: UIView) { 
    view.transform = CGAffineTransformMakeTranslation(view.bounds.width, 0) 
} 

override func presentViewController(pan: UIPanGestureRecognizer) { 
    let location = pan.locationInView((fromViewController as! MainViewController).tableView) 
    let indexPath = (fromViewController as! MainViewController).tableView.indexPathForRowAtPoint(location) 

    if indexPath == nil { 
     pan.state = .Failed 
     return 
    } 
    fromViewController.performSegueWithIdentifier("chartSegue", sender: pan) 
} 
  • usunąć linie "Over", dodając => nie rozwiąże to
  • dodałem updateInteractiveTransition w .Began, w .Ended, w obu => go nie naprawiono
  • Włączony powinienRasteryzować na warstwie widoku mojego toViewController i niech go na cały czas => nie go naprawić

Ale pytanie brzmi, dlaczego, kiedy robi szybki interaktywną gest, nie jest to reaguje wystarczająco szybko

faktycznie działa z szybkim interaktywnym gestem, o ile zostawiam palec wystarczająco długo. Na przykład, jeśli bardzo szybko przesuwalę ponad (powiedzmy) 1 cm, jest w porządku. To nie w porządku, gdybym pan bardzo szybko na małej powierzchni (powiedzmy znowu) mniej niż 1cm

potencjalnych kandydatów obejmują poglądy są animowane są zbyt skomplikowane (lub skomplikowane efekty jak cieniowanie)

Myślałem także o skomplikowanym widoku, ale nie sądzę, że mój widok jest naprawdę skomplikowany. Istnieje kilka przycisków i etykiet, niestandardowy UIControl działający jak segmentowany segment, wykres (ładowany po pojawieniu się kontrolera) i ładowany jest plik xib wewnątrz kontrolki viewController.


Ok Właśnie stworzyliśmy project z klas minimum i obiektów w celu wywołania problemu. Aby go uruchomić, wystarczy wykonać szybkie i krótkie przesunięcie z prawej strony na lewo.

Zauważyłem, że za pierwszym razem działa całkiem łatwo, ale jeśli przeciągasz kontroler widoku normalnie za pierwszym razem, to znacznie trudniej jest go wyzwolić (nawet niemożliwe?). Podczas gdy w moim pełnym projekcie, to naprawdę nie ma znaczenia.

+0

@Rob Próbowałem wszystkiego, ale to nie rozwiązało problemu. Jedynym momentem, w którym było OK, było to, że ToViewController był prawie pusty. Zaktualizowałem pytanie linkiem do minimalnego projektu, który powoduje problem. Dzięki za pomoc. – Nico

+0

@Rob czy wypróbowałeś to na symulatorze lub na urządzeniu? Dla twojej informacji, na symulatorze nie mogę go odtworzyć w ogóle, nawet przy pełnym projekcie. Używam mojego iPhone'a 6 i ostatniej wersji iOS – Nico

+0

ok dzięki jeszcze raz za poświęcony czas. Zauważam też coś innego. Jeśli spróbujesz zaprezentować toViewController i anulować przejście, obiekt nie zostanie dezinicjowany. Zostanie deinicjalizowany po następnym wyświetleniu dowolnego kontrolera widoku (nawet jeśli wywołasz ten sam kontroler widoku). – Nico

Odpowiedz

9

Kiedy zdiagnozowałem ten problem, zauważyłem, że zmiana gestów i zakończone zdarzenia stanu miały miejsce jeszcze przed uruchomieniem. Tak więc animacja została anulowana/zakończona, zanim jeszcze się zaczęła!

Próbowałem za pomocą GCD kolejkę synchronizacji animacji, aby zapewnić, że uaktualnienie UIPercentDrivenInterativeTransition nie zdarza się aż po `ożywionej:

private let animationSynchronizationQueue = dispatch_queue_create("com.domain.app.animationsynchronization", DISPATCH_QUEUE_SERIAL) 

Ja wtedy miałem metodę użytkową do użycia tej kolejki:

func dispatchToMainFromSynchronizationQueue(block: dispatch_block_t) { 
    dispatch_async(animationSynchronizationQueue) { 
     dispatch_sync(dispatch_get_main_queue(), block) 
    } 
} 

i wtedy mój gest obsługi upewnić się, że zmiany i stany ended zostały poprowadzone przez te kolejki:

func handlePan(gesture: UIPanGestureRecognizer) { 
    switch gesture.state { 

    case .Began: 
     dispatch_suspend(animationSynchronizationQueue) 
     fromViewController.performSegueWithIdentifier("segueID", sender: gesture) 

    case .Changed: 
     dispatchToMainFromSynchronizationQueue() { 
      self.updateInteractiveTransition(percentage) 
     } 

    case .Ended: 
     dispatchToMainFromSynchronizationQueue() { 
      if isOkToFinish { 
       self.finishInteractiveTransition() 
      } else { 
       self.cancelInteractiveTransition() 
      } 
     } 

    default: 
     break 
    } 
} 

Tak, mam stan rozpoznawania gestów .Began zawiesić tę kolejkę, i mam kontrolera animacji wznowić kolejkę w animationTransition (zapewniając, że kolejka rozpoczyna się ponownie dopiero po tym, jak ta metoda zostanie uruchomiona, zanim gest przystąpi do próby aktualizacji obiektu UIPercentDrivenInteractiveTransition .

+0

Dzięki Rob, tylko kilka pytań. Po pierwsze, czy istnieje sposób sprawdzenia, czy 'dispatch_suspend (animationSynchronizationQueue) został wywołany? Korzystam z tego animatora przez kilka przejść i kiedy nie zawieszam kolejki, podczas jej wznawiania dostaję błąd. Po drugie, prędkość jest równa 0, gdy kod w 'dispatchToMainFromSynchronizationQueue' został osiągnięty, czy jest to normalne ?. Następnie uchwyciłem prędkość poza kolejką, aby naprawić, ale nie jestem pewien, czy to najlepszy sposób. – Nico

+0

Nie, nie ma sposobu sprawdzenia, czy jest zawieszony, więc dodaj swój własny 'Bool' ivar, aby to śledzić. Re zachowanie, gdy prędkość wynosi zero, nie, 'dispatchToMainFromSynchronizationQueue' nie zmienia wcale logiki' velocity == 0', więc podejrzewam, że dzieje się coś innego. – Rob

+0

Jeśli chodzi o prędkość, wydaje się, że raz. Wygaśnięty został osiągnięty, a kod w nim wykonany, prędkość jest resetowana. Widziałem, że jakiś kod w .Changed (w kolejce synchronizacji animacji) został wywołany po. Zakończony został osiągnięty, a wartość prędkości była równa 0. – Nico