iOS应用程序错误 - 无法将self添加为子视图

Arn*_*nol 156 iphone objective-c ios

我收到了这个崩溃报告,但我不知道如何调试它.

Fatal Exception NSInvalidArgumentException
Can't add self as subview
0 ...    CoreFoundation  __exceptionPreprocess + 130
1    libobjc.A.dylib     objc_exception_throw + 38
2    CoreFoundation  -[NSException initWithCoder:]
3    UIKit   -[UIView(Internal) _addSubview:positioned:relativeTo:] + 110
4    UIKit   -[UIView(Hierarchy) addSubview:] + 30
5    UIKit   __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 1196
6    UIKit   +[UIView(Animation) performWithoutAnimation:] + 72
7    UIKit   -[_UINavigationParallaxTransition animateTransition:] + 732
8    UIKit   -[UINavigationController _startCustomTransition:] + 2616
9    UIKit   -[UINavigationController _startDeferredTransitionIfNeeded:] + 418
10   UIKit   -[UINavigationController __viewWillLayoutSubviews] + 44
11   UIKit   -[UILayoutContainerView layoutSubviews] + 184
12   UIKit   -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 346
13   QuartzCore  -[CALayer layoutSublayers] + 142
14   QuartzCore  CA::Layer::layout_if_needed(CA::Transaction*) + 350
15   QuartzCore  CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 16
16   QuartzCore  CA::Context::commit_transaction(CA::Transaction*) + 228
17   QuartzCore  CA::Transaction::commit() + 314
18   QuartzCore  CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 56
Run Code Online (Sandbox Code Playgroud)

iOS版本是7.0.3.有人经历过这种奇怪的崩溃吗?

更新:

我不知道我的代码在哪里造成了这次崩溃,所以我不能在这里发布代码,抱歉.

第二次更新

请参阅以下答案.

Rob*_*obP 49

我基于我最近调试过的类似的东西进行推测...如果你推动(或弹出)一个带有Animated的视图控制器:是的,它不会马上完成,如果你在动画之前再做一次推送或弹出就会发生坏事完成.您可以通过暂时将Push和Pop操作更改为Animated:NO(以便它们同步完成)并查看是否可以消除崩溃来轻松测试是否确实如此.如果这确实是您的问题,并且您希望将动画重新打开,那么正确的策略是实现UINavigationControllerDelegate协议.这包括以下方法,在动画完成后调用:

navigationController:didShowViewController:animated:
Run Code Online (Sandbox Code Playgroud)

基本上,您希望根据需要将一些代码移动到此方法中,以确保在动画完成并且堆栈准备好进行更多更改之前,不会发生任何可能导致更改NavigationController堆栈的操作.


Kal*_*lle 13

我们也开始讨论这个问题,很有可能我们的问题也是由同样的问题引起的.

在我们的例子中,我们不得不在某些情况下从后端提取数据,这意味着用户可能会点击某些内容,然后在导航推送发生之前会有一点延迟.如果用户正在快速敲击,他们最终可能会从同一个视图控制器中进行两次导航推送,从而触发了这个异常.

我们的解决方案是UINavigationController上的一个类别,它可以防止推送/弹出,除非顶级vc与给定时间点的顶级vc相同.

.h文件:

@interface UINavigationController (SafePushing)

- (id)navigationLock; ///< Obtain "lock" for pushing onto the navigation controller

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Uses a horizontal slide transition. Has no effect if the view controller is already in the stack. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops view controllers until the one specified is on top. Returns the popped controllers. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops until there's only a single view controller left on the stack. Returns the popped controllers. Has no effect if navigationLock is not the current lock.

@end
Run Code Online (Sandbox Code Playgroud)

.m文件:

@implementation UINavigationController (SafePushing)

- (id)navigationLock
{
    return self.topViewController;
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock) 
        [self pushViewController:viewController animated:animated];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock)
        return [self popToRootViewControllerAnimated:animated];
    return @[];
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock)
        return [self popToViewController:viewController animated:animated];
    return @[];
}

@end
Run Code Online (Sandbox Code Playgroud)

到目前为止,这似乎已经解决了我们的问题.例:

id lock = _dataViewController.navigationController.navigationLock;
[[MyApi sharedClient] getUserProfile:_user.id success:^(MyUser *user) {
    ProfileViewController *pvc = [[ProfileViewController alloc] initWithUser:user];
    [_dataViewController.navigationController pushViewController:pvc animated:YES navigationLock:lock];
}];
Run Code Online (Sandbox Code Playgroud)

基本上,规则是:在任何非用户相关的延迟之前从相关的导航控制器获取锁定,并将其包含在对push/pop的调用中.

"锁定"这个词的措辞可能稍微差一些,因为它可能暗示某种形式的锁定发生需要解锁,但由于在任何地方都没有"解锁"方法,所以可能没问题.

(作为旁注,"非用户相关延迟"是代码导致的任何延迟,即任何异步.用户点击动画推送的导航控制器不计算,并且不需要为那些做导航锁:版本例).

  • 我使用了基于你的修改版本:https://gist.github.com/mdewolfe/9369751.看起来已修好了. (4认同)
  • @Kalle此解决方案适用于推/弹.但是如果我使用segue如何解决这个错误呢? (2认同)

non*_*ive 12

此代码解决了该问题:https://gist.github.com/nonamelive/9334458

它使用私有API,但我可以确认它是App Store的安全.(我的一个使用此代码的应用程序已获得App Store的批准.)

@interface UINavigationController (DMNavigationController)

- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

@end

@interface DMNavigationController ()

@property (nonatomic, assign) BOOL shouldIgnorePushingViewControllers;

@end

@implementation DMNavigationViewController

#pragma mark - Push

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (!self.shouldIgnorePushingViewControllers)
    {
        [super pushViewController:viewController animated:animated];
    }

    self.shouldIgnorePushingViewControllers = YES;
}

#pragma mark - Private API

// This is confirmed to be App Store safe.
// If you feel uncomfortable to use Private API, you could also use the delegate method navigationController:didShowViewController:animated:.
- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [super didShowViewController:viewController animated:animated];
    self.shouldIgnorePushingViewControllers = NO;
}
Run Code Online (Sandbox Code Playgroud)


Arn*_*nol 8

我将在我的应用程序中描述有关此崩溃的更多详细信息并将其标记为已回答.

我的应用程序有一个UINavigationController,根控制器是一个UITableViewController,它包含一个note对象列表.note对象在html中有一个content属性.选择一个注释将转到详细控制器.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //get note object
    DetailViewController *controller = [[DetailViewController alloc] initWithNote:note];
    [self.navigationController pushViewController:controller animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

细节控制器

该控制器具有UIWebView,显示从根控制器传递的注释内容.

- (void)viewDidLoad
{
    ...
    [_webView loadHTMLString:note.content baseURL:nil];
    ...
}
Run Code Online (Sandbox Code Playgroud)

此控制器是webview控件的委托.如果笔记包含链接,请点按链接将转到应用内网页浏览器.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    WebBrowserViewController *browserController = [[WebBrowserViewController alloc] init];
    browserController.startupURL = request.URL;
    [self.navigationController pushViewController:webViewController animated:YES];
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

我每天都收到上面的崩溃报告.我不知道我的代码在哪里造成了这次崩溃.经过一些用户的帮助调查,我终于能够解决这个崩溃.这个html内容会导致崩溃:

...
<iframe src="http://google.com"></iframe>
...
Run Code Online (Sandbox Code Playgroud)

在详细控制器的viewDidLoad方法中,我将此html加载到webview控件,之后,立即使用request调用上面的委托方法.URL是iframe的源(google.com).这个委托方法在viewDidLoad => crash时调用pushViewController方法!

我通过检查navigationType修复了这个崩溃:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if (navigationType != UIWebViewNavigationTypeOther)
    {
        //go to web browser controller
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助

  • 当从`viewDidLoad`调用时,在没有动画的情况下推送控制器不是一个好选择吗? (2认同)

Lio*_*789 5

我有同样的问题,对我来说简单的工作是改变动画:是的动画:不.

看起来问题是由于动画没有及时完成.

希望这有助于某人.