Panggilan tidak seimbang untuk memulai / mengakhiri transisi tampilan untuk <UITabBarController: 0x197870>

119

Saya membaca SO tentang pengguna lain yang mengalami kesalahan serupa , tetapi kesalahan ini dalam kasus yang berbeda.

Saya menerima pesan ini ketika saya menambahkan View Controller pada awalnya:

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

Struktur aplikasi adalah sebagai berikut:

Saya mendapat TabBarController 5-tab yang ditautkan ke 5 View Controllers. Di tab tampilan awal, saya memanggil Pengontrol Tampilan baru untuk dihamparkan sebagai pengenalan aplikasi.

Saya menggunakan kode ini untuk memanggil pengontrol tampilan pengantar:

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

Setelah IntroVCpengontrol tampilan ini muncul, kesalahan di atas muncul.

ps Saya menggunakan xCode 4.2 & iOS 5.0 SDK, mengembangkan aplikasi iOS 4.3.

Raptor
sumber
Hai Shivan, saya memiliki masalah yang sama dengan Anda. Tapi saya masih tidak bisa memperbaikinya setelah melihat jawaban di bawah ini. Bolehkah saya tahu di mana Anda memanggil pengontrol tampilan pengantar?
ZYiOS

Jawaban:

98

Tanpa melihat lebih banyak kode di sekitarnya, saya tidak dapat memberikan jawaban yang pasti, tetapi saya memiliki dua teori.

  1. Anda tidak menggunakan UIViewController's ditunjuk initializerinitWithNibName:bundle: . Coba gunakan, bukan hanya init.

  2. Juga, selfmungkin salah satu pengontrol tampilan pengontrol tab bar. Selalu hadirkan pengontrol tampilan dari pengontrol tampilan paling atas, yang berarti dalam hal ini, minta pengontrol bilah tab untuk menampilkan pengontrol tampilan overlay atas nama pengontrol tampilan. Anda masih dapat menyimpan delegasi panggilan balik ke pengontrol tampilan nyata, tetapi Anda harus memiliki pengontrol bilah tab dan menutupnya.

Jesper
sumber
2
# 1 memperbaiki masalah ini untuk saya, saya menggunakan initWithNibName: nil bundle: nil alih-alih init.
Hua-Ying
172
Anda dapat membuat peringatan ini dengan menampilkan modal vc sebelum aplikasi selesai diinisialisasi. yaitu Memulai aplikasi template aplikasi tab dan menampilkan modal vc di atas self.tabBarController sebagai baris terakhir di application: didFinishLaunching. Peringatan muncul. Solusi: biarkan stack terlepas terlebih dahulu, tunjukkan modal vc dalam metode lain, yang dipanggil dengan performSelector withDelay: 0.0.
danh
9
Dan inilah pertanyaan lain yang menjelaskan mengapa performSelector withDelay berfungsi. stackoverflow.com/questions/1922517/…
fatih
1
solusi danh berhasil untuk saya, tetapi saya harus menggunakan 0,1 daripada 0,0.
Brandon O'Rourke
11
Daripada menggunakan performSelectorWithDelay nol, lakukan ini di viewDidAppear daripada viewDidLoad atau yang lainnya.
alat
40

Saya memperbaiki kesalahan ini dengan mengubah animasi dari YES menjadi NO.

Dari:

[tabBarController presentModalViewController:viewController animated:YES];

Untuk:

[tabBarController presentModalViewController:viewController animated:NO];
PokerIncome.com
sumber
4
Ini memperbaiki masalah jika Anda tidak peduli dengan animasi, tetapi jika Anda membutuhkan animasi: YA, coba komentar danh pada jawaban yang diterima: stackoverflow.com/questions/7886096/…
wxactly
3
FYI: presentModalViewController: animated: tidak digunakan lagi di iOS6.
ZS
16

Seperti yang diposting oleh danh

Anda dapat membuat peringatan ini dengan menampilkan modal vc sebelum aplikasi selesai diinisialisasi. yaitu Mulai aplikasi template aplikasi tab dan tampilkan modal vc di atas self.tabBarController sebagai baris terakhir dalam application: didFinishLaunching. Peringatan muncul. Solusi: biarkan tumpukan terlepas terlebih dahulu, tunjukkan modal vc dalam metode lain, yang dipanggil dengan performSelector withDelay: 0.0

Coba pindahkan metode ke viewWillAppear dan jaga agar bisa dijalankan hanya sekali (akan merekomendasikan menyiapkan properti)

Peter Lapisu
sumber
Kenapa viewWillAppeartidak viewDidAppear?
CyberMew
6

Solusi lain untuk banyak kasus adalah memastikan bahwa transisi antara UIViewControllers terjadi setelah prosedur yang tidak sesuai (seperti selama inisialisasi) selesai, dengan melakukan:

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

Ini umum juga pushViewController:animated:, dll.

mllm
sumber
4

Saya memiliki masalah yang sama. Saya memanggil metode di viewDidLoaddalam yang pertamaUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

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

- (void)loadingView{

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

Di dalam detik UIViewControllersaya juga melakukan hal yang sama dengan penundaan 0,5 detik. Setelah mengubah penundaan ke nilai yang lebih tinggi, itu berfungsi dengan baik. Sepertinya segue tidak bisa dimainkan terlalu cepat setelah segue lain.

Alex Cio
sumber
7
Metode siklus hidup tampilan viewDidAppear disediakan untuk tujuan ini, dan akan lebih andal daripada memperkenalkan penundaan buatan, fwiw.
alat
1
Ini adalah jawaban yang benar kecuali penundaan 0 sudah cukup untuk menunggu sampai pengontrol navigasi siap untuk navigasi baru.
malhal
Ini sepenuhnya benar, Anda harus memanggilnya ke dalam viewDidAppearsehingga UINavigationControllersiap untuk menanganinya. Saya mengubah posting saya menjadi ini;)
Alex Cio
Saya merasa ini harus dipindahkan ke viewWillAppear maka Anda tidak perlu khawatir tentang apakah tampilan telah diinisialisasi atau tidak.
horsejockey
3

Saya memiliki masalah yang sama ketika saya perlu Mempresentasikan Pengontrol Tampilan Login Saya dari Pengontrol Tampilan lain Jika Pengguna tidak berwenang, saya melakukannya dalam Metode ViewDidLoad dari Pengontrol Tampilan Lain saya (jika tidak diotorisasi -> presentModalViewController). Ketika saya mulai membuatnya dalam metode ViewDidAppear, saya memecahkan masalah ini. Saya pikir ViewDidLoad hanya menginisialisasi properti dan setelah itu algoritma tampilan yang sebenarnya dimulai! Itulah mengapa Anda harus menggunakan metode viewDidAppear untuk membuat transisi modal!

Tolusha
sumber
3

Saya mengalami masalah ini karena salah ketik:

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

dari pada

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

Itu memanggil "WillAppear" di super, bukan "DidAppear"

Adriano Spadoni
sumber
2

Saya punya banyak masalah dengan masalah yang sama. Saya menyelesaikan yang satu ini dengan

  1. Memulai ViewController menggunakan metode storyboad instantiateViewControllerWithIdentifier. yaituIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

Saya memiliki viewcontroller di storyboard saya, untuk beberapa alasan hanya menggunakan [[introvc alloc] init];tidak berhasil untuk saya.

Mogambolal
sumber
1
senang melihat Anda menggunakan fitur storyboard baru. tapi saya tidak menggunakan storyboard dalam kasus saya ...
Raptor
Hanya ingin menunjukkan bahwa "instantiateViewControllerWithIdentifier" mengambil Identifier pengontrol. untuk lebih jelasnya lihat stackoverflow.com/questions/8186375/…
Kishor Kundan
2

Saya menyelesaikannya dengan menulis

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];
pankesh
sumber
3
FYI agar lebih idiomatis (dan lebih aman!) Anda harus melakukan: animasi: YA penyelesaian: nil
powerj1984
2
Aku akan memberimu lebih idiomatis, tapi bagaimana itu lebih aman?
Zev Eisenberg
2

Saya mengalami masalah ini dengan kode pihak ketiga. Seseorang lupa menyetel super di dalam viewWillAppear dan viewWillDisappear di kelas TabBarController kustom.

- (void) viewWillAppear:(BOOL)animated {

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

or

- (void) viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    // code...
}
J. Lopes
sumber
2

Jika Anda menggunakan transitioningDelegate(bukan kasus dalam contoh pertanyaan ini), setel juga modalPresentationStyleke .Custom.

Cepat

let vc = storyboard.instantiateViewControllerWithIdentifier("...")
vc.transitioningDelegate = self
vc.modalPresentationStyle = .Custom
Kof
sumber
1

Saya mengalami kesalahan yang sama. Saya memiliki bilah tab dengan 3 item dan saya secara tidak sadar mencoba memanggil pengontrol tampilan root item 1 di item 2 dari bilah tab saya menggunakan performSegueWithIdentifier.

Apa yang terjadi adalah ia memanggil pengontrol tampilan dan kembali ke pengontrol tampilan root dari item 2 setelah beberapa detik dan mencatat kesalahan itu.

Rupanya, Anda tidak dapat memanggil pengontrol tampilan root dari suatu item ke item lain.

Jadi, bukan performSegueWithIdentifier

Saya dulu [self.parentViewController.tabBarController setSelectedIndex:0];

Semoga ini bisa membantu seseorang.

Gellie Ann
sumber
1

Saya memiliki masalah yang sama dan berpikir saya akan memposting jika ada orang lain yang mengalami hal serupa.

Dalam kasus saya, saya telah memasang pengenal gerakan tekan lama ke UITableViewController saya.

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

Di pemilih onLongPress saya, saya meluncurkan pengontrol tampilan berikutnya.

- (IBAction)onLongPress:(id)sender {

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

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

    [page release];

}

Dalam kasus saya, saya menerima pesan kesalahan karena pengenal pers lama ditembakkan lebih dari satu kali dan sebagai hasilnya, "SomeViewController" saya didorong ke tumpukan beberapa kali.

Solusinya adalah menambahkan boolean untuk menunjukkan kapan SomeViewController telah didorong ke tumpukan. Ketika metode viewWillAppear UITableViewController saya dipanggil, saya menyetel boolean kembali ke NO.

Dale Moore
sumber
1

Saya menemukan bahwa, jika Anda menggunakan storyboard, Anda akan ingin meletakkan kode yang menampilkan pengontrol tampilan baru di viewDidAppear. Ini juga akan menghilangkan peringatan "Menampilkan pengontrol tampilan pada pengontrol tampilan terpisah tidak disarankan".

Dan Levy
sumber
1

Di Swift 2+ untuk saya bekerja:

Saya memiliki UITabBarViewController di storyboard dan saya telah memilih propertiIndex seperti ini:

masukkan deskripsi gambar di sini

Tapi saya menghapusnya, dan menambahkan metode viewDidLoad kelas awal saya, seperti ini:

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

Saya harap saya dapat membantu seseorang.

Dasoga
sumber
0

Sebenarnya Anda perlu menunggu sampai animasi push berakhir. Jadi, Anda dapat mendelegasikan UINavigationController dan mencegah dorongan hingga animasi berakhir.

- (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
sumber
Saya menyebutnya ketika sel dipilih. Itu tergantung pada Anda sebenarnya
ymutlu
0

Seperti yang disarankan @danh, masalah saya adalah saya mempresentasikan modal vc sebelum UITabBarControllersiap. Namun, saya merasa tidak nyaman mengandalkan penundaan tetap sebelum menampilkan pengontrol tampilan (dari pengujian saya, saya perlu menggunakan penundaan 0,05-0,1 detik performSelector:withDelay:). Solusi saya adalah menambahkan blok yang dipanggil pada UITabBarController's viewDidAppear:metode:

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

Sekarang di 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
sumber
0

Anda perlu memastikan - (void) beginAppearanceTransition: (BOOL) isAppearing animated: (BOOL) animated dan - (void) endAppearanceTransition dibuat bersama di kelas.

zszen
sumber
0

Saya memiliki masalah yang sama. Saat mengembangkan saya ingin melewati layar. Saya menavigasi dari satu pengontrol tampilan ke yang lain di viewDidLoad dengan memanggil metode pemilih.

Masalahnya adalah kita harus membiarkan ViewController menyelesaikan transisi sebelum beralih ke ViewController lain.

Ini memecahkan masalah saya: Penundaan diperlukan agar ViewControllers menyelesaikan transisi sebelum beralih ke yang lain.

self.perform(#selector(YOUR SELECTOR METHOD), with: self, afterDelay: 0.5)
codeedoc
sumber
0

Cepat 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
sumber
-1

Saya mengalami masalah ini ketika saya telah menavigasi dari TVC root ke TVC A lalu ke TVC B. Setelah mengetuk tombol "muat" di TVC BI ingin langsung kembali ke TVC root (tidak perlu mengunjungi kembali TVC A jadi mengapa melakukannya) . Saya punya:

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

... yang memberikan kesalahan "Panggilan tidak seimbang untuk memulai / mengakhiri, dll". Hal berikut memperbaiki kesalahan, tetapi tidak ada animasi:

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

Ini adalah solusi terakhir saya, tidak ada kesalahan dan masih beranimasi:

//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
sumber
-1

Saya mengalami kesalahan ini ketika saya mengaitkan UIButton ke aksi segue storyboard (dalam IB) tetapi kemudian memutuskan untuk memiliki tombol yang secara terprogram memanggil performSegueWithIdentifier lupa untuk menghapus yang pertama dari IB.

Intinya itu melakukan panggilan segue dua kali, memberikan kesalahan ini dan benar-benar mendorong pandangan saya dua kali. Cara mengatasinya adalah menghapus salah satu panggilan segue.

Semoga ini bisa membantu seseorang yang lelah seperti saya!

capikaw.dll
sumber