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


119

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

Я отримав це повідомлення, коли спочатку додав контролер перегляду:

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

Структура програми така:

У мене 5-вкладковий TabBarController, пов'язаний з 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 поверх self.tabBarController як останній рядок у програмі: didFinishLaunching. З'являється попередження. Рішення: нехай стек спочатку розкручується, представляє модальний vc в іншому методі, з яким викликається PerformSelector withDelay: 0.0.
дан

9
І ось ще одне питання пояснює, чому PerformSelector withDelay працює. stackoverflow.com/questions/1922517/…
fatih

1
Розв’язання данх працювало на мене, але мені довелося використовувати 0,1, а не 0,0.
Брендон О'Рурк

11
Замість того, щоб використовувати ExecuSelectorWithDelay з нуля, виконайте це в viewDidAppear, а не viewDidLoad чи що.
інструментів

40

Я виправив цю помилку, змінивши анімовані з ТАК на НІ.

Від:

[tabBarController presentModalViewController:viewController animated:YES];

До:

[tabBarController presentModalViewController:viewController animated:NO];

4
Це усуває проблему , якщо ви не дбаєте про анімації, але якщо вам потрібно анімований: ТАК, спробуйте Danh свій коментар на загальноприйнятому відповідь: stackoverflow.com/questions/7886096 / ...
wxactly

3
FYI: presentModalViewController: анімований: був застарілий у iOS6.
ZS

16

Як опублікував danh

Ви можете генерувати це попередження, подавши модальний комп'ютер, перш ніж програма буде ініціалізована. тобто запустіть додаток шаблону програми на вкладці та представіть модальний 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.
інструментів

1
Це правильна відповідь, окрім затримки 0 достатньо, щоб зачекати, поки навігаційний контролер буде готовий до нової навігації.
мальхал

Це абсолютно правильно, вам потрібно зателефонувати всередину, viewDidAppearщоб UINavigationControllerготовий впоратися з цим. Я змінив свою посаду на це;)
Алекс Ціо

Я відчуваю, що це слід перенести на viewWillAppear, тоді вам не доведеться турбуватися про те, наскільки подання ініціалізовано чи ні.
верховий хокей

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

У мене є контролер перегляду в моїй розгортці, чомусь використання лише [[introvc alloc] init];не працювало для мене.


1
приємно бачити вас за допомогою нової функції раскадровки. але в моєму випадку я не використовував раскадровку ...
Раптор

Просто хотілося вказати на це, що "instantiateViewControllerWithIdentifier" приймає ідентифікатор контролера. для більш детальної інформації перевірити stackoverflow.com/questions/8186375/…
Кішор Кундан

2

Я вирішив це написанням

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

3
FYI, щоб бути більш ідіоматичним (і безпечнішим!), Ви повинні зробити: анімований: ТАК завершення: nil
powerj1984

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

2

У мене була ця проблема з кодом сторонньої сторони. Хтось забув встановити супер всередині 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 було висунуто на стек. Коли мій вигляд методу UITableViewController викликався методом виклику WillAppear, я повернув булеву функцію до НІ.


1

Я виявив, що якщо ви використовуєте табло, ви захочете поставити код, який представляє новий контролер перегляду у viewDidAppear. Це також позбудеться попередження "Подання контролерів подання на контролери відокремленого виду не рекомендується".


1

У Swift 2+ для мене працює:

У мене є UITabBarViewController у табло, і я вибрав властивістьIndex на зразок цього:

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

Але я видаляю його і додаю у свій метод 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

0

Як @danh запропонував, моє питання полягало в тому, що я представляв модальний vc до UITabBarControllerготовності. Однак мені було незручно покладатися на фіксовану затримку перед тим, як подати контролер перегляду (для мого тестування мені потрібно було використовувати затримку 0,05-0,1s 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 анімоване: (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 до дії зразка розкадрівки (в IB), але пізніше вирішив натиснути кнопку програматично performSegueWithIdentifier забувши видалити першу з ІБ.

По суті, вона виконувала дзвінок Segue двічі, давала цю помилку і фактично двічі підштовхувала мене. Виправлення полягало в тому, щоб зняти один із викликів Segue.

Сподіваюсь, це допомагає комусь набридлому, як я!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.