unwind segue / performSegueWithIdentifier: 问题

Posted

技术标签:

【中文标题】unwind segue / performSegueWithIdentifier: 问题【英文标题】:unwind segue / performSegueWithIdentifier: problems 【发布时间】:2013-11-27 18:41:29 【问题描述】:

在我的应用程序中,有一个聊天服务。当用户想给某人发消息时,他按下“新聊天”,然后显示一个表格视图控制器以从朋友列表中进行选择。我正在使用 unwind segues 以返回到前一个视图控制器并在两个视图控制器之间共享数据(主要是数据将成为聊天的朋友)。 unwind segue 完美运行,但是在我的应用程序中,当用户返回主 VC 时,我启动了一个新的 segue 以便直接转到另一个用户聊天的 VC;这不起作用。所有segues都连接良好,我在每个角落都尝试了NsLog,它正在进入那里,甚至prepareForSegue:正在被访问。我尝试放置一个 NsTimer,认为可能存在一些技术冲突,但它没有用。我将聊天 VC 的 segue 更改为模式,现在它给了我这个错误:

警告:尝试呈现不在窗口层次结构中的视图!

我可以通过其他方式访问此 VC,它位于层次结构中。我的问题是:可能出了什么问题? unwind segues 会改变 Windows 层次结构吗?

图片:

为了更直观地呈现问题,我正在谈论的主 VC 位于左下方,连接到导航视图控制器。当用户按下新聊天时,右上角的两个 VC 会出现(一个用于在朋友或群组之间进行选择,另一个用于显示朋友/群组)。因此,当从右上角的 VC 中选择一个朋友时,我应该将 segue 放松到主 VC。正如您从主要的 VC 中看到的,还有其他的 segues。如果我放松 segue,它们中的任何一个都不能工作,如果我正常操作,它们确实可以工作。

【问题讨论】:

纯粹是猜测,考虑到我们看不到您的故事板或代码,但很可能您正试图从展开 segue 已解除的视图中呈现视图控制器,您能否向我们展示任何代码或任何东西? @JAManfredi 好的,我添加了图片和解释。我猜代码很少,不会有任何区别。希望很清楚。 【参考方案1】:

它不工作并且给你这个错误的原因是因为事情没有按照你认为的顺序发生。

当展开发生时,可见的视图尚未关闭,因此您正试图对实际上不在该层次结构中的视图执行 segue,如错误所说,例如,将 NSLog 语句放在最终视图,然后在主视图控制器的 unwind 方法中,您可以看到以下内容:

2013-11-27 14:51:10.848 testUnwindProj[2216:70b] Unwind
2013-11-27 14:51:10.849 testUnwindProj[2216:70b] Will Appear
2013-11-27 14:51:11.361 testUnwindProj[2216:70b] View Did Disappear

因此,主视图中的展开被调用,视图将出现(您的主视图控制器),然后您的可见视图被关闭。这可能是一个简单的修复:

@interface ViewController () 
    BOOL _unwindExecuted;


@end

@implementation ViewController

- (void)viewWillAppear:(BOOL)animated

    NSLog(@"Will Appear");
    if (_unwindExecuted) 
        _unwindExecuted = NO;
        [self performSegueWithIdentifier:@"afterunwind" sender:self];
    


- (IBAction)unwind:(UIStoryboardSegue *)segue

    NSLog(@"Unwind");
    _unwindExecuted = YES;

【讨论】:

好的!感谢您的洞察力!对此,我真的非常感激!没想到任务让我有点不知所措。 我必须将 viewWillAppear 更改为 viewDidAppear,才能使我的代码正常工作,但感谢您的帮助。 就是这样!当然比具有硬编码时间的 dispatch_after 好得多【参考方案2】:

不要使用计时器或延迟来尝试预测视图何时可能存在。

改为使用如下调用:- (void)dismissViewControllerAnimated:(BOOL)标志完成:(void (^)(void))completion

完成块会在您回到主 VC 时通知您。或者,查看与 segue 相关的各种调用,以便准确了解何时可以在新窗口上执行操作。

如果一切都失败了,总是有 UIViewController viewDidAppear。

【讨论】:

这不是答案,而是对 OP 答案的评论【参考方案3】:

这是处理展开的视图控制器的常见问题,因为在展开期间,该视图控制器可能不在窗口层次结构中。

为了解决,我添加了一个属性segueIdentifierToUnwindTo 来协调展开。

这与 JAManfredi 的答案类似,但将其扩展为能够继续到任何视图控制器。

@interface FirstViewController ()
// Use this to coordinate unwind
@property (nonatomic, strong) NSString *segueIdentifierToUnwindTo;
@end

@implementation FirstViewController
- (void)viewDidAppear:(BOOL)animated 
    [super viewDidAppear:animated];   
    // Handle unwind
    if (_segueIdentifierToUnwindTo) 
        [self performSegueWithIdentifier:_segueIdentifierToUnwindTo sender:self];
        self.segueIdentifierToUnwindTo = nil;   // reset
        return;
    
    // Any other code..


// Example of an unwind segue "gotoLogin"
- (IBAction)gotoLogin:(UIStoryboardSegue*)sender 
    // Don't perform segue directly here, because in unwind, this view is not in window hierarchy yet!
    // [self performSegueWithIdentifier:@"Login" sender:self];
    self.segueIdentifierToUnwindTo = @"Login";

@end

我还在我的博客上通过FirstViewController 分享了我如何使用展开转场:http://samwize.com/2015/04/23/guide-to-using-unwind-segues/

【讨论】:

【参考方案4】:

好的解决了这个问题,问题是视图还不存在,我不得不推迟这个过程:

[self performSelector:@selector(fireNewConv) withObject:nil afterDelay:1];

但是,有趣的是,我尝试通过 NSTimer 进行延迟,但没有成功。

【讨论】:

这是最糟糕的实现方式。您只是在猜测展开动画何时结束。正如@Ike 正确建议的那样,这就是viewDidAppear: 的用途;使用一个实例变量来检测视图的这种外观是否是一个信号,表明是时候触发下一个 segue。一个更好的解决方案可能是实现segueForUnwindingToViewController:fromViewController:identifier:,它允许您完全控制代码中的整个展开过程。或者,只是手动展开(而不是展开转场)。 不要这样做。使用完成块。

以上是关于unwind segue / performSegueWithIdentifier: 问题的主要内容,如果未能解决你的问题,请参考以下文章

iOS从推送segue中检测unwind segue

UINavigationController 的 Unwind Segue

如何以编程方式执行Unwind segue?

以编程方式执行 unwind segue

从 Unwind segue 触发时,Push segue 不起作用

Unwind segue 不起作用 SWIFT