Adaptacyjne przechodzenie w scenorysie Xcode 6. Czy wypychanie jest przestarzałe?

121

Konstruktor interfejsu Xcode 6 ma domyślnie nowe pole wyboru „użyj klas wielkości”. Sprawia, że ​​widoki są adaptacyjne. wprowadź opis obrazu tutaj

Kiedy próbuję przejść między 2 widokami w moim storyboardzie, mam nowe opcje: wprowadź opis obrazu tutaj

zamiast starego:

wprowadź opis obrazu tutaj

Teraz mamy „pokaż” i „obecny modalnie” zamiast „wypychania” i „modalnie”. Stare opcje są oznaczone jako przestarzałe. Wybrałem opcję "pokaż", ponieważ w ustawieniach segue nazywa się to "pokaż (np. Push)

wprowadź opis obrazu tutaj

Ale to nie powoduje nacisku. Animacja przejścia wygląda jak slajd od dołu (modalna), a pasek nawigacji znika.

Pytanie brzmi: Jak sprawić, by „show” działało jak push? Czy jest to możliwe, czy powinienem zamiast tego użyć opcji „push (przestarzałe)”? Gdzie mogę znaleźć informacje o nowych typach segue? Jedyne, co znalazłem w bibliotece deweloperskiej iOS8, to Storyboards Help You Design Your User Interface, ale nie ma informacji o „show” segue.

AKTUALIZACJA

Próbowałem stworzyć nowy projekt i „show” działa jak „push”. Myślę, że problem w moim projekcie może wynikać z tego, że ponownie używam kontrolera nawigacji z takim kodem, ale nie wiem, jak to naprawić.

if ( [segue isKindOfClass: [SWRevealViewControllerSegue class]] ) {
    SWRevealViewControllerSegue *swSegue = (SWRevealViewControllerSegue*) segue;
    
    swSegue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) {
        
        UINavigationController* navController = (UINavigationController*)self.revealViewController.frontViewController;
        [navController setViewControllers: @[dvc] animated: NO ];
        [self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated: YES];
    };
    
}

Następnie próbuję wypchnąć NewViewController po MainViewController wprowadź opis obrazu tutaj

AKTUALIZACJA 2:

Wydaje mi się, że dotyczy to tylko iOS 7, iOS 7.1.

John Kakon
źródło

Odpowiedzi:

97

Tak, użyj „Pokaż” zamiast „Wypchnij”

Jak sprawić, by „show” działało jak push? Czy jest to możliwe, czy powinienem zamiast tego użyć opcji „push (depricated)”?

Powinno; dla mnie to robi. Używam Xcode 6 beta 2 i do testów użyłem szablonu pojedynczego widoku (wywołując gotowy kontroler widoku w IB „VC_A”). Następnie dodałem kolejny kontroler widoku („VC_B”). Następnie dodałem przycisk na VC_A, aby pokazać VC_B i inny z VC_B z powrotem do VC_A. Kiedy dodam kontroler nawigacji jako początkowy kontroler widoku w scenorysie i ustawię VC_A jako rootViewController, zarówno „push”, jak i „show” mają ten sam efekt. Jeśli nie mam początkowego kontrolera nawigacyjnego i używam „show”, otrzymuję to, co opisałeś w tym, że VC_B przesuwa się w górę od dołu. Jeśli próbuję „pchać”, dochodzi do awarii, ponieważ aby wykonać pchnięcie, muszę mieć kontroler nawigacji.

Gdzie mogę znaleźć informacje o nowych typach segue?

Więc znalazłem trochę informacji w sesji „Co nowego w Interface Builder” tutaj . Jeśli spojrzysz na slajdy, zobaczysz jeden slajd (41), wspomnij o zmianie. Oglądając to nagranie z sesji, możesz przejść do minuty 38:00, gdzie zaczynają mówić o przejściach adaptacyjnych. Wyjaśniają, że na przykład adaptacyjna płynność „pokaż” bierze pod uwagę kontekst przy podejmowaniu decyzji, jak przeprowadzić prezentację nowego kontrolera widoku.

Spencer Hall
źródło
Dziękuję za Twoją odpowiedź. Próbowałem stworzyć nowy projekt i "show" naprawdę działa jak push z kontrolerem nawigacyjnym. W moim projekcie mam skomplikowaną strukturę z bocznym paskiem z tej lekcji appcoda.com/ios-programming-sidebar-navigation-menu i push działa, pokaż nie. Być może powodem jest ponowne użycie kontrolera nawigacji. Zaktualizowałem moje pytanie o kod ponownego wykorzystania.
John Kakon
Więc pierwszą rzeczą, którą widzę, patrząc na twoją aktualizację, jest to, że używasz oczekiwanego niestandardowego ściegu: „SWRevealViewControllerSegue”, więc tak, prawdopodobnie nie ma sensu używać „show” lub „push” w twoim przypadku, ponieważ są one wbudowane segues, podczas gdy chcesz uruchomić swój własny niestandardowy kod. Kiedy pobieram przykładowy projekt, pokazuje on nawet wybór „niestandardowego” na segue w pliku storyboardu.
Spencer Hall
Nie miałem na myśli tego odcinka. Mówiłem o utworzeniu nowego UIViewController obok "MainViewController" (ma on kontroler nawigacji, a moja "aktualizacja" pokazuje tylko, jak mogę go tam dostać) i próbach wypchnięcia nowego widoku do MainViewController. Zrzut ekranu scenorysu Będę wdzięczny, jeśli włączysz "użyj klas wielkości" w przykładowym projekcie, utworzysz NewViewController i spróbujesz przejść między MainViewController i NewViewController przez "show". Zobaczysz, o czym mówię.
John Kakon,
Zrobiłem to właśnie teraz i przynajmniej dla mnie "show" działało jak "push".
Spencer Hall
9
Możesz obejść ten błąd, upewniając się, że wszystkie drogi do kontrolera widoku mają UINavigationController w swoim katalogu głównym. Nawet jeśli oznacza to umieszczenie kontrolera UINavigationController w swoim storyboardzie, do którego nigdy nie uzyskasz dostępu. Wygląda na to, że okablowanie służy do wnioskowania o zachowaniu.
Scott Robertson
33

Jest już zaakceptowana odpowiedź, ale chciałem podać trochę więcej informacji, być może informacji, które wcześniej nie były dostępne.

Jak wspomniano wcześniej, segmenty „push” i „modal” zostały wycofane i zostały zastąpione odpowiednio przez „show” i „present modally”. Zgodnie z dokumentacją Apple, nowe segmenty zostały dodatkowo podzielone na segmenty, które dostosowują się do klas wielkości. Starsze powinny być używane tylko do obsługi wersji iOS starszych niż iOS 8.

Dokument w poniższym linku wyjaśnia to i opisuje wszystkie dostępne segmenty, stare i nowe.

Dodawanie przejścia między scenami w scenorysie

W przypadku zmiany adresu URL w przyszłości, oto wyjaśnienie podane dla każdego nowego segmentu:

Pokazać

Przedstaw zawartość w obszarze szczegółowym lub wzorcowym w zależności od zawartości ekranu. Jeśli aplikacja wyświetla widok główny i szczegółowy, zawartość jest wypychana do obszaru szczegółów. Jeśli aplikacja wyświetla tylko wzorzec lub szczegóły, zawartość jest wypychana na wierzch bieżącego stosu kontrolera widoku.

Pokaż szczegół

Przedstaw treść w obszarze szczegółów. Jeśli aplikacja wyświetla widok główny i szczegółowy, nowa zawartość zastępuje bieżące szczegóły. Jeśli aplikacja wyświetla tylko wzorzec lub szczegóły, zawartość zastępuje górę bieżącego stosu kontrolera widoku.

Prezentuj modalnie

Przedstaw treść modalnie. Dostępne są opcje wyboru stylu prezentacji (UIModalPresentationStyle) i stylu przejścia (UIModalTransitionStyle).

Obecny jako Popover

Przedstaw zawartość jako popover zakotwiczony w istniejącym widoku. Istnieje możliwość określenia możliwych kierunków strzałki wyświetlanej na jednej krawędzi widoku podręcznego (UIPopoverArrowDirection). Istnieje również opcja określenia widoku zakotwiczenia.

alondono
źródło
24

tldr; Usuń płynnie, który nie jest poprawnie wypychany i utwórz go ponownie w scenorysie, przeciągając z UIView / UIControl do kontrolera widoku docelowego.

Nie ma nic złego w innych odpowiedziach, ale ta wyjaśnia, co się dzieje, w jaki sposób można zweryfikować, że tak się dzieje i jak złagodzić problem w przyszłości.

tło

W moim przypadku żaden z moich kanałów pokazowych nie działał, mimo że miałem już UINavigationController jako mój początkowy kontroler widoku (z moją zawartością UIViewController jako root).

Dlaczego i jak psuje się Show Segue

Przejście pokazu zrywa się, gdy ma akcję skojarzoną z płynnością w źródłowym pliku XML scenorysu. Typowy scenariusz, który to powoduje, może mieć miejsce, jeśli przedefiniowałeś płynność z ręcznej ścieżki poprzednio wywołanej w kodzie. Pozostawia to następujące bity w pliku storyboard xml.

<connections>
    <segue destination="85t-Z1-hxf" kind="show" identifier="ToOptions" action="showDetailViewController:sender:" id="gdZ-IX-KcN">
</connections>

Nota Bene Aby wyświetlić scenorys jako xml; Kliknij prawym przyciskiem myszy plik storyboardu i wybierz Otwórz jako> Kod źródłowy . Aby przywrócić, użyj Otwórz jako> Konstruktor interfejsu - Storyboard

Aby dostosować się do wszelkich niestandardowych działań podczas korzystania z płynności ze scenorysu, można po prostu dotknąć przygotujForSegue i przechwycić kontroler widoku docelowego i wywołać dowolne metody z tej lokalizacji. W każdym razie efektem ubocznym tego małego błędu (błąd polega na tym, że kiedy przedefiniujesz ścieżkę, nie jest ona poprawnie ustawiona w xml ~, tj. Akcja pozostaje nawet po zmianie ścieżki na taką, która działa z UIView (lub UIControl) do docelowego kontrolera widoku).

Niestety najbardziej bezpośrednie rozwiązanie zawodzi. Więc samo usunięcie atrybutu xml dla akcji z poziomu Storyboard NIE rozwiąże problemu. Zamiast tego należy po prostu usunąć i odtworzyć płynność w scenorysie.

Po odtworzeniu plik xml storyboardu nie będzie już miał akcji związanej z konkretnym płynem, a Pokaz zostanie wykonany jako Push.

Przykładowy plik XML dla prawidłowego Pokaż płynne

  <connections>
    <segue destination="RbV-Au-WV9" kind="show" identifier="ToOptions" id="5dm-os-bcS"/>
  </connections>

Łagodzenie

Aby zapobiec ponownemu wystąpieniu, wystarczy trzymać się nie-ręcznych fragmentów scenorysu, jeśli to możliwe, za pomocą przygotowaniaForSegue, aby dodać wymagane akcje na podstawie kontrolera widoku docelowego. Lub jeśli musisz wymieszać i dopasować, podejmij środki ostrożności, aby sprawdzić, czy Twoje segmenty Pokazu nie mają żadnych działań dołączonych do pliku storyboardu xml. Jeśli masz do czynienia ze starszymi projektami, powinieneś zwrócić szczególną uwagę na kod źródłowy Storyboard, ponieważ odkryłem kilka problemów.

Tommie C.
źródło
3
Po godzinach zaoszczędziło mi to prawdopodobnie kilku innych godzin. Oto co sugerowałbym na podstawie powyższego: Otwórz scenorys jako kod źródłowy , wyszukaj kind = "show" i zobacz, czy wiersz zawiera coś takiego jak action = "showDetailViewController: sender:" , jeśli tak, usuń wszystko z akcji = do zamknięcia " . Mam naprawdę ogromną scenorys, a sekwencja, której to dotyczy, nie zawierała tego parametru akcji, ale inna niepowiązana linia tak. Po usunięciu akcji wszystkie sekwencje adaptacyjne działały ponownie zgodnie z oczekiwaniami. Po prostu usunięcie zmienionego seque nie działało t praca.
Marcus
To też mnie uratowało.
Adam Bardon
1
Usunięcie atrybutu akcji w XML zadziałało dla mojego przyjaciela.
Brent Royal-Gordon
mnie też uratował
Jimmy George Thomas
Usunięto atrybut akcji w pliku xml i voila, działa. Bez tego postu nigdy nie znalazłbym problemu.
krizzzn
20

Jak skomentował tutaj Scott Robertson , wygląda to na błąd w iOS 7.

Wygląda na to, że w iOS 8 przejście jest wywnioskowane w czasie wykonywania (prawidłowe zachowanie), podczas gdy w iOS 7 przejście jest wywnioskowane w czasie projektowania (zachowanie błędne).

Najprostszym obejściem jest dodanie nieużywanego kontrolera nawigacji do scenorysu i połączenie go w taki sposób, aby dany kontroler widoku był częścią tego kontrolera nawigacji. W rzeczywistości nie musisz tworzyć instancji kontrolera nawigacji, wystarczy, że błędny kontroler widoku będzie wiedział, że jest osadzony w kontrolerze nawigacji.

Uwaga: Symulacja paska nawigacji nie jest wystarczająca do tych celów; musisz mieć kontroler nawigacyjny w swoim stosie push.

Aby odtworzyć błąd:

  1. Utwórz nowy scenorys wykorzystujący klasy rozmiarów.
  2. Utwórz dwa kontrolery widoku (bez kontrolerów nawigacji).
  3. Spraw, aby pierwszy kontroler widoku wyświetlał drugi kontroler widoku za pośrednictwem pokazu (np. Push) ścieżki połączonej z przyciskiem.
  4. W kodzie pokaż pierwszy kontroler widoku, ale osadz go w kontrolerze nawigacji za pomocą initWithRootViewController:metody.
  5. Uruchom aplikację na iOS 7.
  6. Dotknij przycisku, który powinien wykonać naciśnięcie.
  7. Dostaniesz przejście modalne zamiast wypychania w iOS 7. W iOS 8 uzyskasz poprawne zachowanie wypychania.

wprowadź opis obrazu tutaj

Aby naprawić błąd:

  1. Dodaj kontroler nawigacji do scenorysu i ustaw pierwszy kontroler widoku jako główny kontroler widoku. (Uwaga: dodanie drugiego jako głównego kontrolera widoku NIE naprawi tego błędu.)
  2. Nadaj mu identyfikator śmieciowy, aby pominąć ostrzeżenie o niedostępności kontrolera nawigacji i udokumentować, że istnieje wyłącznie jako obejście. (np workaround for show segues in iOS 7.).

wprowadź opis obrazu tutaj

Zwróć uwagę, jak kontroler nawigacji został dodany na drugim obrazku i jak nie ma żadnych przychodzących strzałek (tj. Nie ma innego sposobu na jego utworzenie niż użycie identyfikatora kontrolera widoku).

Rozsądne
źródło
1
Dzięki, sztuczka z NavigationController zrobiła to za mnie podczas zamiany katalogu głównego NavigationControllers ViewController-Stack
Peter Pint
1
Najlepsze rozwiązanie. Dzięki
Илья Голованов
13

Wiem, że się spóźniłem, ale chciałem podzielić się tym, czego się nauczyłem. W rzeczywistości jest to błąd i nadal występuje (18.12.2014).

Napisałem o tym artykuł tutaj .

Jest łatwo odtwarzalny; na iOS8 będzie działać dobrze, a nawet w iOS7.x, o ile nie wepchniesz programowo kontrolera widoku do stosu przed wywołaniem Showsegue.

Jeśli pchasz na stos tylko za pomocą połączeń scenorysu, zadziała; ale najwyraźniej jeśli w jakiś sposób wypchniesz za pomocą kodu, navigationControllerwłaściwość wypchniętego elementu UIViewControllerbędzie miała wartość, nila kiedy wywołasz Show, przyjmie, że jest modalna, ponieważ nie ma nawigacji do kontrolowania stosu.

Jedynym jak dotąd obejściem jest nie przekazywanie za pośrednictwem kodu (niewykonalne) lub używanie obecnie przestarzałego Push.

Złożyłem radar (link do artykułu). Zapraszam do zgłaszania duplikatów z nadzieją, że Apple naprawi ten problem.

esttorhe
źródło
3
To był dokładnie mój problem, dzięki! użycie przestarzałego push wydaje się być mniejszym złym rozwiązaniem w tym przypadku.
Moshe Gottlieb
Innym obejściem jest dodanie (nieużywanego) kontrolera nawigacji do scenorysu. Zobacz stackoverflow.com/questions/24184003/…
Senseful
Nie! Dzieje się to tutaj, a przyczyną nie jest programistyczny nacisk. W moim przypadku wykonuję Showodcinek, to na drugim odcinku Showjest prezentowany modalnie, Rozwiązanie?
Frade
@Frade Czy możesz utworzyć link do repozytorium github, w którym można to odtworzyć? Czy korzystasz z której wersji systemu iOS?
esttorhe
projekt prywatny .. Zdarza się to podczas próby wykonania (sekundy) Showkontrolera viewController na iOS 7
Frade.
2

Miałem ten sam problem z plikami segue w Xcode 7 i iOS 7.1.2. Pokaż segmenty (nowa funkcja z iOS 8) działa jak modalne segmenty w iOS 7 i nie pozwala na wypychanie kontrolerów widoku do stosu kontrolera nawigacji podczas definiowania typu ściegu za pomocą Xcode w Storyboard. Dlatego twój self.navigationController zwróci nil, ponieważ kontroler widoku nie został umieszczony na stosie i nie możesz go zdjąć.

Nie rozumiem, dlaczego Apple nie dodał żadnych powiadomień dla tego przypadku w Xcode, kiedy chcesz, aby Twoja aplikacja działała na iOS 7. Mówią, że metoda Push jest przestarzała, ale Show nie działa poprawnie z iOS 7.

Co zrobiłem, aby rozwiązać problem:

Stworzyłem klasę MYShowSegue z .h

#import <UIKit/UIKit.h>

@interface MYShowSegue : UIStoryboardSegue

@end

Oraz plik .m z tylko jedną metodą wykonania :

#import "MYShowSegue.h"

@implementation MYShowSegue

- (void) perform {

    if ([[[self sourceViewController] navigationController] respondsToSelector:@selector(showViewController:sender:)]) {

        id sender = nil;
        [[[self sourceViewController] navigationController] showViewController:[self destinationViewController] sender:sender];
    }else{

        [[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:YES];
    }
}

@end

Następnie musisz ustawić niestandardowy typ dla każdego ściegu w swoim Storyboard i wybrać dla niego nową klasę, w moim przypadku był to MYShowSegue.

Przykład niestandardowego odcinka

To rozwiązanie pomoże Ci uzyskać pełną obsługę aplikacji na iOS 7, będą one używać metody pushViewController do przesyłania Twoich widoków, a dla iOS 8,9 itd. Twoja segue będzie działać z nową metodą (iOS 8) showViewController

Nie zapomnij zrobić tego samego ze wszystkimi swoimi plikami w Storyboard.

Igor Tichonow
źródło
Niezłe rozwiązanie - u mnie zadziałało (zamiast dodawać „nieużywany” kontroler nawigacyjny ...
Laurenz Glück
1

Nadal dzieje się to w iOS 10.x.

Usunięcie i przywrócenie segmentów nie rozwiązało niczego dla mnie:

Problem: Wymagana funkcjonalność to 7 segmentów, które działają tylko jako „wypychanie” (w rzeczywistości szczegół pokazu), ale w rzeczywistości tylko pierwsza dodana przeze mnie ścieżka będzie wypychać, pozostałe będą zachowywać się modalnie. Dzieje się tak pomimo tego, że Interface Builder opisuje każdy z segmentów identycznie.

Rozwiązanie: musiałem dodać akcję do 6 segmentów, które jej nie miały.

Oryginalny plik XML scenorysu

<connections>
  <segue destination="tIr-4a-WfZ" kind="showDetail" identifier="A" action="showViewController:sender:" id="8yd-Ne-7KA"/>
  <segue destination="4mB-YE-5dM" kind="showDetail" identifier="B" id="Uod-JC-786"/>
  <segue destination="Qh5-bJ-KcE" kind="showDetail" identifier="C" id="3PW-nV-hWl"/>
  <segue destination="EI6-f4-QBB" kind="showDetail" identifier="D" id="WUK-ju-KDm"/>
  <segue destination="nTz-N4-fpW" kind="showDetail" identifier="E" id="Id6-bW-Huc"/>
  <segue destination="JEp-CH-6dW" kind="showDetail" identifier="F" id="G0L-XW-7f4"/>
  <segue destination="AET-S1-O6h" kind="showDetail" identifier="G" id="3NK-93-wTy"/>
</connections>

Zmieniłem to, dodając showViewController: sender

<connections>
  <segue destination="tIr-4a-WfZ" kind="showDetail" identifier="A" action="showViewController:sender:" id="8yd-Ne-7KA"/>
  <segue destination="4mB-YE-5dM" kind="showDetail" identifier="B" action="showViewController:sender:" id="Uod-JC-786"/>
  <segue destination="Qh5-bJ-KcE" kind="showDetail" identifier="C" action="showViewController:sender:" id="3PW-nV-hWl"/>
  <segue destination="EI6-f4-QBB" kind="showDetail" identifier="D" action="showViewController:sender:" id="WUK-ju-KDm"/>
  <segue destination="nTz-N4-fpW" kind="showDetail" identifier="E" action="showViewController:sender:" id="Id6-bW-Huc"/>
  <segue destination="JEp-CH-6dW" kind="showDetail" identifier="F" action="showViewController:sender:" id="G0L-XW-7f4"/>
  <segue destination="AET-S1-O6h" kind="showDetail" identifier="G" action="showViewController:sender:" id="3NK-93-wTy"/>
</connections>
Damo
źródło