在我的观点中,我怎么知道孩子什么时候被解开?

时间:2015-04-14 02:59:37

标签: ios swift model-view-controller

当我的孩子执行unwind segue时,我的控制器的viewDidAppear会被调用。

在这种方法中(仅此方法,我需要知道它是否来自放松)

注意:孩子正在向第一个视图控制器展开,因此这是一个中间视图控制器,而不是真正的根。

6 个答案:

答案 0 :(得分:6)

您应该能够使用以下内容在每个控制器中检测视图控制器的曝光是由于被推/呈现的结果,还是由于弹出/解除/展开而暴露的结果。

这可能或可能足以满足您的需求。

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

    // Handle controller being exposed from push/present or pop/dismiss
    if (self.isMovingToParentViewController || self.isBeingPresented){
        // Controller is being pushed on or presented.
    }
    else{
        // Controller is being shown as result of pop/dismiss/unwind.
    }
}

如果你想知道viewDidAppear被调用因为unwind segue与传统的pop / dismiss被调用不同,那么你需要添加一些代码来检测是否发生了展开。为此,您可以执行以下操作:

对于您想要完全检测到展开的任何中间控制器,请添加以下形式的属性:

/** BOOL property which when TRUE indicates an unwind occured. */
@property BOOL unwindSeguePerformed;

然后重写unwind segue方法canPerformUnwindSegueAction:fromViewController:withSender:方法,如下所示:

- (BOOL)canPerformUnwindSegueAction:(SEL)action
                 fromViewController:(UIViewController *)fromViewController
                         withSender:(id)sender{
  // Set the flag indicating an unwind segue was requested and then return
  // that we are not interested in performing the unwind action.
  self.unwindSeguePerformed = TRUE;

  // We are not interested in performing it, so return NO. The system will
  // then continue to look backwards through the view controllers for the 
  // controller that will handle it.
  return NO;
}

现在你有一个标志来检测一个展开,以及一个在它发生之前检测展开的方法。然后调整viewDidAppear方法以包含此标记。

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

    // Handle controller being exposed from push/present or pop/dismiss
    // or an unwind
    if (self.isMovingToParentViewController || self.isBeingPresented){
        // Controller is being pushed on or presented.
        // Initialize the unwind segue tracking flag.
        self.unwindSeguePerformed = FALSE;
    }
    else if (self.unwindSeguePerformed){
        // Controller is being shown as a result of an unwind segue
    }
    else{
        // Controller is being shown as result of pop/dismiss.
    }
}

希望这符合您的要求。

有关处理展开segue链的文档,请参阅:https://developer.apple.com/library/ios/technotes/tn2298/_index.html

答案 1 :(得分:2)

这是UIViewController上的一个简单类别,您可以使用它来跟踪您呈现的视图控制器是否处于展开segue中。我想它可能会更多,但我相信这对你的情况很有用。

要使用它,您需要在目标视图控制器上注册unwind操作方法中的unwind segue:

- (IBAction) prepareForUnwind:(UIStoryboardSegue *)segue
{
    [self ts_registerUnwindSegue: segue];
}

那就是它。从中间视图控制器,您可以测试您是否处于展开segue:

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

    BOOL unwinding = [self ts_isUnwinding];

    NSLog( @"%@:%@, unwinding: %@", self.title, NSStringFromSelector(_cmd), unwinding ? @"YES" : @"NO" );
}

没有必要清理任何东西; segue会在结束时自行注销。

以下是完整类别:

@interface UIViewController (unwinding)

- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue;
- (BOOL) ts_isUnwinding;

@end


static NSMapTable* g_viewControllerSegues;

@implementation UIViewController (unwinding)

- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        g_viewControllerSegues = [NSMapTable weakToWeakObjectsMapTable];
    });

    for ( UIViewController* vc = segue.sourceViewController ; vc != nil ; vc = vc.presentingViewController )
    {
        [g_viewControllerSegues setObject: segue forKey: vc];
    }
}

- (BOOL) ts_isUnwinding
{
    return [g_viewControllerSegues objectForKey: [self ts_topMostParentViewController]] != nil;
}

- (UIViewController *)ts_topMostParentViewController {
    UIViewController *viewController = self;
    while (viewController.parentViewController) {
        viewController = viewController.parentViewController;
    }
    return viewController;
}

@end

答案 2 :(得分:1)

你的问题对我来说真的很有意思,因为我从来没有使用IB和segues(不要因此而评判我)并且想要学习新的东西。正如您在评论中所描述的那样:

  当B回到C

时,将在A上调用

viewDidAppear

所以我想出了一个简单的自定义解决方案:

protocol ViewControllerSingletonDelegate: class {

    func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController)
}

class ViewControllerSingleton {

    static let sharedInstance = ViewControllerSingleton()

    private var delegates: [ViewControllerSingletonDelegate] = []

    func addDelegate(delegate: ViewControllerSingletonDelegate) {

        if !self.containsDelegate(delegate) {

            self.delegates.append(delegate)
        }
    }

    func removeDelegate(delegate: ViewControllerSingletonDelegate) {

        /* implement any other function by your self :) */
    }

    func containsDelegate(delegate: ViewControllerSingletonDelegate) -> Bool {

        for aDelegate in self.delegates {

            if aDelegate === delegate { return true }
        }

        return false
    }

    func forwardToDelegate(closure: (delegate: ViewControllerSingletonDelegate) -> Void) {

        for aDelegate in self.delegates { closure(delegate: aDelegate) }
    }
}


class SomeViewController: UIViewController, ViewControllerSingletonDelegate {

    let viewControllerSingleton = ViewControllerSingleton.sharedInstance

    func someFunction() { // some function where you'll set the delegate

        self.viewControllerSingleton.addDelegate(self)
    }

    /* I assume you have something like this in your code */
    @IBAction func unwindToSomeOtherController(unwindSegue: UIStoryboardSegue) {

        self.viewControllerSingleton.forwardToDelegate { (delegate) -> Void in

            delegate.viewControllerWillUnwind(unwindSegue.sourceViewController, toViewController: unwindSegue.destinationViewController)
        }

        /* do something here */
    }

    // MARK: - ViewControllerSingletonDelegate
    func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController) {

        /* do something with the callback */
        /* set some flag for example inside your view controller so your viewDidAppear will know what to do */
    }
}

您还可以修改回调函数以返回其他内容,例如控制器标识符,而不是控制器本身。

我以编程方式完成所有工作,所以请不要因此而对我进行评判。 ;)

如果此代码段无法帮助您,我仍然希望看到一些反馈。

答案 3 :(得分:1)

假设segue导航是ViewController - > FirstViewController - > SecondViewController。从SecondViewControllerViewController有一个放松。您可以在中介FirstViewController中添加以下代码来检测展开操作。

import UIKit

class FirstViewController: UIViewController {

    var unwindAction:Bool = false
    override func viewDidAppear(animated: Bool) {
        if unwindAction {
            println("Unwind action")
            unwindAction = false
        }
    }
    override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController? {
        self.unwindAction = true

         return super.viewControllerForUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender)
    }

}

修改 在考虑了这个问题之后,我决定解决这个问题取决于你在这里处理的复杂程度。当您放松时,您到底做了什么?这里给出的解决方案是可行的,它们可以工作 - 只有当你想要检测它是否是一个展开动作时。如果要在展开发生的点之间传递数据,该怎么办?如果您想在其中一个中间视图控制器中进行一系列复杂的准备工作,该怎么办?如果你想做这两件事怎么办?

在这种复杂的场景中,我会立即排除覆盖视图控制器的展开方法。做这样的操作会有效,但不会干净。一种方法将做它不应该做的事情。闻到了吗?那是代码味道。

如果视图控制器以某种方式通知事件层次结构中的下一个视图控制器,该怎么办?更好的是,我们如何在不紧密耦合这两者的情况下做到这一点?

协议。

具有类似的协议定义:

protocol UnwindResponding {
    prepareForUnwindSegue(segue:UISegue , formViewController:UIViewController, withImportantInfo info:[String,AnyObject])
}

使用协议,您将保持对象之间的关系 - 在这种情况下视图控制器的层次结构 - 显式。在发生特定事件时,您将调用委托给层次结构中的下一个控制器,以通知另一个视图控制器中特定事件的发生。这是一个例子:

override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {

    if let unwindResponder = self.presentingViewController as? UnwindResponding where segue.identifier = "unwindSegue" {
        unwindResponder.prepareForUnwindSegue(segue:UISegue, fromViewController:self,info:info)
    }


}

在中间视图控制器中,您可以执行以下操作:

extension IntermediaryViewController : UnwindResponding {
    prepareForUnwindSegue(segue:UISegue , fromViewController:UIViewController, withImportantInfo info:[String,AnyObject]) {
        if let unwindResponder =  self.presentingViewController {
            unwindResponder.prepareForUnwindSegue(segue,fromViewController:fromViewController, info:info)
        }
        unwindSegue = true
    }
}

当然,如果您只是想要检测展开的segues,你不会想要这样做。也许你这样做,你永远不会知道将来会发生什么。永远不要伤害你的代码清洁。

答案 4 :(得分:1)

在父视图控制器中添加方法

@IBAction func unwindToParent(unwindSegue: UIStoryboardSegue) {
    if let childViewController = unwindSegue.sourceViewController as? ChildViewController {
        println("unwinding from child")
    }
}

作为示例,如果unwind segue与按钮相关,则在故事板中将按钮链接到它的视图控制器出口

enter image description here

建议链接到unwindToParent方法

enter image description here

然后每次执行展开segue时,都会调用unwindToParent方法

答案 5 :(得分:0)

您可以覆盖函数import sys from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html' # PDFJS = 'file:///usr/share/pdf.js/web/viewer.html' PDF = 'file:///path/to/my/sample.pdf' class Window(QtWebEngineWidgets.QWebEngineView): def __init__(self): super(Window, self).__init__() self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF))) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) window = Window() window.setGeometry(600, 50, 800, 600) window.show() sys.exit(app.exec_()) ,该函数在ViewController位于展开segue的路径时调用。它意味着用于重新配置ViewController。

Swift示例:

unwindForSegue:towardsViewController: