Niezbalansowane wywołania rozpoczynające / kończące przejścia między wyglądami dla <UITabBarController: 0x197870>

119

Czytałem SO o tym, że inny użytkownik napotkał podobny błąd , ale ten błąd jest w innym przypadku.

Otrzymałem tę wiadomość, gdy początkowo dodałem kontroler widoku:

Unbalanced calls to begin/end appearance transitions for 
<UITabBarController: 0x197870>

Struktura aplikacji jest następująca:

Mam 5-zakładkowy TabBarController połączony z 5 kontrolerami widoku. Na początkowej karcie wyświetlania wzywam nowy kontroler widoku do nakładki jako wprowadzenie do aplikacji.

Używam tego kodu do wywołania kontrolera widoku wprowadzenia:

IntroVC *vc = [[IntroVC alloc] init];
[self presentModalViewController:vc animated:YES];
[vc release]; 

Po pojawieniu się tego IntroVCkontrolera widoku pojawia się powyższy błąd.

ps Używam SDK xCode 4.2 i iOS 5.0, rozwijam aplikację na iOS 4.3.

Raptor
źródło
Cześć Shivan, mam z tobą ten sam problem. Ale nadal nie mogę tego naprawić po obejrzeniu poniższych odpowiedzi. Czy mogę wiedzieć, gdzie dzwonisz do kontrolera widoku wprowadzenia?
ZYiOS

Odpowiedzi:

98

Nie widząc więcej otaczającego kodu, nie mogę udzielić jednoznacznej odpowiedzi, ale mam dwie teorie.

  1. Nie używasz UIViewController„s wyznaczony inicjatorinitWithNibName:bundle: . Spróbuj go użyć zamiast po prostu init.

  2. Ponadto, selfmoże być jednym z kontrolerów widoku na karcie Pasek kontrolera. Zawsze prezentuj kontrolery widoku z najwyższego kontrolera widoku, co oznacza w tym przypadku poproś kontroler paska kart o przedstawienie kontrolera widoku nakładki w imieniu kontrolera widoku. Nadal możesz zachować wszystkich delegatów wywołania zwrotnego do kontrolera widoku rzeczywistego, ale musisz mieć kontroler paska kart obecny i odrzucić.

Jesper
źródło
2
# 1 naprawiłem ten problem za mnie, użyłem initWithNibName: nil bundle: nil zamiast init.
Hua-Ying
172
Możesz wygenerować to ostrzeżenie, przedstawiając modalne vc przed zakończeniem inicjalizacji aplikacji. tj. Uruchom aplikację szablonu aplikacji z zakładkami i przedstaw modalne vc na górze self.tabBarController jako ostatnią linię w aplikacji: didFinishLaunching. Pojawia się ostrzeżenie. Rozwiązanie: pozwól stosowi rozwinąć się najpierw, przedstaw modalne vc w innej metodzie, wywołanej za pomocą performSelector withDelay: 0.0.
danh
9
A oto kolejne pytanie wyjaśniające, dlaczego działa performSelector withDelay. stackoverflow.com/questions/1922517/...
Fatih
1
Rozwiązanie Danha zadziałało dla mnie, ale musiałem użyć 0,1 zamiast 0,0.
Brandon O'Rourke,
11
Zamiast używać performSelectorWithDelay o wartości zero, wykonaj to w viewDidAppear zamiast viewDidLoad lub czymkolwiek.
tooluser
40

Naprawiłem ten błąd, zmieniając animację z TAK na NIE.

Z:

[tabBarController presentModalViewController:viewController animated:YES];

Do:

[tabBarController presentModalViewController:viewController animated:NO];
PokerIncome.com
źródło
4
To rozwiązuje problem, jeśli nie zależy Ci na animacji, ale jeśli potrzebujesz animacji: TAK, wypróbuj komentarz danh na temat zaakceptowanej odpowiedzi: stackoverflow.com/questions/7886096/ ...
wxactly
3
FYI: presentModalViewController: animated: został wycofany w iOS6.
ZS
16

Jak opublikował danh

Możesz wygenerować to ostrzeżenie, przedstawiając modalne vc przed zakończeniem inicjalizacji aplikacji. tj. Uruchom aplikację szablonu aplikacji z zakładkami i przedstaw modalne vc na górze self.tabBarController jako ostatnią linię w aplikacji: didFinishLaunching. Pojawia się ostrzeżenie. Rozwiązanie: pozwól stosowi rozwinąć się najpierw, przedstaw modalne vc w innej metodzie, wywołanej za pomocą performSelector withDelay: 0.0

Spróbuj przenieść metodę do viewWillAppear i chroń ją, aby została wykonana tylko raz (zalecamy ustawienie właściwości)

Peter Lapisu
źródło
Dlaczego viewWillAppeara nie viewDidAppear?
CyberMew
6

Innym rozwiązaniem w wielu przypadkach jest upewnienie się, że przejście między UIViewControllers nastąpi po zakończeniu nieodpowiedniej (np. Podczas inicjalizacji) procedury, wykonując:

__weak MyViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf presentViewController:vc animated:YES];
});

Jest to ogólne dla również pushViewController:animated:itp.

mllm
źródło
4

Miałem ten sam problem. Wywołałem metodę viewDidLoadw moim pierwszymUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

    [self performSelector:@selector(loadingView)
               withObject:nil afterDelay:0.5];
}

- (void)loadingView{

    [self performSegueWithIdentifier:@"loadedData" sender:self];
}

Wewnątrz sekundy UIViewControllerzrobiłem to samo również z opóźnieniem 0,5 sekundy. Po zmianie opóźnienia na wyższą wartość działało dobrze. To tak, jakby seksu nie można było wykonać zbyt szybko po kolejnym.

Alex Cio
źródło
7
Metoda viewDidAppear cyklu życia widoku służy właśnie do tego celu i byłaby bardziej niezawodna niż wprowadzenie sztucznego opóźnienia fwiw.
tooluser
1
To jest prawidłowa odpowiedź, z tym że opóźnienie wynoszące 0 wystarczy, aby poczekać, aż kontroler nawigacyjny będzie gotowy do nowej nawigacji.
malhal
Jest całkowicie w porządku, musisz to wywołać w środku, viewDidAppearaby UINavigationControllerbyć gotowym do obsługi. Zmieniłem swój post na ten;)
Alex Cio
Wydaje mi się, że to powinno zostać przeniesione do viewWillAppear, wtedy nie musisz się martwić, czy widok został zainicjowany, czy nie.
horsejockey
3

Miałem ten sam problem, gdy muszę przedstawić mój kontroler widoku logowania z innego kontrolera widoku. Jeśli użytkownik nie jest autoryzowany, zrobiłem to w metodzie ViewDidLoad mojego innego kontrolera widoku (jeśli nie jest autoryzowany -> presentModalViewController). Kiedy zacząłem robić to metodą ViewDidAppear, rozwiązałem ten problem. Myślę, że ViewDidLoad tylko inicjalizuje właściwości, a następnie zaczyna się faktyczny algorytm wyświetlania! Dlatego musisz użyć metody viewDidAppear, aby wykonać przejścia modalne!

Tolusha
źródło
3

Miałem ten problem z powodu literówki:

override func viewDidAppear(animated: Bool) {
    super.viewWillAppear(animated)

zamiast

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

Dzwonił „WillAppear” w super zamiast „DidAppear”

Adriano Spadoni
źródło
2

Miałem wiele problemów z tym samym problemem. Rozwiązałem to przez

  1. Inicjowanie ViewController przy użyciu metody instancji StoryboadViewControllerWithIdentifier. to znaczyIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

Mam kontroler widoku w moim storyboardzie, z jakiegoś powodu używanie tylko [[introvc alloc] init];nie działa dla mnie.

Mogambolal
źródło
1
miło widzieć, że korzystasz z nowej funkcji tworzenia scenariuszy. ale nie używałem storyboardu w moim przypadku ...
Raptor,
Chciałem tylko zwrócić uwagę, że „instantiateViewControllerWithIdentifier” pobiera identyfikator kontrolera. więcej szczegółów znajdziesz na stackoverflow.com/questions/8186375/…
Kishor Kundan
2

Rozwiązałem to pisząc

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];
pankesh
źródło
3
Do Twojej wiadomości, aby być bardziej idiomatycznym (i bezpieczniejszym!), Powinieneś zrobić: animowany: TAK zakończenie: zero
powerj1984
2
Przyznaję ci bardziej idiomatyczne, ale czy to jest bezpieczniejsze?
Zev Eisenberg
2

Miałem ten problem z kodem strony trzeciej. Ktoś zapomniał ustawić super wnętrze viewWillAppear i viewWillDisappear w niestandardowej klasie TabBarController.

- (void) viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];
    // code...
}

or

- (void) viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    // code...
}
J. Lopes
źródło
2

Jeśli używasz transitioningDelegate(nie ma to miejsca w przykładzie tego pytania), ustaw również modalPresentationStylena .Custom.

Szybki

let vc = storyboard.instantiateViewControllerWithIdentifier("...")
vc.transitioningDelegate = self
vc.modalPresentationStyle = .Custom
Kof
źródło
1

Miałem ten sam błąd. Mam pasek kart z 3 elementami i nieświadomie próbowałem wywołać główny kontroler widoku elementu 1 w elemencie 2 mojego paska kart za pomocą performSegueWithIdentifier.

Dzieje się tak, że wywołuje kontroler widoku i wraca do głównego kontrolera widoku elementu 2 po kilku sekundach i rejestruje ten błąd.

Najwyraźniej nie można wywołać głównego kontrolera widoku elementu do innego elementu.

Więc zamiast performSegueWithIdentifier

użyłem [self.parentViewController.tabBarController setSelectedIndex:0];

Mam nadzieję, że to komuś pomoże.

Gellie Ann
źródło
1

Miałem ten sam problem i pomyślałem, że napiszę na wypadek, gdyby ktoś inny wpadł na coś podobnego.

W moim przypadku dołączyłem aparat rozpoznawania gestów długiego naciśnięcia do mojego UITableViewController.

UILongPressGestureRecognizer *longPressGesture = [[[UILongPressGestureRecognizer alloc]
                                                   initWithTarget:self
                                                   action:@selector(onLongPress:)]
                                                  autorelease];
[longPressGesture setMinimumPressDuration:1];
[self.tableView addGestureRecognizer:longPressGesture];

W moim selektorze onLongPress uruchomiłem następny kontroler widoku.

- (IBAction)onLongPress:(id)sender {

    SomeViewController* page = [[SomeViewController alloc] initWithNibName:@"SomeViewController" bundle:nil];

    [self.navigationController pushViewController:page animated:YES];

    [page release];

}

W moim przypadku otrzymałem komunikat o błędzie, ponieważ aparat rozpoznawania długiego naciśnięcia odpalił więcej niż jeden raz, w wyniku czego mój „SomeViewController” został wielokrotnie wypchnięty na stos.

Rozwiązaniem było dodanie wartości logicznej, aby wskazać, kiedy SomeViewController został wypchnięty na stos. Kiedy moja metoda viewWillAppear UITableViewControllera została wywołana, ustawiłem wartość logiczną z powrotem na NO.

Dale Moore
źródło
1

Odkryłem, że jeśli używasz storyboardu, będziesz chciał umieścić kod prezentujący nowy kontroler widoku w viewDidAppear. Pozbędzie się również ostrzeżenia „Odradza się prezentowanie kontrolerów widoku na odłączonych kontrolerach widoku”.

Dan Levy
źródło
1

W Swift 2+ dla mnie działa:

Mam UITabBarViewController w scenorysie i mam taką właściwość selectedIndex:

wprowadź opis obrazu tutaj

Ale usuwam go i dodaję w mojej metodzie viewDidLoad mojej początkowej klasy, w ten sposób:

override func viewDidLoad() {
   super.viewDidLoad()
   self.tabBarController?.selectedIndex = 2
}

Mam nadzieję, że mogę komuś pomóc.

Dasoga
źródło
0

Właściwie musisz poczekać, aż animacja wypychania się zakończy. Możesz więc delegować UINavigationController i zapobiegać wypychaniu do zakończenia animacji.

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    waitNavigation = NO;
}


-(void)showGScreen:(id)gvc{

    if (!waitNavigation) {
        waitNavigation = YES;
        [_nav popToRootViewControllerAnimated:NO];
        [_nav pushViewController:gvc animated:YES];
    }
}
ymutlu
źródło
Nazywam to, gdy wybrana komórka. To zależy właściwie od ciebie
ymutlu
0

Jak zasugerował @danh, moim problemem było to, że prezentowałem modalny vc, zanim UITabBarControllerbył gotowy. Jednak czułem się nieswojo polegając na stałym opóźnieniu przed prezentacją kontrolera widoku (z moich testów potrzebowałem użyć opóźnienia 0,05-0,1 s performSelector:withDelay:). Moje rozwiązanie jest dodanie bloku, który jest wywoływana na UITabBarController„s viewDidAppear:metody:

PRTabBarController.h:

@interface PRTabBarController : UITabBarController

@property (nonatomic, copy) void (^viewDidAppearBlock)(BOOL animated);

@end

PRTabBarController.m:

#import "PRTabBarController.h"

@implementation PRTabBarController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (self.viewDidAppearBlock) {
        self.viewDidAppearBlock(animated);
    }
}

@end

Teraz w application:didFinishLaunchingWithOptions:

PRTabBarController *tabBarController = [[PRTabBarController alloc] init];

// UIWindow initialization, etc.

__weak typeof(tabBarController) weakTabBarController = tabBarController;
tabBarController.viewDidAppearBlock = ^(BOOL animated) {
    MyViewController *viewController = [MyViewController new];
    viewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
    [weakTabBarController.tabBarController presentViewController:navigationController animated:NO completion:nil];
    weakTabBarController.viewDidAppearBlock = nil;
};
johnboiles
źródło
0

musisz upewnić się, że - (void) beginAppearanceTransition: (BOOL) isAppearing animated: (BOOL) animated i - (void) endAppearanceTransition są tworzone razem w klasie.

zszen
źródło
0

Miałem ten sam problem. Podczas tworzenia chciałem ominąć ekrany. Przechodziłem z jednego kontrolera widoku do drugiego w viewDidLoad, wywołując metodę selektora.

Problem polega na tym, że powinniśmy pozwolić ViewController zakończyć przejście przed przejściem do innego ViewController.

To rozwiązało mój problem: opóźnienie jest konieczne, aby umożliwić ViewControllers zakończenie przejścia przed przejściem do innego.

self.perform(#selector(YOUR SELECTOR METHOD), with: self, afterDelay: 0.5)
codeedoc
źródło
0

Szybki 5

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {


//Delete or comment the below lines on your SceneDelegate.

//        guard let windowScene = (scene as? UIWindowScene) else { return }
//        window?.windowScene = windowScene
//        window?.makeKeyAndVisible()

        let viewController = ListVC()
        let navViewController = UINavigationController(rootViewController: viewController)
        window?.rootViewController = navViewController

    }
Marlhex
źródło
-1

Miałem ten problem, kiedy przechodziłem z głównego TVC do TVC A, a następnie do TVC B. Po naciśnięciu przycisku „załaduj” w TVC BI chciałem przejść od razu z powrotem do głównego TVC (nie ma potrzeby ponownego odwiedzania TVC A, więc po co to robić) . Miałem:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:YES];
//Pop self to return to root
[self.navigationController popViewControllerAnimated:YES];

... co spowodowało błąd „Niezbalansowane wywołania do rozpoczęcia / zakończenia itd.”. Następujące czynności naprawiły błąd, ale brak animacji:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root
[self.navigationController popViewControllerAnimated:NO];

To było moje ostateczne rozwiązanie, bez błędu i nadal animowane:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root, only works if first pop above is *not* animated
[self.navigationController popViewControllerAnimated:YES];
dawid
źródło
-1

Napotkałem ten błąd, kiedy podłączyłem UIButton do akcji typu segue scenorysu (w IB), ale później zdecydowałem, że przycisk programowo wywoła performSegueWithIdentifierzapominanie o usunięciu pierwszego z IB.

W istocie dwukrotnie wykonał wywołanie płynności, dał ten błąd i faktycznie dwukrotnie popchnął mój widok. Rozwiązaniem było usunięcie jednego z połączeń przychodzących.

Mam nadzieję, że pomoże to komuś tak samo zmęczonemu jak ja!

capikaw
źródło