当从另一个模态视图控制器调用并且两者都被解雇时,ABPeoplePicker 崩溃

发布于 2024-10-15 07:36:24 字数 4122 浏览 3 评论 0原文

(注意:我之前在我的项目上下文中提交了这个问题,但我现在在测试项目中重新创建了崩溃。任何帮助告诉我我做错了什么的帮助将不胜感激。)

调用 ABPeoplePicker 时发生崩溃来自另一个模态视图控制器。具体来说,主窗口有一个 NavController,它加载 myVC。然后 myVC 加载一个包含我的控制器的模式 NavController,然后调用 ABPeoplePicker。在此演示程序中,在 ABPeoplePicker 运行之前不需要用户干预。

如果您使用人员选择器中的搜索框,然后选择结果人员之一,则会发生崩溃。 (如果您使用模拟器,则需要在运行程序之前在“联系人”中添加一个人。)程序返回,但在关闭两个模态 VC 期间,我收到断言错误崩溃。每次在 iphone、ipad 以及两者的模拟器上都会出现这种情况。这似乎是一件很正常的事情,所以我很难相信这是一个真正的错误。崩溃消息是:

断言失败 -[ABMembersSearchDisplayController setActive:animated:], /SourceCache/UIKit_Sim/UIKit-1448.69/UISearchDisplayController.m:589 2011-01-31 13:51:11.903 testcrasher2[26044:207] * 由于未捕获而终止应用程序 例外 'NSInternalInconsistencyException', Reason: '搜索内容导航 控制器不得在之间更改 -setActive:YES 和 -setActive:NO'

因此,为了演示,在新的 Xcode iPhone Window 应用程序中,我修改了 didFinishLaunchingWithOptions 以调用我的控制器。然后我创建两个 VC,如下所示。 (请注意,您需要将 Addressbook 框架添加到目标。)这是整个程序...

AppDelegate.didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    myViewController *detailViewController = [[myViewController alloc] init];

    // Set the navigation controller as the window's root view controller and display.
    UINavigationController * navController = [[UINavigationController alloc] initWithRootViewController: detailViewController];

    self.window.rootViewController = navController;
    [self.window makeKeyAndVisible];

    [detailViewController release];
    [navController release];

    return YES;
}

myViewController.h:

@interface myViewController :  UIViewController<addDelegate>{
 }
@end

myViewController.m:

#import "myViewController.h"
#import "AddNewViewController.h"        

@implementation myViewController

- (void)controllerDidFinish:(addNewViewController *)controller  {
    [self dismissModalViewControllerAnimated:YES];
}

-(void) viewWillAppear:(BOOL)animated  {
    [super viewWillAppear: animated];

    addNewViewController *addController = [[addNewViewController alloc] init];
    addController.delegate = self;

    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addController];
    [self presentModalViewController:navController animated:YES];

    [navController release];
    [addController release];
}

@end

AddNewViewController.h:

#import <AddressBookUI/AddressBookUI.h>

@protocol addDelegate;

@interface addNewViewController : UIViewController  < ABPeoplePickerNavigationControllerDelegate> {
    id <addDelegate> delegate;  
}
    @property(nonatomic, assign) id <addDelegate> delegate;
@end


@protocol addDelegate <NSObject> 
    - (void)controllerDidFinish:(addNewViewController *)controller ; 
@end

AddNewViewController.m:

#import "AddNewViewController.h"

@implementation addNewViewController

@synthesize delegate;

-(void) viewDidAppear:(BOOL)animated {  
    ABPeoplePickerNavigationController * peoplepicker =  [[ABPeoplePickerNavigationController alloc] init] ;    
    peoplepicker.peoplePickerDelegate = self;
    [self presentModalViewController:peoplepicker animated:YES];
    [peoplepicker release];
}

#pragma mark AddressBook delegate methods

- (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker { 
    [self dismissModalViewControllerAnimated:YES];
}

- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
    [self.delegate controllerDidFinish:self ];  
    return NO;   //EDIT:  This MUST be YES or it will crash (see answer below)
}

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker 
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
      property:(ABPropertyID)property 
      identifier:(ABMultiValueIdentifier)identifier {
    return NO;
}

@end

(Note: I filed this question before in the context of my project, but I've now recreated the crash in a test project. Any help in telling me what I'm doing wrong would be appreciated.)

The crash occurs when calling ABPeoplePicker from another modal viewcontroller. Specifically, the main window has a NavController, which loads myVC. myVC then loads a modal NavController containing my controller, which then calls ABPeoplePicker. In this demo program, no user intervention is necessary until ABPeoplePicker runs.

The crash occurs if you use the search box in the people picker, and then select one of the resulting people. (If you use the simulator, you'll need to add a person in Contacts before running the program.) The program returns, but during the dismissal of the two modal VCs, I get an assertion error crash. It occurs every time on iphone, ipad, and simulators for both. This seems a very normal thing to do, so I find it hard to believe this is a real bug. The crash message is:

Assertion failure in
-[ABMembersSearchDisplayController setActive:animated:],
/SourceCache/UIKit_Sim/UIKit-1448.69/UISearchDisplayController.m:589 2011-01-31 13:51:11.903
testcrasher2[26044:207] *
Terminating app due to uncaught
exception
'NSInternalInconsistencyException',
reason: 'search contents navigation
controller must not change between
-setActive:YES and -setActive:NO'

So to demonstrate, in a new Xcode iPhone Window application, I modify the didFinishLaunchingWithOptions to call my controller. Then I create two VCs as follows. (Note you need to add Addressbook frameworks to the target.) Here's the entire program...

AppDelegate.didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    myViewController *detailViewController = [[myViewController alloc] init];

    // Set the navigation controller as the window's root view controller and display.
    UINavigationController * navController = [[UINavigationController alloc] initWithRootViewController: detailViewController];

    self.window.rootViewController = navController;
    [self.window makeKeyAndVisible];

    [detailViewController release];
    [navController release];

    return YES;
}

myViewController.h:

@interface myViewController :  UIViewController<addDelegate>{
 }
@end

myViewController.m:

#import "myViewController.h"
#import "AddNewViewController.h"        

@implementation myViewController

- (void)controllerDidFinish:(addNewViewController *)controller  {
    [self dismissModalViewControllerAnimated:YES];
}

-(void) viewWillAppear:(BOOL)animated  {
    [super viewWillAppear: animated];

    addNewViewController *addController = [[addNewViewController alloc] init];
    addController.delegate = self;

    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addController];
    [self presentModalViewController:navController animated:YES];

    [navController release];
    [addController release];
}

@end

AddNewViewController.h:

#import <AddressBookUI/AddressBookUI.h>

@protocol addDelegate;

@interface addNewViewController : UIViewController  < ABPeoplePickerNavigationControllerDelegate> {
    id <addDelegate> delegate;  
}
    @property(nonatomic, assign) id <addDelegate> delegate;
@end


@protocol addDelegate <NSObject> 
    - (void)controllerDidFinish:(addNewViewController *)controller ; 
@end

AddNewViewController.m:

#import "AddNewViewController.h"

@implementation addNewViewController

@synthesize delegate;

-(void) viewDidAppear:(BOOL)animated {  
    ABPeoplePickerNavigationController * peoplepicker =  [[ABPeoplePickerNavigationController alloc] init] ;    
    peoplepicker.peoplePickerDelegate = self;
    [self presentModalViewController:peoplepicker animated:YES];
    [peoplepicker release];
}

#pragma mark AddressBook delegate methods

- (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker { 
    [self dismissModalViewControllerAnimated:YES];
}

- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
    [self.delegate controllerDidFinish:self ];  
    return NO;   //EDIT:  This MUST be YES or it will crash (see answer below)
}

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker 
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
      property:(ABPropertyID)property 
      identifier:(ABMultiValueIdentifier)identifier {
    return NO;
}

@end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜柠檬 2024-10-22 07:36:24

事实证明这是一个真正的错误。确实,当用户单击搜索中的某个人时,如果您对 ABPeoplePicker 执行两次 ModalVC 关闭,您将会遇到此崩溃。幸运的是,有一个简单的解决方法:在委托的 shouldContinueAfterSelectingPerson 中返回 YES。当您同时关闭选择器时,返回 YES 或 NO 并不重要,它不会继续,但 NO 会崩溃,而 YES 则不会。 (与我原来的帖子的答案相同:ABPeoplePicker 中的奇怪崩溃

Turns out this is an actual bug. It is indeed true that if you do a double ModalVC dismiss to ABPeoplePicker when the user clicks a person in search, you'll get this crash. Fortunately, there's a simple workaround: return YES in your delegate's shouldContinueAfterSelectingPerson. As you're simultaneously dismissing the picker, it doesn't really matter whether you return YES or NO, it won't continue, but NO will crash and YES doesn't. (Same answer as for my original post: Weird crash in ABPeoplePicker )

背叛残局 2024-10-22 07:36:24

该错误实际上存在于您的代码中。我花了几分钟找到它,我会尽力解释。

  1. 你的
    ABPeoplePickerNavigationController
    目前以模态方式呈现。
  2. 您单击搜索栏并输入
    一些东西。
  3. 您单击一个人的名字。

这里发生的情况是,ABPeoplePickerNavigationController 询问其委托(即您的 addNewViewController)在选择一个人后是否应该继续。当它等待您的回复时,您突然调用您自己的协议方法(在 myViewController 中),该方法尝试关闭模态 addNewViewController。您已经超前了,因为 ABPeoplePickerNavigationController 仍然处于打开状态。

将 ABPeoplePickerNavigationControllerDelegate 方法的实现更改为:

- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
    // This line is new.
    [self.navigationController dismissModalViewControllerAnimated:YES];
    [self.delegate controllerDidFinish:self];
    return NO;
}

并且您的崩溃将会消失。当您处理一层又一层的 UIViewController 和 UINavigationController 时,您必须非常小心地按照与呈现它们相反的顺序来消除它们。

The bug is in fact in your code. Took me a few minutes to find it, I'll try to explain as best I can.

  1. Your
    ABPeoplePickerNavigationController
    is currently presented modally.
  2. You click in the search bar and type
    some stuff.
  3. You click a person's name.

What happens here, is the ABPeoplePickerNavigationController asks its delegate (which is your addNewViewController) whether it should continue after selecting a person. While it's waiting to hear back from you, you suddenly call your own protocol's method (in myViewController) that attempts to dismiss the modal addNewViewController. You're jumping ahead of yourself, as the ABPeoplePickerNavigationController is still open.

Change your implementation of the ABPeoplePickerNavigationControllerDelegate method to read:

- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
    // This line is new.
    [self.navigationController dismissModalViewControllerAnimated:YES];
    [self.delegate controllerDidFinish:self];
    return NO;
}

And your crash will go away. When you're dealing with layers upon layers of UIViewControllers and UINavigationControllers, you have to be very careful to dismiss them in the reverse order you presented them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文