Несбалансированные вызовы для перехода начала / конца появления для <UITabBarController: 0x197870>


119

Я читал ТАК, что другой пользователь столкнулся с аналогичной ошибкой , но эта ошибка в другом случае.

Я получил это сообщение, когда изначально добавил View Controller:

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

Структура приложения следующая:

У меня есть TabBarController с 5 вкладками, связанный с 5 контроллерами представления. На вкладке начального показа я вызываю новый контроллер представления для наложения в качестве представления приложения.

Я использую этот код для вызова контроллера представления:

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

После IntroVCпоявления этого контроллера представления отображается указанная выше ошибка.

ps Я использую xCode 4.2 и iOS 5.0 SDK, разрабатываю приложение для iOS 4.3.


Привет, Шиван, у меня с тобой такая же проблема. Но я все еще не могу это исправить после просмотра ответов ниже. Могу я узнать, где вы вызываете контроллер представления?
ZYiOS

Ответы:


98

Не видя больше окружающего кода, я не могу дать однозначного ответа, но у меня есть две теории.

  1. Вы не используете UIViewController«ы назначенный инициализаторinitWithNibName:bundle: . Попробуйте использовать его вместо просто init.

  2. Также selfможет быть одним из контроллеров представления панели вкладок. Всегда представляйте контроллеры представления от самого верхнего контроллера представления, что означает, что в этом случае попросите контроллер панели вкладок представить контроллер представления наложения от имени контроллера представления. Вы по-прежнему можете сохранить любые делегаты обратного вызова для реального контроллера представления, но вы должны иметь контроллер панели вкладок и закрыть его.


2
# 1 исправил эту проблему для меня, я использовал initWithNibName: nil bundle: nil вместо init.
Хуа-Инь,

172
Вы можете сгенерировать это предупреждение, представив модальный vc до завершения инициализации приложения. т.е. запустите приложение-шаблон приложения с вкладками и представьте модальный vc поверх self.tabBarController в качестве последней строки в приложении: didFinishLaunching. Появляется предупреждение. Решение: позвольте стеку сначала развернуться, представьте модальный vc в другом методе, вызываемом с помощью performSelector withDelay: 0.0.
danh

9
И вот еще один вопрос, объясняющий, почему работает performSelector withDelay. stackoverflow.com/questions/1922517/…
fatih

1
Решение danh сработало для меня, но мне пришлось использовать 0,1, а не 0,0.
Брэндон О'Рурк,

11
Вместо того, чтобы использовать значение performSelectorWithDelay, равное нулю, выполните это в viewDidAppear, а не в viewDidLoad и т. Д.
tooluser

40

Я исправил эту ошибку, изменив анимацию с ДА на НЕТ.

Из:

[tabBarController presentModalViewController:viewController animated:YES];

Для того, чтобы:

[tabBarController presentModalViewController:viewController animated:NO];

4
Это решает проблему, если вас не волнует анимация, но если вам нужна анимация: ДА, попробуйте комментарий danh к принятому ответу: stackoverflow.com/questions/7886096/…
ровно

3
К вашему сведению: presentModalViewController: animated: устарел в iOS6.
ZS

16

Автор сообщения: danh

Вы можете сгенерировать это предупреждение, представив модальный vc до завершения инициализации приложения. т.е. запустите приложение-шаблон приложения с вкладками и представьте модальный vc поверх self.tabBarController в качестве последней строки в приложении: didFinishLaunching. Появляется предупреждение. Решение: позвольте стеку сначала развернуться, представьте модальный vc в другом методе, вызываемом с помощью performSelector withDelay: 0.0

Попробуйте переместить метод в viewWillAppear и защитить его, чтобы он выполнялся только один раз (рекомендуется настроить свойство).


Почему viewWillAppearи нет viewDidAppear?
CyberMew

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 секунды. После изменения задержки на большее значение все заработало. Это похоже на то, что переход не может быть выполнен слишком быстро после другого перехода.


7
Метод жизненного цикла представления viewDidAppear предназначен именно для этой цели и будет более надежным, чем введение искусственной задержки, fwiw.
tooluser

1
Это правильный ответ, за исключением того, что задержки в 0 достаточно, чтобы дождаться, пока контроллер навигации не будет готов к новой навигации.
malhal

Это совершенно правильно, вы должны вызвать это внутри, viewDidAppearчтобы он был UINavigationControllerготов с этим справиться. Я сменил свой пост на это;)
Alex Cio

Я чувствую, что это должно быть перемещено в viewWillAppear, тогда вам не нужно беспокоиться о том, инициализировано ли представление или нет.
Horsejockey

3

У меня была такая же проблема, когда мне нужно было представить мой контроллер представления входа в систему из другого контроллера представления. Если пользователь не авторизован, я сделал это в методе 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. Запуск ViewController с помощью метода storyboad instantiateViewControllerWithIdentifier. т.е.Intro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

У меня есть viewcontroller в моей раскадровке, по какой-то причине использование только [[introvc alloc] init];для меня не сработало.


1
приятно видеть, что вы используете новую функцию раскадровки. но я не использовал раскадровку в моем случае ...
Raptor

Просто хочу отметить, что instantiateViewControllerWithIdentifier принимает идентификатор контроллера. для получения дополнительной информации посетите stackoverflow.com/questions/8186375/…
Кишор Кундан

2

Я решил это написав

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

3
К вашему сведению, чтобы быть более идиоматичным (и более безопасным!), Вы должны сделать: animated: YES завершение: nil
powerj1984

2
Я дам вам больше идиоматики, но как это безопаснее?
Зев Айзенберг

2

У меня была эта проблема со сторонним кодом. Кто-то забыл установить super внутри viewWillAppear и viewWillDisappear в пользовательском классе TabBarController.

- (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 элементами, и я бессознательно пытался вызвать контроллер корневого представления элемента 1 в элементе 2 моей панели вкладок, используя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 был помещен в стек. Когда был вызван мой метод viewWillAppear моего UITableViewController, я снова установил логическое значение NO.


1

Я обнаружил, что если вы используете раскадровку, вам нужно поместить код, представляющий новый контроллер представления, в viewDidAppear. Он также избавится от предупреждения «Представление контроллеров представления на контроллерах отсоединенного представления не рекомендуется».


1

В Swift 2+ у меня работает:

У меня есть UITabBarViewController в раскадровке, и я выбрал свойство selectedIndex следующим образом:

введите описание изображения здесь

Но я удаляю его и добавляю в свой метод viewDidLoad моего исходного класса, например:

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

Я надеюсь, что смогу кому-то помочь.


0

На самом деле вам нужно дождаться окончания анимации push. Таким образом, вы можете делегировать 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

0

Как предположил @danh, моя проблема заключалась в том, что я представлял модальный vc до того, как он UITabBarControllerбыл готов. Однако мне было неудобно полагаться на фиксированную задержку перед представлением контроллера представления (из моего тестирования мне нужно было использовать задержку 0,05–0,1 с performSelector:withDelay:). Мое решение добавить блок , который вызывается на UITabBarController«S viewDidAppear:метод:

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) isAppearing animated: (BOOL) animated и - (void) endAppearanceTransition создается вместе в классе.


0

Я была такая же проблема. При разработке хотел обойти экраны. Я переходил от одного контроллера представления к другому в viewDidLoad, вызывая метод селектора.

Проблема в том, что мы должны позволить ViewController завершить переход перед переходом к другому ViewController.

Это решило мою проблему: задержка необходима, чтобы позволить ViewControllers завершить переход до перехода к другому.

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

У меня была эта проблема, когда я переходил от корневого 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 к действию перехода раскадровки (в IB), но позже решил, что кнопка будет вызывать программный вызов performSegueWithIdentifier забыв удалить первую из IB.

По сути, он дважды выполнил вызов segue, выдал эту ошибку и дважды подтолкнул мое представление. Исправление заключалось в удалении одного из вызовов перехода.

Надеюсь, это поможет кому-то так же уставшему, как я!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.