การเรียกที่ไม่สมดุลเพื่อเริ่มต้น / สิ้นสุดการเปลี่ยนลักษณะสำหรับ <UITabBarController: 0x197870>


119

ฉันอ่าน SO เกี่ยวกับผู้ใช้รายอื่นที่พบข้อผิดพลาดที่คล้ายกันแต่ข้อผิดพลาดนี้อยู่คนละกรณี

ฉันได้รับข้อความนี้เมื่อฉันเพิ่ม View Controller ในตอนแรก:

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

โครงสร้างของแอพมีดังนี้:

ฉันมี TabBarController 5 แท็บที่เชื่อมโยงกับ 5 View Controllers ในแท็บการแสดงเริ่มต้นฉันเรียก 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

คำตอบ:


98

หากไม่เห็นรหัสรอบข้างมากขึ้นฉันไม่สามารถให้คำตอบที่ชัดเจนได้ แต่ฉันมีสองทฤษฎี

  1. คุณไม่ได้ใช้UIViewController's กำหนดค่าเริ่มต้น initWithNibName:bundle:initลองใช้มันแทนเพียง

  2. นอกจากนี้selfอาจเป็นหนึ่งในตัวควบคุมมุมมองของตัวควบคุมแถบแท็บ นำเสนอตัวควบคุมมุมมองจากตัวควบคุมมุมมองบนสุดเสมอซึ่งหมายความว่าในกรณีนี้ขอให้ตัวควบคุมแถบแท็บนำเสนอตัวควบคุมมุมมองซ้อนทับในนามของตัวควบคุมมุมมอง คุณยังคงสามารถให้ผู้รับมอบสิทธิ์เรียกกลับไปยังตัวควบคุมมุมมองจริงได้ แต่คุณต้องมีและปิดตัวควบคุมแถบแท็บ


2
# 1 แก้ไขปัญหานี้ให้ฉันฉันใช้ initWithNibName: nil bundle: nil แทน init
Hua-Ying

172
คุณสามารถสร้างคำเตือนนี้ได้โดยการนำเสนอ modal vc ก่อนที่แอปจะเริ่มต้นเสร็จสิ้น เช่นเริ่มแอปเทมเพลตแอปพลิเคชันแบบแท็บและนำเสนอ modal vc ที่ด้านบนของ self.tabBarController เป็นบรรทัดสุดท้ายในแอปพลิเคชัน: didFinishLaunching คำเตือนปรากฏขึ้น วิธีแก้ไข: ปล่อยให้สแต็กคลายออกก่อนนำเสนอ modal vc ด้วยวิธีอื่นเรียกใช้ด้วย performSelector withDelay: 0.0
Danh

9
และนี่คือคำถามอื่นที่อธิบายว่าเหตุใดจึงใช้งาน performSelector withDelay ได้ stackoverflow.com/questions/1922517/…
fatih

1
วิธีแก้ปัญหาของ danh ใช้ได้ผลสำหรับฉัน แต่ฉันต้องใช้ 0.1 แทนที่จะเป็น 0.0
Brandon O'Rourke

11
แทนที่จะใช้ performSelectorWithDelay ของศูนย์ให้ดำเนินการนี้ใน viewDidAppear แทน viewDidLoad หรือ whatnot
tooluser

40

ฉันแก้ไขข้อผิดพลาดนี้โดยเปลี่ยนภาพเคลื่อนไหวจาก YES เป็น NO

จาก:

[tabBarController presentModalViewController:viewController animated:YES];

ถึง:

[tabBarController presentModalViewController:viewController animated:NO];

4
วิธีนี้จะช่วยแก้ปัญหาได้หากคุณไม่สนใจเกี่ยวกับภาพเคลื่อนไหว แต่ถ้าคุณต้องการภาพเคลื่อนไหว: ใช่ลองแสดงความคิดเห็นของ danh ในคำตอบที่ยอมรับ: stackoverflow.com/questions/7886096/…
wxactly

3
FYI: presentModalViewController: ภาพเคลื่อนไหว: เลิกใช้แล้วใน iOS6
ZS

16

ตามที่โพสต์โดยdanh

คุณสามารถสร้างคำเตือนนี้ได้โดยการนำเสนอ modal vc ก่อนที่แอปจะเริ่มต้นเสร็จสิ้น เช่นเริ่มแอปเทมเพลตแอปพลิเคชันแบบแท็บและนำเสนอ modal vc ที่ด้านบนของ self.tabBarController เป็นบรรทัดสุดท้ายในแอปพลิเคชัน: didFinishLaunching คำเตือนปรากฏขึ้น วิธีแก้ไข: ปล่อยให้สแต็กคลายออกก่อนนำเสนอ modal 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];

ฉันมีตัวควบคุมมุมมองในกระดานเรื่องราวของฉันด้วยเหตุผลบางอย่างการใช้เพียงอย่างเดียว[[introvc alloc] init];ไม่ได้ผลสำหรับฉัน


1
ยินดีที่ได้พบคุณโดยใช้คุณลักษณะการเขียนเรื่องราวใหม่ แต่ฉันไม่ได้ใช้สตอรี่บอร์ดในกรณีของฉัน ...
Raptor

แค่อยากจะชี้ให้เห็นว่า "instantiateViewControllerWithIdentifier" ใช้ตัวระบุตัวควบคุม ดูรายละเอียดเพิ่มเติมได้ที่stackoverflow.com/questions/8186375/…
Kishor Kundan

2

ฉันแก้มันด้วยการเขียน

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

3
FYI เป็นสำนวนมากขึ้น (และปลอดภัยยิ่งขึ้น!) คุณควรทำ: เคลื่อนไหว: YES เสร็จสิ้น: ไม่มี
powerj1984

2
ฉันจะให้สำนวนคุณมากขึ้น แต่จะปลอดภัยกว่าอย่างไร
Zev Eisenberg

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 ในสตอรี่บอร์ดและฉันได้เลือกคุณสมบัติดัชนีเช่นนี้:

ใส่คำอธิบายภาพที่นี่

แต่ฉันลบมันและเพิ่มเมธอด 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 แนะนำปัญหาของฉันคือฉันกำลังนำเสนอ modal 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

สวิฟต์ 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

โดยพื้นฐานแล้วมันทำการโทรตามสองครั้งให้ข้อผิดพลาดนี้และผลักดันมุมมองของฉันสองครั้ง การแก้ไขคือการลบหนึ่งในการเรียกต่อ

หวังว่านี่จะช่วยใครสักคนที่เหนื่อยเท่าฉัน!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.