Otrzymałem ten raport o awarii, ale nie wiem, jak go debugować.
Fatal Exception NSInvalidArgumentException
Can't add self as subview
0 ... CoreFoundation __exceptionPreprocess + 130
1 libobjc.A.dylib objc_exception_throw + 38
2 CoreFoundation -[NSException initWithCoder:]
3 UIKit -[UIView(Internal) _addSubview:positioned:relativeTo:] + 110
4 UIKit -[UIView(Hierarchy) addSubview:] + 30
5 UIKit __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 1196
6 UIKit +[UIView(Animation) performWithoutAnimation:] + 72
7 UIKit -[_UINavigationParallaxTransition animateTransition:] + 732
8 UIKit -[UINavigationController _startCustomTransition:] + 2616
9 UIKit -[UINavigationController _startDeferredTransitionIfNeeded:] + 418
10 UIKit -[UINavigationController __viewWillLayoutSubviews] + 44
11 UIKit -[UILayoutContainerView layoutSubviews] + 184
12 UIKit -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 346
13 QuartzCore -[CALayer layoutSublayers] + 142
14 QuartzCore CA::Layer::layout_if_needed(CA::Transaction*) + 350
15 QuartzCore CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 16
16 QuartzCore CA::Context::commit_transaction(CA::Transaction*) + 228
17 QuartzCore CA::Transaction::commit() + 314
18 QuartzCore CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 56
Wersja iOS to 7.0.3. Czy ktoś doświadczył tej dziwnej katastrofy?
AKTUALIZACJA:
Nie wiem, gdzie w moim kodzie spowodowało to awarię, więc nie mogę tutaj umieścić kodu, przepraszam.
Druga aktualizacja
Zobacz odpowiedź poniżej.
ios
iphone
objective-c
Arnol
źródło
źródło
Odpowiedzi:
Spekuluję na podstawie czegoś podobnego, co ostatnio debugowałem ... jeśli wepchniesz (lub pop) kontroler widoku za pomocą Animated: TAK, nie kończy się to od razu, a złe rzeczy się zdarzają, jeśli wykonasz kolejne naciśnięcie lub pop przed animacją kończy. Możesz łatwo sprawdzić, czy tak jest, tymczasowo zmieniając operacje Push i Pop na Animated: NO (tak, aby zakończyły się synchronicznie) i sprawdzając, czy to eliminuje awarię. Jeśli rzeczywiście jest to twój problem i chcesz ponownie włączyć animację, właściwą strategią jest zaimplementowanie protokołu UINavigationControllerDelegate. Obejmuje to następującą metodę, która jest wywoływana po zakończeniu animacji:
Zasadniczo chcesz przenieść część kodu zgodnie z potrzebą do tej metody, aby upewnić się, że żadne inne akcje, które mogłyby spowodować zmianę stosu NavigationController, nie wystąpią, dopóki animacja nie zostanie zakończona, a stos jest gotowy do dalszych zmian.
źródło
[newViewController setLabelTitle:...]
tuż po wywołaniu pushViewController za pomocą.Animated:YES.
I rozwiązałem przeniesienie metody setLabelTitle do viewDidLoad na newViewController. Dzięki, że dałeś mi wskazówkę.Zaczęliśmy również pojawiać się ten problem i było bardzo prawdopodobne, że nasz był spowodowany tym samym problemem.
W naszym przypadku musieliśmy w niektórych przypadkach pobierać dane z zaplecza, co oznaczało, że użytkownik mógł coś dotknąć, a następnie nastąpiło niewielkie opóźnienie przed wystąpieniem push nawigacji. Jeśli użytkownik szybko stukał, może skończyć z dwoma wypchnięciami nawigacji z tego samego kontrolera widoku, co wywołało ten wyjątek.
Nasze rozwiązanie to kategoria w UINavigationController, która zapobiega pchnięciom / wyskakiwaniu, chyba że górny vc jest taki sam z danego punktu w czasie.
plik .h:
plik .m:
Jak dotąd wydaje się, że rozwiązało to problem za nas. Przykład:
Zasadniczo obowiązuje zasada: przed jakimikolwiek opóźnieniami niezwiązanymi z użytkownikiem należy pobrać blokadę z odpowiedniego kontrolera nawigacyjnego i uwzględnić ją w wywołaniu funkcji push / pop.
Słowo „blokada” może być nieco słabo sformułowane, ponieważ może sugerować, że występuje jakaś forma blokady, która wymaga odblokowania, ale ponieważ nigdzie nie ma metody „odblokowania”, prawdopodobnie jest w porządku.
(Na marginesie, „opóźnienia niezwiązane z użytkownikiem” to wszelkie opóźnienia powodowane przez kod, tj. Wszystko asynchroniczne. Użytkownicy dotykający kontrolera nawigacyjnego, który jest animowany, nie liczą się i nie ma potrzeby wykonywania funkcji navigationLock: wersja dla tych przypadków.)
źródło
Ten kod rozwiązuje problem: https://gist.github.com/nonamelive/9334458
Używa prywatnego interfejsu API, ale mogę potwierdzić, że jest bezpieczny w App Store. (Jedna z moich aplikacji korzystających z tego kodu została zatwierdzona przez App Store).
źródło
Opiszę więcej szczegółów na temat tej awarii w mojej aplikacji i oznaczę jako odpowiedź.
Moja aplikacja ma UINavigationController z kontrolerem głównym to UITableViewController, który zawiera listę obiektów notatek. Obiekt notatki ma właściwość content w formacie html. Wybierz notatkę, która trafi do kontrolera szczegółowego.
Szczegółowy kontroler
Ten kontroler ma UIWebView, wyświetla zawartość notatki przekazaną z kontrolera głównego.
Ten kontroler jest delegatem kontrolki widoku internetowego. Jeśli notatka zawiera łącza, stuknij łącze, aby przejść do przeglądarki internetowej w aplikacji.
Powyższy raport o awarii otrzymywałem codziennie. Nie wiem, gdzie w moim kodzie spowodowała awarię. Po kilku badaniach z pomocą użytkownika, w końcu udało mi się naprawić tę awarię. Ta zawartość html spowoduje awarię:
W metodzie viewDidLoad kontrolera detail załadowałem ten html do kontrolki webview, zaraz po tym powyższa metoda delegata została wywołana natychmiast z request.URL jest źródłem iframe (google.com). Ta metoda delegata wywołuje metodę pushViewController w trybie viewDidLoad => awaria!
Naprawiłem tę awarię, sprawdzając navigationType:
Mam nadzieję że to pomoże
źródło
viewDidLoad
?Miałem ten sam problem, co po prostu zadziałało, to zmiana Animated: Yes na Animated: No.
Wygląda na to, że problem był spowodowany tym, że animacja nie kończyła się na czas.
Mam nadzieję, że to komuś pomoże.
źródło
Aby odtworzyć ten błąd, spróbuj wypchnąć dwa kontrolery widoku w tym samym czasie. Albo pchanie i popychanie jednocześnie. Przykład:
Stworzyłem kategorię, która przechwytuje te połączenia i zabezpiecza je, upewniając się, że żadne inne naciśnięcia nie mają miejsca, gdy jedno jest w toku. Po prostu skopiuj kod do swojego projektu, a dzięki zamianie metod będziesz gotowy.
źródło
popToRootViewController
lubpopToViewController:
kiedy jesteś już na kontrolerze widoku głównego lub na viewController być pojawiło się, todidShowViewController
nie będzie się nazywać i będzie tkwić wviewTransitionInProgress
.self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES];
Kiedy aparat rozpoznawania został wyłączony? A skąd wiesz, jaki powinien być delegat? Z tymi liniami, dla mnie przerywa to gest popu po jednym wyskoku.Właśnie doświadczyłem tego problemu. Pokażę Ci mój kod:
Błąd pojawia się z powodu tej linii:
Nie możesz dodać siebie do widoku podrzędnego. Zmieniłem linię kodu na:
Błąd zniknął i mogłem zobaczyć oba widoki. Pomyślałem, że to pomoże każdemu, kto chce zobaczyć przykład.
źródło
Wyszukaj w swoim kodzie „addSubview”.
W jednym z miejsc, które wywołałeś tę metodę, próbowałeś dodać widok do własnej tablicy widoków podrzędnych za pomocą tej metody.
Na przykład:
Lub:
źródło
[View2.view addSubview:View2.view]
ten sposób dodając self jako podwidok.Myślę, że wypychanie / wyskakiwanie kontrolerów widoku z animacją w dowolnym momencie powinno być idealnie w porządku, a SDK powinien łaskawie obsługiwać kolejkę wywołań za nas.
Dlatego tak się nie dzieje i wszystkie rozwiązania starają się ignorować kolejne wypychania, co można uznać za błąd, ponieważ ostateczny stos nawigacji nie jest zgodny z zamierzeniem kodu.
Zamiast tego zaimplementowałem kolejkę połączeń push:
Nie obsługuje mieszanych kolejek push i pop, ale jest dobrym początkiem do naprawienia większości naszych awarii.
Streszczenie: https://gist.github.com/rivera-ernesto/0bc628be1e24ff5704ae
źródło
Przepraszam za spóźnienie na przyjęcie. Niedawno miałem ten problem, w którym mój pasek nawigacyjny przechodzi w stan uszkodzony z powodu jednoczesnego naciskania więcej niż jednego kontrolera widoku. Dzieje się tak, ponieważ drugi kontroler widoku jest wypychany, gdy pierwszy kontroler widoku nadal jest animowany. Biorąc pod uwagę odpowiedź nonamelive, wymyśliłem proste rozwiązanie, które działa w moim przypadku. Wystarczy
UINavigationController
podklasować i zastąpić metodę pushViewController oraz sprawdzić, czy poprzednia animacja kontrolera widoku została jeszcze zakończona. Możesz posłuchać zakończenia animacji, ustawiając swoją klasę jako delegataUINavigationControllerDelegate
i ustawiając delegata naself
.Wrzuciłem tutaj streszczenie aby uprościć sprawę.
Po prostu upewnij się, że ustawiłeś tę nową klasę jako NavigationController w swoim scenorysie.
źródło
Bazując na @RobP, stworzyłem podklasę UINavigationController , aby zapobiec takim problemom. Obsługuje pchanie i / lub popping i możesz bezpiecznie wykonać:
Jeśli flaga 'acceptConflictingCommands' to prawda (domyślnie), użytkownik zobaczy animowane wypychanie vc1, vc2, vc3, a następnie zobaczy animowane wyskakiwanie vc3. Jeśli „acceptConflictingCommands” ma wartość false, wszystkie żądania push / pop będą odrzucane, dopóki vc1 nie zostanie w pełni przekazane - stąd pozostałe 3 wywołania zostaną odrzucone.
źródło
animated:true
flagą?Rozwiązanie Nonamelive jest niesamowite. Ale jeśli nie chcesz używać prywatnego interfejsu API, możesz po prostu zastosować tę
UINavigationControllerDelegate
metodę lub zmienić animowanyYES
naNO
. Oto przykład kodu, który możesz dziedziczyć. Mam nadzieję, że to pomocne :)https://github.com/antrix1989/ANNavigationController
źródło
Dużo przeszukiwałem ten problem, być może wciskając dwa lub więcej VC w tym samym czasie, co powoduje problem z animacją wypychania, możesz odnieść się do tego: Can't Add Self as Subview 崩溃 解决 办法
po prostu upewnij się, że w tym samym czasie jest jeden VC na postępie przejścia , powodzenia.
źródło
Czasami omyłkowo próbowałeś dodać widok do własnego widoku.
zmień to na swój widok podrzędny.
źródło
Ja też napotkałem ten problem. Kiedy przeprowadziłem analizę dziennika Firebase, odkryłem, że ten problem występuje tylko wtedy, gdy aplikacja jest uruchamiana na zimno. Więc napisałem demo które może odtworzyć tę awarię.
.
Odkryłem również, że gdy wyświetlany jest główny kontroler widoku okna, wykonanie wielu naciśnięć nie spowoduje ponownie tego samego problemu. (Możesz skomentować testColdStartUp (rootNav) w AppDelegate.swift i odkomentować komentarz testColdStartUp () w ViewController.swift)
ps: Przeanalizowałem miejsce tej awarii w mojej aplikacji. Gdy użytkownik kliknie powiadomienie push, aby uruchomić aplikację na zimno, aplikacja nadal znajduje się na stronie uruchamiania i klika kolejne naciśnięcie, aby przejść. W tej chwili w aplikacji może pojawić się awaria. Moje obecne Rozwiązanie polega na zapisaniu w pamięci podręcznej zimnego startu łącza push lub uniwersalnego, aby otworzyć stronę skoku aplikacji, poczekaniu na wyświetlenie kontrolera rootview, a następnie opóźnieniu wykonania.
źródło
wypróbuj nawigację metodą opóźnienia, aby ukończyć ostatnią animację nawigacji,
[self performSelector:<#(SEL)#> withObject:<#(id)#> afterDelay:<#(NSTimeInterval)#>]
źródło
Widok nie może być dodany jako widok podrzędny.
Widoki zachowują hierarchię nadrzędny-podrzędny, więc jeśli dodasz widok jako widok podrzędny sam w sobie, przejdzie on przez wyjątek.
jeśli klasa to UIViewController, aby uzyskać jej widok, użyj self.view.
jeśli klasa jest klasą UIView, aby uzyskać jej widok, użyj self.
źródło
nie możesz dodać siebie jako widoku podrzędnego, jeśli ma to być klasa UiViewController. możesz dodać siebie jako widok podrzędny, jeśli ma to być klasa UiView.
źródło
Jeśli chcesz dodać widok podrzędny do widoku, możesz to zrobić w ten sposób;
źródło