<UITabBarController:0x197870>的开始/结束外观转换的不平衡调用


119

我读到关于另一个遇到类似错误的用户的信息,但是这种错误在不同情况下。

最初添加视图控制器时,我收到此消息:

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

该应用程序的结构如下:

我有一个5个标签的TabBarController链接到5个View Controller。在最初的显示选项卡中,我调出一个新的View Controller作为应用程序的介绍进行叠加。

我使用以下代码来调用简介视图控制器:

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

IntroVC视图控制器显示后,将显示上述错误。

ps我正在使用xCode 4.2和iOS 5.0 SDK,正在开发iOS 4.3应用程序。


您好Shivan,我也遇到同样的问题。但是查看下面的答案后,我仍然无法修复它。我可以知道您在哪里调用简介视图控制器吗?
ZYiOS 2011年

Answers:


98

没有看到更多周围的代码,我无法给出确切的答案,但是我有两种理论。

  1. 您没有使用UIViewController指定初始值设定项initWithNibName:bundle:。尝试使用它而不是init

  2. 另外,它self可能是选项卡栏控制器的视图控制器之一。始终从最顶部的视图控制器显示视图控制器,这意味着在这种情况下,请标签栏控制器代表视图控制器显示叠加视图控制器。您仍然可以保留任何到真实视图控制器的回调委托,但是必须存在并关闭选项卡栏控制器。


2
#1为我解决了这个问题,我使用initWithNibName:nil bundle:nil代替了init。
华英

172
您可以通过在应用初始化完成之前显示模态vc来生成此警告。例如,启动一个选项卡式应用程序模板应用程序,并在self.tabBarController顶部显示模式vc,作为application:didFinishLaunching中的最后一行。出现警告。解决方案:首先让堆栈展开,在另一种方法中显示模式vc,该方法是用performSelector withDelay:0.0调用的。
丹,2012年

9
这是另一个问题,说明了performPerformor withDelay为何起作用。stackoverflow.com/questions/1922517/...
法提赫

1
danh的解决方案对我有用,但是我必须使用0.1而不是0.0。
Brandon O'Rourke 2012年

11
而不是使用performSelectorWithDelay为零,而是在viewDidAppear中执行此操作,而不是在viewDidLoad中执行此操作。
使用者2014年

40

我通过将动画从“是”更改为“否”来修复此错误。

从:

[tabBarController presentModalViewController:viewController animated:YES];

至:

[tabBarController presentModalViewController:viewController animated:NO];

4
这样可以解决问题,如果你不关心动画,但如果你需要动画:是,尝试对接受的答案DANH的评论:stackoverflow.com/questions/7886096/...
wxactly

3
仅供参考:presentModalViewController:animated:在iOS6中已弃用。
ZS

16

正如danh发表

您可以通过在应用完成初始化之前显示模式vc来生成此警告。例如,启动一个选项卡式应用程序模板应用程序,并在self.tabBarController顶部显示模式vc,作为application:didFinishLaunching中的最后一行。出现警告。解决方案:首先让堆栈展开,在另一方法中显示模态vc,该方法用performSelector withDelay:0.0调用

尝试将方法移入viewWillAppear并对其进行保护,使其仅执行一次(建议设置属性)


为什么viewWillAppearviewDidAppear呢?
Cyber​​Mew

6

在许多情况下,另一种解决方案是通过执行以下操作来确保UIViewControllers 之间的过渡发生不合适的过程(如初始化期间)完成之后:

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

这对于pushViewController:animated:等也很普遍。


4

我有同样的问题。viewDidLoad我第一次在内部调用了一个方法UIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

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

- (void)loadingView{

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

在第二秒之内,UIViewController我也做了同样的事情,延迟了0.5秒。将延迟更改为更高的值后,它可以正常工作。就像在另一个Segue之后不能快速执行Segue一样。


7
视图生命周期方法viewDidAppear正是为此目的而提供的,并且比引入人为的延迟fwiw更可靠。
使用者2014年

1
这是正确的答案,只是延迟0足以等待导航控制器准备好进行新的导航。
malhal 2014年

完全正确,您必须在内部调用它,viewDidAppear以便UINavigationController可以处理它。我将帖子更改为此;)
Alex Cio 2014年

我觉得应该将其移至viewWillAppear,然后您不必担心视图是否已初始化。
马骑师

3

当我需要从另一个View Controller呈现我的登录View Controller时,我遇到了同样的问题。如果该用户未获得授权,则在另一个View Controller的ViewDidLoad方法中进行了此设置(如果未授权-> presentModalViewController)。当我开始使用ViewDidAppear方法创建它时,我解决了这个问题。我认为ViewDidLoad仅初始化属性,然后开始实际的显示视图算法!这就是为什么必须使用viewDidAppear方法进行模式转换的原因!


3

由于输入错误,我遇到了这个问题:

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

代替

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

它在超级窗口中称为“ WillAppear”,而不是“ DidAppear”


2

同一问题我遇到很多问题。我解决了这个

  1. 使用Storyboad InstantiateViewControllerWithIdentifier方法初始化ViewController。即Intro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

我在情节提要中有ViewController,由于某种原因,仅对[[introvc alloc] init];我不起作用。


1
很高兴看到您使用新的故事板功能。但我没有使用情节提要...
Raptor

只是想指出这一点,“ instantiateViewControllerWithIdentifier”采用了控制器的标识符。有关详细信息,请查看stackoverflow.com/questions/8186375/...
基肖尔Kundan

2

我通过写解决了

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

3
仅供参考,您应该这样做:动画:是完成:零
powerj1984 2014年

2
我会给你更多惯用语,但是它更安全吗?
Zev Eisenberg 2014年

2

我在第三方代码中遇到了这个问题。有人忘记在自定义TabBarController类中设置viewWillAppear和viewWillDisappear的超级内部。

- (void) viewWillAppear:(BOOL)animated {

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

or

- (void) viewWillDisappear:(BOOL)animated {

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

2

如果您正在使用transitioningDelegate(在此问题的示例中不是这种情况),还请设置modalPresentationStyle.Custom

迅速

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

1

我有同样的错误。我有一个包含3个项目的标签栏,并且我不知不觉地尝试使用来调用我的标签栏的项目2中项目1的根视图控制器performSegueWithIdentifier

发生的是,它调用了视图控制器,并在几秒钟后返回到项目2的根视图控制器,并记录了该错误。

显然,您不能将一个项目的根视图控制器调用到另一个项目。

所以代替 performSegueWithIdentifier

我用了 [self.parentViewController.tabBarController setSelectedIndex:0];

希望这对某人有帮助。


1

我遇到了同样的问题,并认为如果有人遇到类似问题,我会发布。

就我而言,我已经将长按手势识别器附加到了UITableViewController上。

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

在onLongPress选择器中,我启动了下一个视图控制器。

- (IBAction)onLongPress:(id)sender {

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

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

    [page release];

}

就我而言,我收到了错误消息,因为长按识别器触发了多次,结果,我的“ SomeViewController”被多次压入堆栈。

解决方案是添加一个布尔值以指示将SomeViewController何时推入堆栈。当调用UITableViewController的viewWillAppear方法时,我将布尔值设置回NO。


1

我发现,如果您使用的是情节提要,则需要将显示新视图控制器的代码放在viewDidAppear中。它还将消除“不鼓励在分离的视图控制器上呈现视图控制器”警告。


1

Swift 2+对我有效:

我在情节提要中有UITabBarViewController,并且我具有如下的selectedIndex属性:

在此处输入图片说明

但是我删除了它,并添加了我的初始类的viewDidLoad方法,如下所示:

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

我希望我能帮助某人。


0

实际上,您需要等待推送动画结束。因此,您可以委派UINavigationController并阻止推送到动画结束。

- (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 2014年

0

正如@danh所建议的那样,我的问题是我UITabBarController在准备好VC之前先介绍了模态VC 。但是,在展示视图控制器之前,我依赖于固定的延迟感到不舒服(根据我的测试,我需要在中使用0.05-0.1s的延迟performSelector:withDelay:)。我的解决方案是添加一个在UITabBarControllerviewDidAppear:方法上调用的块:

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

现在在 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

您需要确保-(void)beginAppearanceTransition:(BOOL)已动画显示:(BOOL)动画和-(void)endAppearanceTransition在类中一起创建。


0

我遇到过同样的问题。开发时,我想绕开屏幕。我通过调用选择器方法在viewDidLoad中从一个视图控制器导航到另一个。

问题在于,我们应该让ViewController在过渡到另一个ViewController之前完成过渡。

这解决了我的问题:延迟是必要的,以便允许ViewControllers在转换到另一个之前完成转换。

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

0

迅捷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

当我从根TVC导航到TVC A然后到TVC B时,我遇到了这个问题。在TVC BI中点击“加载”按钮后,我想直接跳回到根TVC(无需重新访问TVC A,为什么要这样做) 。我有:

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

...出现错误“开始/结束等的不平衡呼叫”。以下内容修复了错误,但没有动画:

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

这是我的最终解决方案,没有错误,仍然很生动:

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

当我将UIButton挂接到情节提要Segue动作时(在IB中),我遇到了此错误,但后来决定以编程方式调用按钮 performSegueWithIdentifier忘记从IB中删除第一个。

从本质上讲,它执行了两次segue调用,给出了此错误,并实际上两次推动了我的观点。解决方法是删除其中一个segue调用。

希望这可以帮助像我一样累的人!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.