Lệnh gọi không cân bằng để bắt đầu / kết thúc chuyển đổi giao diện cho <UITabBarController: 0x197870>


119

Tôi đọc SO về một người dùng khác gặp phải lỗi tương tự , nhưng lỗi này là trong trường hợp khác.

Tôi nhận được thông báo này khi tôi thêm Bộ điều khiển Chế độ xem ban đầu:

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

Cấu trúc của ứng dụng như sau:

Tôi có một TabBarController 5 tab được liên kết với 5 View Controllers. Trong tab hiển thị ban đầu, tôi gọi Bộ điều khiển Chế độ xem mới để phủ lên dưới dạng phần giới thiệu của ứng dụng.

Tôi sử dụng mã này để gọi bộ điều khiển chế độ xem giới thiệu:

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

Sau khi IntroVCbộ điều khiển chế độ xem này hiển thị, lỗi trên sẽ hiển thị.

ps Tôi đang sử dụng xCode 4.2 & iOS 5.0 SDK, đang phát triển ứng dụng iOS 4.3.


Chào Shivan, tôi có cùng một vấn đề với bạn. Nhưng tôi vẫn không thể sửa nó sau khi xem câu trả lời bên dưới. Tôi có thể biết nơi bạn gọi bộ điều khiển chế độ xem giới thiệu không?
ZYiOS

Câu trả lời:


98

Nếu không xem thêm mã xung quanh, tôi không thể đưa ra câu trả lời chắc chắn, nhưng tôi có hai giả thuyết.

  1. Bạn không sử dụng UIViewController's được khởi tạoinitWithNibName:bundle: . Hãy thử sử dụng nó thay vì chỉ init.

  2. Ngoài ra, selfcó thể là một trong những bộ điều khiển chế độ xem của bộ điều khiển thanh tab. Luôn hiển thị bộ điều khiển chế độ xem từ bộ điều khiển chế độ xem trên cùng, có nghĩa là trong trường hợp này, hãy yêu cầu bộ điều khiển thanh tab trình bày bộ điều khiển chế độ xem lớp phủ thay cho bộ điều khiển chế độ xem. Bạn vẫn có thể giữ bất kỳ đại biểu gọi lại nào cho bộ điều khiển chế độ xem thực, nhưng bạn phải có và loại bỏ bộ điều khiển thanh tab.


2
# 1 đã khắc phục sự cố này cho tôi, tôi đã sử dụng initWithNibName: nil pack: nil thay vì init.
Hua-Ying

172
Bạn có thể tạo cảnh báo này bằng cách hiển thị vc phương thức trước khi ứng dụng được khởi chạy xong. tức là Bắt đầu một ứng dụng mẫu ứng dụng theo thẻ và trình bày một vc phương thức ở đầu self.tabBarController ở dòng cuối cùng trong ứng dụng: didFinishLaunching. Cảnh báo xuất hiện. Giải pháp: trước tiên hãy để ngăn xếp giãn ra, trình bày phương thức vc trong một phương thức khác, được gọi với một Trình dò ​​tìm biểu diễn với Delay: 0.0.
danh sách

9
Và đây là một câu hỏi khác giải thích tại sao performanceSelector withDelay hoạt động. stackoverflow.com/questions/1922517/…
fatih

1
giải pháp của danh đã làm việc cho tôi, nhưng tôi phải sử dụng 0,1 thay vì 0,0.
Brandon O'Rourke

11
Thay vì sử dụng performanceSelectorWithDelay bằng 0, hãy thực hiện điều này trong viewDidAppear thay vì viewDidLoad hoặc whatnot.
tooluser,

40

Tôi đã sửa lỗi này bằng cách thay đổi hoạt ảnh từ CÓ thành KHÔNG.

Từ:

[tabBarController presentModalViewController:viewController animated:YES];

Đến:

[tabBarController presentModalViewController:viewController animated:NO];

4
Điều này khắc phục sự cố nếu bạn không quan tâm đến hoạt ảnh, nhưng nếu bạn cần hoạt ảnh: CÓ, hãy thử nhận xét của danh về câu trả lời được chấp nhận: stackoverflow.com/questions/7886096/…
wxactly Ngày

3
FYI: presentModalViewController: animation: không được chấp nhận trong iOS6.
ZS

16

Như đã đăng bởi danh

Bạn có thể tạo cảnh báo này bằng cách hiển thị vc phương thức trước khi ứng dụng được khởi chạy xong. tức là Bắt đầu một ứng dụng mẫu ứng dụng theo thẻ và trình bày một vc phương thức ở đầu self.tabBarController ở dòng cuối cùng trong ứng dụng: didFinishLaunching. Cảnh báo xuất hiện. Giải pháp: hãy để ngăn xếp được thư giãn trước, trình bày phương thức vc trong một phương thức khác, được gọi với một Trình dò ​​tìm biểu diễn với Delay: 0.0

Cố gắng di chuyển phương thức vào viewWillAppear và bảo vệ nó để nó được thực thi chỉ một lần (bạn nên thiết lập một thuộc tính)


Tại sao viewWillAppearvà không viewDidAppear?
CyberMew

6

Một giải pháp khác cho nhiều trường hợp là đảm bảo rằng quá trình chuyển đổi giữa UIViewControllercác s xảy ra sau khi thủ tục không phù hợp (như trong quá trình khởi tạo) kết thúc, bằng cách:

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

Điều này là chung cho cả pushViewController:animated:, v.v.


4

Tôi đã từng gặp vấn đề tương tự. Tôi đã gọi một phương thức bên viewDidLoadtrongUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

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

- (void)loadingView{

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

Trong giây UIViewControllertôi cũng làm như vậy với độ trễ 0,5 giây. Sau khi thay đổi độ trễ thành giá trị cao hơn, nó hoạt động tốt. Nó giống như segue không thể được thực hiện quá nhanh sau một segue khác.


7
Phương thức vòng đời chế độ xem viewDidAppear được cung cấp cho chính xác mục đích này và sẽ đáng tin cậy hơn so với việc đưa ra một độ trễ giả tạo, fwiw.
tooluser,

1
Đây là câu trả lời đúng ngoại trừ độ trễ 0 là đủ để đợi cho đến khi bộ điều khiển điều hướng sẵn sàng cho một điều hướng mới.
malhal

Nó hoàn toàn đúng, bạn phải gọi nó bên trong viewDidAppearđể UINavigationControllersẵn sàng xử lý nó. Tôi đã thay đổi bài đăng của mình thành cái này;)
Alex Cio

Tôi cảm thấy như thế này nên được chuyển đến viewWillAppear, sau đó bạn không phải lo lắng về việc chế độ xem đã khởi tạo hay chưa.
cưỡi ngựa vào

3

Tôi đã gặp vấn đề tương tự khi tôi cần Trình điều khiển Chế độ xem Đăng nhập của mình từ Bộ điều khiển Chế độ xem khác Nếu Người dùng không được ủy quyền, tôi đã làm điều đó trong Phương thức ViewDidLoad của Bộ điều khiển Chế độ xem Khác của tôi (nếu không được ủy quyền -> presentModalViewController). Khi tôi bắt đầu tạo nó trong phương thức ViewDidAppear, tôi đã giải quyết được vấn đề này. Tôi nghĩ rằng ViewDidLoad chỉ khởi tạo các thuộc tính và sau đó thuật toán hiển thị chế độ xem thực tế sẽ bắt đầu! Đó là lý do tại sao bạn phải sử dụng phương thức viewDidAppear để thực hiện chuyển đổi phương thức!


3

Tôi gặp sự cố này do lỗi chính tả:

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

thay vì

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

Nó đang gọi "WillAppear" trong siêu thay vì "DidAppear"


2

Tôi đã có rất nhiều vấn đề với cùng một vấn đề. Tôi đã giải quyết vấn đề này bằng cách

  1. Khởi tạo ViewController bằng phương thức storyboad InstantiateViewControllerWithIdentifier. I EIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

Tôi có bộ điều khiển chế độ xem trong bảng phân cảnh của mình, vì một số lý do mà việc sử dụng chỉ [[introvc alloc] init];không phù hợp với tôi.


1
rất vui khi thấy bạn sử dụng tính năng phân cảnh mới. nhưng tôi đã không sử dụng bảng phân cảnh trong trường hợp của mình ...
Raptor

Chỉ muốn chỉ ra điều này rằng "InstantiateViewControllerWithIdentifier" lấy Mã định danh của bộ điều khiển. để biết thêm chi tiết, hãy xem stackoverflow.com/questions/8186375/…
Kishor Kundan,

2

Tôi đã giải quyết nó bằng cách viết

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];

3
FYI được nhiều thành ngữ bạn nên làm: hoạt hình: YES hoàn thành: (và an toàn hơn!) Nil
powerj1984

2
Tôi sẽ cho bạn nhiều thành ngữ hơn, nhưng làm thế nào là an toàn hơn?
Zev Eisenberg

2

Tôi gặp sự cố này với mã của bên thứ ba. Ai đó đã quên đặt siêu bên trong viewWillAppear và viewWillDisappear trong một lớp TabBarController tùy chỉnh.

- (void) viewWillAppear:(BOOL)animated {

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

or

- (void) viewWillDisappear:(BOOL)animated {

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

2

Nếu bạn đang sử dụng transitioningDelegate(không phải trường hợp trong ví dụ của câu hỏi này), hãy đặt modalPresentationStylethành .Custom.

Nhanh

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

1

Tôi đã có những lỗi giống nhau. Tôi có một thanh tab có 3 mục và tôi đã vô thức cố gắng gọi bộ điều khiển chế độ xem gốc của mục 1 trong mục 2 của thanh tab của tôi bằng cách sử dụng performSegueWithIdentifier.

Điều xảy ra là nó gọi bộ điều khiển chế độ xem và quay lại bộ điều khiển chế độ xem gốc của mục 2 sau vài giây và ghi lại lỗi đó.

Rõ ràng, bạn không thể gọi bộ điều khiển chế độ xem gốc của một mục đến một mục khác.

Vì vậy, thay vì performSegueWithIdentifier

Tôi đã sử dụng [self.parentViewController.tabBarController setSelectedIndex:0];

Hy vọng điều này sẽ giúp ai đó.


1

Tôi đã gặp vấn đề tương tự và nghĩ rằng tôi sẽ đăng trong trường hợp người khác gặp phải điều gì đó tương tự.

Trong trường hợp của tôi, tôi đã gắn một công cụ nhận dạng cử chỉ nhấn và giữ vào UITableViewController của mình.

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

Trong bộ chọn onLongPress, tôi đã khởi chạy bộ điều khiển chế độ xem tiếp theo của mình.

- (IBAction)onLongPress:(id)sender {

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

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

    [page release];

}

Trong trường hợp của tôi, tôi nhận được thông báo lỗi vì trình nhận dạng báo chí lâu đã kích hoạt nhiều lần và kết quả là "SomeViewController" của tôi đã được đẩy lên ngăn xếp nhiều lần.

Giải pháp là thêm boolean để cho biết thời điểm SomeViewController đã được đẩy lên ngăn xếp. Khi phương thức viewWillAppear của UITableViewController của tôi được gọi, tôi đã đặt boolean trở lại NO.


1

Tôi thấy rằng, nếu bạn đang sử dụng bảng phân cảnh, bạn sẽ muốn đặt mã hiển thị bộ điều khiển chế độ xem mới trong viewDidAppear. Nó cũng sẽ loại bỏ cảnh báo "Không khuyến khích trình bày bộ điều khiển chế độ xem trên bộ điều khiển chế độ xem tách rời".


1

Trong Swift 2+ cho tôi hoạt động:

Tôi có UITabBarViewController trong bảng phân cảnh và tôi đã chọn thuộc tínhIndex như thế này:

nhập mô tả hình ảnh ở đây

Nhưng tôi xóa nó và thêm vào phương thức viewDidLoad của lớp ban đầu của tôi, như sau:

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

Tôi hy vọng tôi có thể giúp ai đó.


0

Trên thực tế, bạn cần phải đợi cho đến khi hoạt ảnh đẩy kết thúc. Vì vậy, bạn có thể ủy quyền UINavigationController và ngăn chặn việc đẩy cho đến khi hoạt ảnh kết thúc.

- (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];
    }
}

Tôi gọi nó khi một ô được chọn. Điều đó thực sự phụ thuộc vào bạn
ymutlu

0

Như @danh đã đề xuất, vấn đề của tôi là tôi đã trình bày vc phương thức trước khi UITabBarControllersẵn sàng. Tuy nhiên, tôi cảm thấy không thoải mái khi dựa vào độ trễ cố định trước khi trình bày bộ điều khiển chế độ xem (từ thử nghiệm của tôi, tôi cần sử dụng độ trễ 0,05-0,1 giây performSelector:withDelay:). Giải pháp của tôi là để thêm một khối mà được kêu gọi UITabBarController's viewDidAppear:phương pháp:

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

Bây giờ trong 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;
};

0

bạn cần chắc chắn rằng - (void) beginAppearanceTransition: (BOOL) isAppearance animation: (BOOL) animation và - (void) endAppearanceTransition được tạo cùng nhau trong lớp.


0

Tôi gặp vấn đề tương tự. Khi phát triển, tôi muốn bỏ qua màn hình. Tôi đang điều hướng từ bộ điều khiển chế độ xem này sang bộ điều khiển chế độ xem khác trong viewDidLoad bằng cách gọi một phương thức bộ chọn.

Vấn đề là chúng ta nên để ViewController chuyển đổi xong trước khi chuyển sang ViewController khác.

Điều này đã giải quyết được vấn đề của tôi: Sự chậm trễ là cần thiết để cho phép ViewControllers hoàn tất quá trình chuyển đổi trước khi chuyển sang một phần mềm khác.

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

0

Swift 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

    }

-1

Tôi gặp sự cố này khi tôi đã điều hướng từ TVC gốc sang TVC A rồi đến TVC B. Sau khi nhấn vào nút "tải" trong TVC BI muốn chuyển thẳng trở lại TVC gốc (không cần phải truy cập lại TVC A vậy tại sao phải làm vậy) . Tôi đã có:

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

... gây ra lỗi "Cuộc gọi không cân bằng để bắt đầu / kết thúc, v.v.". Phần sau đã khắc phục lỗi nhưng không có hoạt ảnh:

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

Đây là giải pháp cuối cùng của tôi, không có lỗi và vẫn hoạt hình:

//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];

-1

Tôi đã gặp lỗi này khi nối UIButton với một hành động giả mạo bảng phân cảnh (trong IB) nhưng sau đó quyết định gọi nút theo chương trình mà performSegueWithIdentifierquên xóa cái đầu tiên khỏi IB.

Về bản chất, nó đã thực hiện cuộc gọi segue hai lần, đưa ra lỗi này và thực sự đã đẩy chế độ xem của tôi hai lần. Cách khắc phục là xóa một trong các cuộc gọi giả mạo.

Hy vọng điều này sẽ giúp ai đó mệt mỏi như tôi!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.