在关闭视图控制器时出现错误

17

在关闭视图控制器时出现错误,这是我之前从未遇到过的,互联网上也没有太多相关信息。

以下是错误内容: * 在-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished]中的断言失败,/SourceCache/UIKit/UIKit-2903.2/Keyboard/UIKeyboardTaskQueue.m:368

背景:我在保存一些数据后关闭了视图控制器。每次都能成功保存数据。最近我将日期保存代码更改为在主线程上运行此方法,因为我在后台保存时遇到了一些问题。

你有任何想法吗?

谢谢提前。


一点点代码可能会很有帮助。 - Lord Zsolt
请发布一些代码。 - Vinay Jain
抱歉,它确实是[self dismissViewController animated:YES onCompletion:nil];。可能有些不准确,这是我从记忆中写下来的。就在那之前,我使用了Parse数据方法[object save]; 它在主线程中运行。在此之前,我使用了后台方法,并且关闭视图控制器可以正常工作。 - Nick Farrant
刚想到一件事 - 我想要关闭的视图不是以模态显示,而是被推出。Xcode仍然支持popViewController吗?如果是这样,那将是更好的选择,不是吗? - Nick Farrant
你能验证一下你是否在主线程上解散视图控制器吗? - Hyperbole
我确定我是的...除了一些Parse数据方法,我没有任何后台运行的代码。 - Nick Farrant
6个回答

46

在调用-presentViewController:animated:completion:方法时,我收到了这个错误,此时所处的线程不是主线程(来自网络请求回调)。解决方案是将呈现和关闭视图控制器的调用分派到主线程:

dispatch_async(dispatch_get_main_queue(), ^{
    //Code that presents or dismisses a view controller here
});

1
这正是原因 - 我正在对 + (void)requestAccessForMediaType:(NSString *)mediaType completionHandler:(void (^)(BOOL granted))handler NS_AVAILABLE_IOS(7_0); 进行回调服务,而且回调不是在主线程上执行的。 - Lee
我也遇到了同样的错误。我应该把这段代码放在哪里? - Abhinandan Pratap
似乎如果必须在主线程上调用presentViewController:,那么它应该作为其内部功能的一部分进行调度。我为UIViewController创建了一个类别,并添加了一个方法presentViewController:withAnimation:completion:,以便方法名称不同,然后按照您建议的方式分派原始方法。看起来像是Cocoa的修复。我们不应该知道或关心presentViewController(仅限主线程)的这种限制,如果限制得到修复,这些细节应该被埋藏在Cocoa实现中。 - Cerniuk

8

我在调用相机视图时遇到了同样的问题。

同样问题的Swift语法:

dispatch_async(dispatch_get_main_queue(), { 
    //Code that presents or dismisses a view controller here  
    self.presentViewController(imagePicker, animated: true, completion: nil)
})

2

0

你不仅要确保present/dismissViewController在主线程上调用,还要确保present/dismissViewController是从相同的父视图控制器中调用的。

例如有两个导航控制器子视图。第一个子视图控制器为了完成某些任务而呈现了第二个子视图控制器,该任务将通过委托(接口)返回。在任务完成后,第二个子视图控制器会关闭自身并调用委托/接口函数,以便呈现另一个视图控制器(例如自定义弹出窗口)。这时就会出现错误,因为当弹出窗口被调用时,第二个子视图控制器没有关闭,但是当弹出窗口关闭时,它已经关闭了。

因此,在这种情况下:

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(400 * NSEC_PER_MSEC)), dispatch_get_main_queue(), { () -> Void in
            if let fs = self.scenarios[indexPath.item]{
                fs.loadScenario()
                sDelegate.onSelectedScenario(fs)
            }
        })

好的。


0
如果有人遇到了断言失败的问题,这里是 Swift 3 的解决方案:
OperationQueue.main.addOperation{
    <your segue or function call>
}

经过测试:在Xcode 8.3.2和Swift 3.1上


0
In the target view controller define delegate and protocol:

@class TargetVC;
@protocol TargetVCDelegate <NSObject>
-(void)dismissTargetVC:(TargetVC*)vc;
@end

@interface TargetVC : UIViewController
@property (nonatomic, weak) id<TargetVCDelegate> delegate;
@end

when you done the job at the target view controller call the delegate:
if( [self.delegate respondsToSelector:@selector(dismissTargetVC:)])
{
   [self.delegate dismissTargetVC:self];
}

The implementation of the delegate protocol should be:
-(void)dismissTargetVC:(TargetVC*)vc
{
    [vc dismissViewControllerAnimated:YES completion:nil];

    // you can get relevant data from vc as you still hold reference to it in this block

    // your code ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接