„Z kontrolera widoku” znika przy użyciu UIViewControllerContextTransitioning

105

Mam jeden problem i opisałem go poniżej.

Używam UIViewControllerContextTransitioningdo niestandardowych przejść.

Mam 2 kontrolery widoku, pierwszy kontroler widoku i drugi kontroler widoku.

Teraz chcę dodać drugi kontroler widoku na pierwszym kontrolerze widoku z animacją. Udało mi się to, teraz drugi kontroler widoku jest przezroczysty, więc możemy zobaczyć pierwszy kontroler widoku poniżej drugiego kontrolera widoku.

Ale nie mogę zobaczyć pierwszego kontrolera widoku, a widzę tylko czarny ekran poniżej drugiego kontrolera widoku.

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    self.transitionContext = transitionContext;
    if(self.isPresenting){
        [self executePresentationAnimation:transitionContext];
    }
    else{
       [self executeDismissalAnimation:transitionContext];
    }
  }

-(void)executePresentationAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{
     UIView* inView = [transitionContext containerView];
     UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

     UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

     CGRect offScreenFrame = inView.frame;
     offScreenFrame.origin.y = inView.frame.size.height;
     toViewController.view.frame = offScreenFrame;

    toViewController.view.backgroundColor = [UIColor clearColor];
    fromViewController.view.backgroundColor = [UIColor clearColor];
    inView.backgroundColor = [UIColor  clearColor];
    [inView insertSubview:toViewController.view aboveSubview:fromViewController.view];
     // [inView addSubview:toViewController.view];
    CFTimeInterval duration = self.presentationDuration;
    CFTimeInterval halfDuration = duration/2;

    CATransform3D t1 = [self firstTransform];
    CATransform3D t2 = [self secondTransformWithView:fromViewController.view];

    [UIView animateKeyframesWithDuration:halfDuration delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{

    [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.5f animations:^{
        fromViewController.view.layer.transform = t1;
    }];

    [UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.5f animations:^{
        fromViewController.view.layer.transform = t2;
    }];
    } completion:^(BOOL finished) {
    }];


    [UIView animateWithDuration:duration delay:(halfDuration - (0.3*halfDuration)) usingSpringWithDamping:0.7f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
        toViewController.view.frame = inView.frame;
    } completion:^(BOOL finished) {
        [self.transitionContext completeTransition:YES];
    }];
}

Po [self.transitionContext completeTransition:YES];wywołaniu nagle pierwszy kontroler widoku znika, a pod drugim kontrolerem widoku pojawia się czarny ekran.

Czy ktoś ma pomysł? Dzięki.

NiravPatel
źródło

Odpowiedzi:

98

Miałem tutaj ten sam problem - wygląda na błąd w iOS 8. Złożyłem radar .

Użyłem Reveal do sprawdzenia hierarchii widoku po tym, jak ekran stał się czarny. Klucz UIWindowjest całkowicie pusty - w ogóle nie ma hierarchii widoków!

Reveal'd

Trochę się bawiłem i wygląda na to, że istnieje łatwe obejście dla prostych przypadków. Możesz po prostu ponownie dodać toViewControllerwidok jako podwidok okna klucza:

transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)

Sprawdziłem i okno klucza rootViewControllerjest nadal poprawnie ustawione, więc w porządku. Nie jestem pewien, co by się stało, gdybyś zaprezentował swój kontroler z poziomu już przedstawionego kontrolera modalnego, więc w przypadku bardziej złożonych przypadków będziesz musiał poeksperymentować.

Jesion Bruzda
źródło
2
Widziałem też ten problem. iOS 8 wprowadza nową metodę i klucze dostępu do fromView i toView (uwaga: nie kontroler widoku). Wygląda na to, że te odwołania nie są tracone podczas przejścia. Możesz dodać je do widoku kontenera tak, jak zwykle, gdybyś właśnie pobrał je z kontrolerów widoku.
tapi,
1
Widziałem podobne dziwactwa w iOS 8, kiedy próbowałem dodać podglądy do widoku mojego kontrolera nawigacji, w viewDidLoad. Ponowne dodanie widoku navigationControllera do keyWindow wydawało się załatwić sprawę, wielkie dzięki, Ash!
taber
1
Nadal widzę to w GM (i ta poprawka nadal działa). Czy inni widzą to samo? Czy to tylko zmiana w API?
rjkaplan
21
Odkryłem, że ten błąd (i wiele więcej!) Znika, jeśli ustawisz modalPresentationStyle = UIModalPresentationFullScreen. Oczywiście nadal otrzymujesz niestandardową animację przejścia.
Chris
1
Dzięki @AshFurrow. Niezłe obejście, dopóki nie zostanie naprawione!
kandelvijaya
78

Uważam, że należy lepiej wyjaśnić uzasadnienie tego.

Widok znika, ponieważ usuwasz widok kontrolera widoku prezentacji z jego pierwotnej lokalizacji (hierarchia widoków), umieszczasz go w module containerView, który zapewnia Twój animator, ale nigdy nie zwraca go z powrotem po zakończeniu animacji. Tak więc widok kontrolera widoku jest całkowicie usuwany wraz z jego superviewem (containerView) z okna.

W iOS 7 system zawsze przywracał widoki kontrolerów widoku, które są zaangażowane w prezentację (prezentację i prezentację), do ich oryginalnych miejsc po automatycznym zakończeniu animacji przejścia. Tak się już nie dzieje w przypadku niektórych stylów prezentacji w iOS 8.

Zasada jest bardzo prosta: animator powinien manipulować widokiem kontrolera widoku prezentacji tylko wtedy, gdy widok kontrolera widoku ma zostać całkowicie ukryty (usunięty z hierarchii widoków) do końca przejścia . Innymi słowy, oznacza to, że po zakończeniu animacji początkowej prezentacji będzie widoczny tylko widok kontrolera prezentowanego widoku, a nie widok kontrolera widoku prezentacji. Na przykład, jeśli ustawisz nieprzezroczystość widoku kontrolera widoku prezentowanego na 50% i użyjesz UIModalPresentationFullScreen, nie będziesz mógł zobaczyć widoku kontrolera widoku prezentującego pod prezentowanym, ale jeśli użyjesz UIModalPresentationOverFullscreen - tak ( shouldRemovePresentersViewza określenie tego odpowiada metoda UIPresentationController ).

Dlaczego nie pozwolić animatorowi na ciągłe manipulowanie widokiem kontrolera widoku prezentacji? Po pierwsze, jeśli widok kontrolera widoku prezentacji ma pozostać widoczny po zakończeniu animacji przez cały cykl życia prezentacji, nie ma potrzeby jej w ogóle animować - po prostu pozostaje tam, gdzie jest. Po drugie, jeśli własność tego kontrolera widoku zostanie przeniesiona na kontroler prezentacji, najprawdopodobniej kontroler prezentacji nie będzie wiedział, jak rozmieścić widok tego kontrolera widoku w razie potrzeby, na przykład w przypadku zmiany orientacji, ale pierwotny właściciel kontrolera widoku prezentacji tak .

W iOS 8 viewForKey:wprowadzono metodę uzyskiwania widoków, którymi manipuluje animator. Po pierwsze, pomaga postępować zgodnie z zasadą opisaną powyżej, zwracając zero, gdy animator nie powinien dotykać widoku. Po drugie, animator może zwrócić inny widok do animacji. Wyobraź sobie, że wdrażasz prezentację podobną do arkusza formularzy. W tym przypadku chciałbyś dodać cień lub dekorację wokół widoku kontrolera prezentowanego widoku. Zamiast tego animator ożywi tę dekorację, a widok kontrolera widoku prezentowanego będzie dzieckiem tej dekoracji.

viewControllerForKey: nie znika, można go nadal używać, jeśli potrzebny jest bezpośredni dostęp do kontrolerów widoku, ale animator nie powinien robić żadnych założeń dotyczących widoków, które ma animować.

Jest kilka rzeczy, które możesz zrobić, aby poprawnie rozwiązać problem ze znikającym widokiem kontrolera widoku prezentacji, gdy umieścisz go bezpośrednio w widoku kontenera animatora:

  1. Jeśli nie ma potrzeby animowania widoku kontrolera widoku prezentacji, użyj viewForKey:polecenia, aby animować widoki, zamiast sięgać do widoków kontrolera bezpośrednio. viewForKey:może zwrócić zerowe lub nawet zupełnie inne poglądy.

  2. Jeśli chcesz animować widok kontrolerów widoku prezentacji, powinieneś rozważyć użycie UIModalPresentationFullScreenstyle lub kontynuować używanie UIModalPresentationCustomi zaimplementować własną podklasę UIPresentationController z shouldRemovePresentersViewzwracaniem YES. W rzeczywistości implementacja tej metody jest główną różnicą między wewnętrznymi kontrolerami prezentacji zdefiniowanymi przez UIModalPresentationFullScreeni UIModalPresentationCustomstylami, poza tym, że ten ostatni pozwala na użycie niestandardowych kontrolerów prezentacji.

  3. We wszystkich innych rzadkich przypadkach będziesz musiał przywrócić widok kontrolera widoku prezentacji do jego pierwotnej lokalizacji, zgodnie z sugestiami innych odpowiedzi.

egdmitry
źródło
2
To super dziwne, ponieważ kod ten opiera się na viewControllerForKey:„s views tylko kiedy viewForKey:powraca zerowa, a ja nadal musiał ponownie dodać ją do okna ręcznie. Czy masz przykład kodu działającego bez tego obejścia?
Ash Furrow
Cóż, jeśli viewForKey:zwraca zero, na pewno będziesz musiał ponownie dodać widok kontrolera widoku prezentacji do okna, jeśli usuniesz go z niego w swoim animatorze. W przypadku, gdy viewForKey zwraca widok rzeczywistego kontrolera widoku, można bezpiecznie przenieść ten widok, ponieważ UIKit przeniósłby go z powrotem do pierwotnej pozycji po zakończeniu cyklu życia prezentacji.
egdmitry
Dziękuję za wyjaśnienie przyczyny tego problemu. Masz całkowitą rację. Przeniesienie pozycji widoku w hierarchii widoku bez zastępowania go oczywiście spowodowałoby, że zniknąłby (po iOS 8, a pracuję teraz z iOS 10!) Dziękuję za wyjaśnienie.
Clay Ellis
1
Dzięki egdmitry za wyjaśnienie. Co rodzi kolejne pytanie: jak myślisz, w jaki sposób powinienem zaimplementować prezentację typu ujawnienie ? Jeden z tych bardzo powszechnych w dzisiejszych czasach, w których widok prezentacji wysuwa się częściowo, aby pokazać prezentowany widok pod spodem? W tym scenariuszu zarówno prezentacja, jak i prezentacja muszą być wyświetlane na ekranie, a widok prezentacji jest animowany.
Andrea
70

W systemie iOS 8 należy manipulować widokami zwracanymi przez viewForKey:zamiast .viewwłaściwości kontrolerów widoku zwróconych przez viewControllerForKey:. Nie jest to szczególnie jasne w dokumentacji beta, ale jeśli spojrzysz na źródło UIViewControllerTransitioning.h, zobaczysz ten komentarz powyżej viewControllerForKey::

// Currently only two keys are defined by the
// system - UITransitionContextToViewControllerKey, and
// UITransitionContextFromViewControllerKey.
// Animators should not directly manipulate a view controller's views and should
// use viewForKey: to get views instead.
- (UIViewController *)viewControllerForKey:(NSString *)key;

Dlatego zamiast dostosowywania ramek itp. toViewController.viewUżyj wartości zwracanej przez [transitionContext viewForKey:UITransitionContextToViewKey].

Jeśli Twoja aplikacja musi obsługiwać iOS7 i / lub Xcode 5, możesz użyć prostej metody kategorii na UIViewController, jak poniżej:

- (UIView *)viewForTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
        NSString *key = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey] == self ? UITransitionContextFromViewKey : UITransitionContextToViewKey;
        return [transitionContext viewForKey:key];
    } else {
        return self.view;
    }
#else
    return self.view;
#endif
}

Następnie zdobądź swoje toViewControlleri fromViewControllerjak zwykle, ale uzyskaj widoki za pomocą [toViewController viewForTransitionContext:transitionContext].

Edycja: Wygląda na to, że jest błąd viewForKeypolegający na tym, że widok kontrolera widoku prezentacji jest zerowy po powrocie z , co uniemożliwia wykonanie przejść modalnych, które w ogóle animują widok prezentacji (takich jak zsuwanie się lub obracanie w poziomie). Zgłosiłem błąd dla iOS8 na rdar: // 17961976 ( http://openradar.appspot.com/radar?id=5210815787433984 ). Zobacz także przykładowy projekt pod adresem http://github.com/bcherry/TransitionBug

Edycja 2: Dzięki graveley za sugestię, użycie UIModalPresentationFullScreen rozwiązuje problem. Być może to nie jest błąd. Apple może chcieć, aby UIModalPresentationCustom modyfikował tylko widok przychodzącego modalu. Jeśli chcesz zmodyfikować widok wychodzący, musisz zagwarantować prezentację nowego widoku na pełnym ekranie? W każdym przypadku należy użyć viewForKeyi UIModalPresentationFullScreen.

bcherry
źródło
2
Błąd viewForKey doprowadzał mnie do szału! - dzięki za zgłoszenie. FWIW, moje przejście działa dobrze, pobierając widok z UITransitionContextToViewControllerKey, ale moje przejście stosuje transformację tylko do całego widoku. Nie jestem pewien, czy należy to interpretować jako manipulatingpoglądy VC, czy nie ...
MathewS,
1
Wow - to szaleństwo. Nie widziałem tego w różnicach - prawdopodobnie dlatego, że to tylko mały komentarz. Naprawdę frustrujące, gdy Apple robi taki wyczyn. Trzymaj kciuki za swoim radarem.
Jesion Bruzda
Widzę również viewForKeybłąd w GM. Czy inni też? Czy znalazłeś rozsądne obejście tego problemu?
rjkaplan
2
Pomyślałem zgodnie z komentarzem - viewForKey// viewForKey: może zwrócić nil, co oznaczałoby, że animator nie powinien manipulować widokiem powiązanego kontrolera widoku. Powrót nilnie jest błędem.
Ken Kuan,
4
@kenKuan możesz mieć rację. podczas korzystania z UIModalPresentationFullScreen viewForKeyzwraca z widoku i do widoku. Może więc celowo zwraca zero dla UIModalPresentationCustom. Aktualizuję raport o błędzie i wrócę tutaj, jeśli otrzymam informację od Apple.
bcherry
24

Brak ustawienia modalPresentationStylena UIModalPresentationCustom rozwiązał problem.

Innymi słowy, pozostawienie wartości domyślnej UIModalPresentationFullScreen zamiast określania UIModalPresentationCustom rozwiązało problem znikającego widoku. Zwróć uwagę, że protokół UIViewControllerTransitioningDelegate nadal wydaje się być przestrzegany, nawet jeśli pozostawiasz to ustawienie domyślne. Jeśli dobrze pamiętam, kiedyś UIModalPresentationCustom był wymagany.

Działa do tej pory, próbowałem tego tylko w przypadku nieinteraktywnych animacji.

graveley
źródło
1
łał. Zrobiło to! Testowałem bez modalPresentationStyle w iOS7 i 8 i działa w obu. Dzięki!!
Ah Ryun Moon
1
Dziękuję Ci! To w połączeniu z użyciem viewForKey:zamiast .viewna viewControllerForKey:rozwiązaniu wszystkich problemów dla mnie.
bcherry
1
To rozwiązało problem bez użycia viewForKey, ale przypuszczam, że powinno być również używane.
Kevin Sliech
5
Chociaż wydaje się, że rozwiązuje to problem, należy zauważyć, że ekran za kontrolerem widoku stanie się czarny po jego wyświetleniu. Jest to ważne, jeśli kontroler widoku nie jest wyświetlany na pełnym ekranie.
Koleś
16

Znalazłem tę niezwykle przydatną odpowiedź w powiązanym wątku autorstwa Lefterisa: https://stackoverflow.com/a/27165723/3709173

Podsumowując:

  1. ustaw modalPresentationStyle na .Custom
  2. podklasa UIPresentationController, przesłonięcie shouldRemovePresentersView (z NO)
  3. Zastąp prezentację PresentationControllerForPresentedViewController w swojej klasie TransitionDelegate i zwróć niestandardowy UIPresentationController

+1 w niestandardowym przejściu, nie dodawaj do View, gdy ma miejsce animacja zwolnienia.

Zademonstrowano tutaj:

https://www.dropbox.com/s/7rpkyamv9k9j18v/CustomModalTransition.zip?dl=0 bez żadnych hacków! to jest jak magia! :)

Mark Aron Szulyovszky
źródło
1
To jest właściwa odpowiedź. Bez magicznych sztuczek, jak w zaakceptowanym. Dzięki, Mark!
Andrei Malygin
Niestety to nie działa w iOS 12.4, Xcode 10.3. Po zakończeniu przejścia ekran staje się czarny (wszystkie widoki zostały usunięte z hierarchii. Jednak ustawienie właściwości „modalPresentationStyle” na „.fullscreen” DZIAŁA. Pozdrawiam.
Womble,
Wypróbowałem wersję Obj-C z implementacji Swift Marka i gwinyai w moim projekcie. Niestety żaden z nich nie działa zgodnie z oczekiwaniami. Używam Xcode 11.1, a celem kompilacji jest iOS 13.0, próbowałem zarówno na urządzeniu, jak i na symulatorze. W moim przypadku podstawową konfiguracją jest widok kolekcji i po dotknięciu jednej komórki nastąpi przejście do widoku szczegółowego z animacją. Jednak działa całkowicie dobrze, jeśli używam domyślnej animacji przejścia. Prezentujący VC nie zniknie, gdy wznowię od szczegółów widok.
infinity_coding7
8

W systemie iOS 8 musisz utworzyć UIPresentationController i zaimplementować poniższą metodę w UIViewControllerTransitioningDelegate.

- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source;

Pyta delegata o niestandardowy kontroler prezentacji do zarządzania hierarchią widoków podczas prezentowania kontrolera widoku.

Wartość zwracana:

Niestandardowy kontroler prezentacji do zarządzania prezentacją modalną.

Dyskusja:

Podczas prezentowania kontrolera widoku przy użyciu stylu prezentacji UIModalPresentationCustom system wywołuje tę metodę i pyta o kontroler prezentacji, który zarządza stylem niestandardowym. Jeśli zaimplementujesz tę metodę, użyj jej do utworzenia i zwrócenia niestandardowego obiektu kontrolera prezentacji, którego chcesz użyć do zarządzania procesem prezentacji.

Jeśli nie zaimplementujesz tej metody lub jeśli implementacja tej metody zwróci nil, system użyje domyślnego obiektu kontrolera prezentacji. Domyślny kontroler prezentacji nie dodaje żadnych widoków ani zawartości do hierarchii widoków.

Dostępność Dostępne w iOS 8.0 i nowszych.

Aby uzyskać więcej informacji, obejrzyj wideo WWDC 2014:

https://developer.apple.com/videos/wwdc/2014/?include=228

Istnieje również przykładowy kod z WWDC o nazwie „LookInside: Presentation Controllers Adaptivity and Custom Animator Objects”, który można pobrać z przykładowej strony kodowej WWDC 2014.

Być może trzeba będzie trochę zmienić przykładowy kod. Metoda init UIPresentationController została zmieniona na:

initWithPresentedViewController:presented presentingViewController:presenting

Wcześniej prezentował, a potem prezentował. Po prostu zamień je i powinno działać.

Paulo Faria
źródło
Przepraszamy, że nie oglądałem połączonego wideo, ale nie sądzę, abyś potrzebował niestandardowego kontrolera UIPresentationController, chyba że chcesz niestandardowej prezentacji po zakończeniu animacji, takiej jak okrągły przedstawiony widok. Jeśli chcesz po prostu inną animację, wdrożenie UIViewControllerAnimatedTransitioning powinno wystarczyć, w oparciu o moją ograniczoną wiedzę.
Vaddadi Kartick
7

zamiast [inView insertSubview: toViewController.view aboveSubview: fromViewController.view]; po prostu dodaj: [inView addSubview: toViewController.view];

if (self.presenting) {

    [transitionContext.containerView addSubview:toViewController.view];
    // your code

} else {
    // your code
}

Możesz zobaczyć przykład tutaj: link i działa na iOS 7 i iOS 8

CarlosGz
źródło
Powinna to być akceptowana odpowiedź na wykonanie animacji typu UIModalPresentationStyleCustom, ponieważ nie ma potrzeby dodawania fromViewController do containerView. Wystarczy dodać toViewController podczas animacji prezentacji.
Scott Kaiser
To jest naprawdę bardzo pomocne
Dmitry Bondarev
7

Oto wersja poprawki Asha w Objective C.

// my attempt at obj-c version of Ash's fix
UIView *theToView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
[[[UIApplication sharedApplication] keyWindow] addSubview:theToView];
[transitionContext completeTransition:YES]

Musiałem zamienić kolejność i wywołać metodę [przejścieContext completeTransition:] po ponownym dodaniu widoku, aby wyświetlić nowy kontroler widoku z bloku zakończenia zwolnienia innego kontrolera widoku, aby działał poprawnie.

Nie wiem, czy to rozwiąże problem dla wszystkich, ale działa w mojej aplikacji. Twoje zdrowie!

vichudson1
źródło
5

Okazało się, że to działa dobrze dla Obj-C:

    [transitionContext completeTransition:YES];
    if(![[UIApplication sharedApplication].keyWindow.subviews containsObject:toViewController.view]) {
        [[UIApplication sharedApplication].keyWindow addSubview:toViewController.view];
    }

Wydaje się, że działa dobrze na ios7 i ios8.

Piskliwy
źródło
5

Odkryłem, że viewForKey:UITransitionContextToViewKeyzwraca zero na ios8. Więc jeśli jest zerowy, pobieram widok z kontrolera widoku „do”.

Jednak wydaje się, że powoduje to, że widok „do” nie jest przenoszony z kontenera do okna po completeTransition:YESwywołaniu. Więc jeśli viewForKey:UITransitionContextToViewKeyzwraca zero, przewracam się do toVC.viewi śledzę fakt, że zwróciło zero, a po zakończeniu przenoszę go do początkowego nadzoru kontenera (który jest oknem).

Tak więc ten kod działa zarówno na iOS7, jak i iOS8 i powinien działać również na iOS9, nawet jeśli to naprawią, czy nie.

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    // Get the 'from' and 'to' views/controllers.
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    BOOL hasViewForKey = [transitionContext respondsToSelector:@selector(viewForKey:)]; // viewForKey is iOS8+.
    UIView *fromView = hasViewForKey ?
        [transitionContext viewForKey:UITransitionContextFromViewKey] :
        fromVC.view;
    UIView *toView = hasViewForKey ?
        [transitionContext viewForKey:UITransitionContextToViewKey] :
        toVC.view;

    // iOS8 has a bug where viewForKey:to is nil: http://stackoverflow.com/a/24589312/59198
    // The workaround is: A) get the 'toView' from 'toVC'; B) manually add the 'toView' to the container's
    // superview (eg the root window) after the completeTransition call.
    BOOL toViewNilBug = !toView;
    if (!toView) { // Workaround by getting it from the view.
        toView = toVC.view;
    }
    UIView *container = [transitionContext containerView];
    UIView *containerSuper = container.superview; // Used for the iOS8 bug workaround.

    // Perform the transition.
    toView.frame = container.bounds;
    [container insertSubview:toView belowSubview:fromView];
    [UIView animateWithDuration:kDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        fromView.frame = CGRectOffset(container.bounds, 0, CGRectGetHeight(container.bounds));
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:YES];

        if (toViewNilBug) {
            [containerSuper addSubview:toView];
        }
    }];
}
Chris
źródło
3

Odkryłem, że ten błąd (i wiele więcej!) Znika, jeśli ustawisz modalPresentationStyle = UIModalPresentationFullScreen. Oczywiście nadal otrzymujesz niestandardową animację przejścia.

Chris
źródło
2

W tej kwestii też utknąłem. Chciałem stworzyć niestandardowe przejście z półprzezroczystym tłem, w którym nadal mogłem zobaczyć kontroler widoku, z którego pochodzę, ale mam tylko czarne tło. Znalazłem odpowiedź Marka Arona w tym wątku, która pomogła mi, ale jest napisana w celu C, więc oto wersja Swift 3 tej odpowiedzi, którą przetestowałem dla iOS 9 i iOS 10:

  1. Utwórz podklasę UIPresentationController. Zastąp shouldRemovePresentersView na false w następujący sposób:

    class ModalPresentationController: UIPresentationController {
    
    override var shouldRemovePresentersView: Bool {
    return false
    }
    
    override func containerViewWillLayoutSubviews() {
    presentedView?.frame = frameOfPresentedViewInContainerView
    }
    }
  2. W miejscu, w którym tworzysz wystąpienie nowego kontrolera widoku i ustawiasz jego delegata przejścia, wskaż, że chcesz, aby pokazywał niestandardowy styl prezentacji modalnej w następujący sposób:

    let newVC = mainStoryboard.instantiateViewController(withIdentifier: "newVC") as! NewViewController 
    
    newVC.transitioningDelegate = self
    
    newVC.modalPresentationStyle = UIModalPresentationStyle.custom
    
    newVC.modalPresentationCapturesStatusBarAppearance = true //optional
    
    present(newVC, animated: true, completion: nil)
  3. Teraz Zastąp metodę PresentationController UIViewControllerTransitioningDelegate i zwróć niestandardowy UIPresentationController. Miałem swój jako rozszerzenie mojej obecnej klasy:

    extension CurrentViewController: UIViewControllerTransitioningDelegate {
    
    //this is where you implement animationController(forPresented) and animationController(forDismissed) methods
    
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    
    return ModalPresentationController(presentedViewController: presented, presenting: source)
    
    }
    }

Inną rzeczą wartą uwagi jest to, że nie powinieneś próbować odwoływać się do fromView w swojej klasie presentAnimator. To będzie zero i pojawi się błąd w czasie wykonywania. Poza tym, jeśli zaimplementujesz takie rzeczy, otrzymasz niestandardowe przejście z animacją i półprzezroczystym tłem, jeśli je utworzysz.

gwinyai
źródło
Ten iis jest świetnym przykładem tworzenia niestandardowej prezentacji modalnej w Swift 3! Dzięki @gwinyai! Utknąłem w tym, dopóki nie znalazłem przykładu, który pokazał nowy interfejs API Swift 3, presentationController(forPresented presented UIViewController,... ponieważ poprzedni interfejs API Swift nie zakłócił zgodności, ale nie został wywołany.
Natalia
2

Po napotkaniu tego problemu byłem bardzo zdezorientowany, ponieważ niedawno napisałem coś prawie identycznego, co działało dobrze. Przyszedłem tutaj, szukając odpowiedzi, aby znaleźć poprawki, które wyglądają dość hakersko i wydają się nie rozumieć głównej przyczyny ... w rzeczywistości jest to bardzo łatwe do naprawienia.

Niektóre odpowiedzi wspominają o zmianie modalPresentationStylena .overFullScreen. To prawda, też .overCurrentContextby działało. Jest to oczekiwane, a zachowanie dokumentów Apple. Ale dlaczego to nie działa dla wszystkich? Po co ten cały hackerski kod i kombinacje tego z czymś innym i szalonymi rzeczami, których nie powinieneś robić?

Okazuje się, że musisz ustawić styl prezentacji ZANIM WIDOK ZAŁADUJE . Nie później. Zrób to w init, zrób to z poprzedniego kontrolera, lub jak chcesz - o ile jest to przed załadowaniem widoku.

Jordan Smith
źródło
1
.overCurrentContext init
Ustawiłem
1

Użycie nowego UIModalPresentationOverCurrentContext naprawiło to za mnie. Moje pierwotne przejście na iOS 7 polegało tylko na rozmytym tle widoku pod modalem.

malhal
źródło
Z jakiegoś powodu wydaje się, że nie pozwala to na interakcję z widokiem poniżej, gdzie UIModalPresentationCurrentContext zrobił w iOS 7 .. Jakieś myśli?
Christopher Wirt
Hmm dla mnie na iOS 10, .overCurrentContext powoduje ten błąd, ale .fullscreen nie. Przyszedłem tutaj z nadzieją na poprawkę do używania .overCurrentContext, ale jak dotąd nic nie wygląda na to, by działało w iOS 10, z wyjątkiem może podklasy UIPresentationController ...
Natalia
0

Ok, myślę, że rozwiązuję jeden przypadek, w którym „działający animator” przestaje działać poprawnie, gdy tworzysz aplikację w iOS 13 i nowszych.

Env Xcode 11.1, iOS 13.1

Problem

To, co chcę zrobić, jest bardzo proste: mam widok kolekcji, po dotknięciu komórki przechodzi do widoku szczegółowego. Zamiast używać nudnego domyślnego stylu „prezentuj modalnie”, chcę uczynić go bardziej interesującym, więc napisałem animator do przejścia kontrolera widoku.

Skonfigurowałem segue w IB, przeciągając i upuszczając z mojej kolekcji VC do VC szczegółów. Styl przejścia to „Obecny modalnie”, a prezentacja jest ustawiona na „Pełny ekran”.

Kiedy pokazuje widok szczegółowy, wszystko działa zgodnie z oczekiwaniami. Jednak kiedy zamykam widok szczegółów i wracam do widoku kolekcji, widzę tylko animowany widok szczegółów, widok kolekcji po prostu zniknął. Grzebałem tu i tam i mam kilka odkryć

1. tuż po wywołaniu następującego wiersza z funkcji `` animateTransition () '' widok kolekcji zostaje wznowiony i pojawia się

transitionContext.completeTransition(true)

2. Tak długo, jak widok szczegółowy nie obejmuje w pełni widoku kolekcji, widok kolekcji nie zniknie po przejściu z widoku szczegółowego

Rozwiązanie

Szczerze mówiąc, niewiele wiem o tym, jak działa animowane przejście. Więc mogę tylko śledzić ten post i drugi , wypróbuj każdą z odpowiedzi. Niestety żaden z nich nie działa dla mnie. W końcu doszedłem do punktu, w którym jedyne, co mogę zmienić, to styl prezentacji segue w IB (co powinienem był zrobić na samym początku). Kiedy ustawię prezentację na „Pełny ekran”, dzieje się cud i mój problem zostaje rozwiązany. Widok szczegółów może być wyświetlany na pełnym ekranie z animacją, a kiedy zostanie zamknięty, widzę zarówno widok kolekcji jako tło, jak i animowany widok szczegółów.

Potem jeszcze jedno odkrycie na drodze

Aby odwołać się do „toView” i „fromView”, działają obie poniższe metody

Pośrednio:

transitionContext.viewController(forKey: .to)?.view
transitionContext.viewController(forKey: .from)?.view

Bezpośrednio sposób:

transitionContext.view(forKey: .to)
transitionContext.view(forKey: .from)

Ale kiedy zmieniłem styl płynności na „Over Full Screen”, bezpośredni sposób zwracania „nil” zarówno dla „toView”, jak i „fromView” i tylko pośrednio działa, ten problem jest również wspomniany w innym poście , więc myślę, że warto opublikować tutaj moje małe odkrycie.

Mam nadzieję, że w przyszłości będzie to pomocne dla kogoś.

infinity_coding7
źródło
0

Miałem ten sam problem podczas zamykania kontrolera widoku treści.

Moja aplikacja ma ten nadrzędny kontroler widoku, który pokazuje modalnie podrzędny kontroler widoku (prezentujący vc). Następnie, gdy zostanie dotknięty widok podrzędny w childVC, pokazuje inny vc (który nazywam kontrolerem widoku zawartości (przedstawiony vc))

Mój problem polega na tym, że po odrzuceniu contentVC (teraz prezentujący VC), powinien on przejść do podrzędnego VC (teraz przedstawiony VC), ale gdy tylko zakończy się moje niestandardowe przejście, childVC nagle znika, pokazując nadrzędny VC.

To, co zrobiłem, aby rozwiązać ten problem, to

  1. zmienić .modalPresentationStylechildVC prezentowane przez parentVC z domyślnego .automaticna .fullscreen.
  2. Następnie zmieniono również .modalPresentationStylecontentVC na .fullscreen.

To rozwiązuje problem. ale nie pokaże twojego dziecka VC jako arkusza w stylu karty na górze parentVC (podczas używania .overCurrentContextlub automatycznego), co jest nowe w iOS 13.

Chciałbym wiedzieć, czy istnieje rozwiązanie, które zachowa arkusz stylu karty dla childVC, gdy zostanie przedstawiony przez rodzica.

arvinq
źródło
-3

dodaje kontroler widoku jako element podrzędny innego kontrolera widoku.

[self addChildViewController:childViewController];                 

sprawdź i daj mi znać.

Rushabh
źródło
nie rozumiem, czy możesz to opisać za pomocą kodowania?
NiravPatel
sprawdzić tę dokumentację jabłko developer.apple.com/library/ios/featuredarticles/...
Rushabh
to w żaden sposób nie odpowiada na pytanie. ChildViewControllers nie są zaangażowane w żadną część niestandardowych przejść, są one zupełnie innym tematem.
Andras M.