如何以编程方式重新排序我的UINavigationController项目?

时间:2016-02-15 02:54:30

标签: ios objective-c swift uinavigationcontroller

在我的TabBarViewController中,我正在创建一个导航并以模态方式呈现它。

func viewDidLoad(){ 
    super.viewDidLoad();
    //Create a present this view controller in viewDidLoad
    self.navController =  UINavigationController() 
    self.presentViewController(self.navController, animated: true, completion: nil)
}

//This method gets called when TabBarVC gets a NSNotification
func showMessageForUser(user_id: Int){
    let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
    mvc.user_id = user_id //set this user

    //display this to the user.
    self.navController.pushViewController(mvc, animated: true)
}

这很简单。但是,我想这样做:

  • 如果堆栈中的user_id 已经 ,请将其移至堆栈的末尾,以便用户可以看到它。没有重复。我怎么能这样做?
  • 否则,只需创建视图控制器的新实例并将其添加到堆栈顶部。我想我已经在上面这样做了。

重新排序后,所有“后退”按钮应按预期工作。

3 个答案:

答案 0 :(得分:4)

您可以使用setViewControllers(_:animated:)重新排列视图控制器堆栈。您无需执行任何特殊操作即可使后退按钮正常工作。导航控制器根据其viewControllers数组中的第二项设置后退按钮(如果有第二项),并在更新viewControllers数组时更新后退按钮。

以下是我如何做到这一点。首先,我们向UIViewController添加一个方法,询问它是否是特定userId的视图控制器。由于大多数视图控制器不是(也可能不是)正确的视图控制器,它只返回false

extension UIViewController {
    func isViewControllerForUserId(userId: Int) -> Bool {
        return false
    }
}

然后我们在MessagesViewController中覆盖此方法,以便在适当时返回true

extension MessagesViewController {
    override func isViewControllerForUserId(userId: Int) -> Bool {
        return self.userId == userId
    }
}

现在,为了显示特定用户的视图控制器,我们在导航控制器的堆栈中搜索现有的视图控制器。我们采取的行动取决于我们是否找到了它:

func showMessageForUserId(userId: Int) {
    if let index = navController.viewControllers.indexOf({ $0.isViewControllerForUserId(userId) }) {
        navController.moveToTopOfNavigationStack(viewControllerAtIndex: index)
    } else {
        pushNewViewControllerForUserId(userId)
    }
}

如果我们没有找到它,我们会创建一个新的视图控制器并推送它:

    private func pushNewViewControllerForUserId(userId: Int) {
        let vc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
        vc.userId = userId
        self.navController.pushViewController(vc, animated: true)
    }

如果我们确实找到了它,我们会使用以下方法将其移动到导航堆栈的顶部:

extension UINavigationController {

    func moveToTopOfNavigationStack(viewControllerAtIndex index: Int) {
        var stack = viewControllers
        if index == stack.count - 1 {
            // nothing to do because it's already on top
            return
        }
        let vc = stack.removeAtIndex(index)
        if (reorderingIsBuggy) {
            setViewControllers(stack, animated: false)
        }
        stack.append(vc)
        setViewControllers(stack, animated: true)
    }

    private var reorderingIsBuggy: Bool {
        // As of iOS 9.3 beta 3, `UINavigationController` drops the prior top-of-stack
        // when you use `setViewControllers(_:animated:)` to move a lower item to the
        // top with animation. The workaround is to remove the lower item from the stack
        // without animation, then add it to the top of the stack with animation. This
        // makes it display a push animation instead of a pop animation and avoids
        // dropping the prior top-of-stack.
        return true
    }

}

答案 1 :(得分:0)

这是方法showMessageForUser(_ :),用你的方法替换你的,就像魅力一样......

func showMessageForUser(user_id: Int){
    var mvcItem: MessagesViewController?
    var controllers: [UIViewController] = (self.navController?.viewControllers)!

    var matchindex: Int? = -1
    for (index, viewItem) in controllers.enumerate() {
        if (viewItem is MessagesViewController) {

            if viewItem.user_id == user_id {
                matchindex = index
                break
            }
        }
    }

    if matchindex != -1 {       //jus making sure a matching index was found
        mvcItem = (controllers.removeAtIndex(matchindex!)) as? MessagesViewController
    }

    if mvcItem != nil {
        self.navController?.viewControllers = controllers
        self.navController?.pushViewController(mvcItem!, animated: true)
    } else {
        let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
        mvc.user_id = user_id //set this user

        //display this to the user.
        self.navController.pushViewController(mvc, animated: true)
    }
}

答案 2 :(得分:0)

只是为了让代码更易于推理我将创建自己的UINavigationController。

这样的事情:

final class MessagesNavigationController: UINavigationController {

    // Option 1: If you want to dismiss everything that was in the midle, you can just do this:
    func showMessageForUser(user_id: Int){
        guard let messagesViewController = findMessagesViewController(withUserId: user_id) else {
            let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
            mvc.user_id = user_id
            pushViewController(mvc, animated: true)
            return
        }

        popToViewController(messagesViewController, animated: false)

    }

    private func findMessagesViewController(withUserId userId: Int) -> MessagesViewController? {
        for viewController in viewControllers {
            guard let messagesViewController = viewController as? MessagesViewController
                where messagesViewController.user_id == userId else { continue }
            return messagesViewController

        }
        return nil
    }

    // Option 2: If you want to move the viewcontroller from the position it currently is, to the end
    // but keeping everything in the middle, you can do this:
    func showMessageForUser2(user_id: Int){
        guard let index = findMessagesViewControllerIndex(withUserId: user_id) else {
            let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
            mvc.user_id = user_id
            pushViewController(mvc, animated: true)
            return
        }

        // This is the main change
        let viewController = viewControllers.removeAtIndex(index)
        pushViewController(viewController, animated: false)
    }

    private func findMessagesViewControllerIndex(withUserId userId: Int) -> Int? {
        for (index, viewController) in viewControllers.enumerate() {
            guard let messagesViewController = viewController as? MessagesViewController
                where messagesViewController.user_id == userId else { continue }
            return index

        }
        return nil
    }
}

所以你的TabBarController看起来像这样:

var navController: MessagesNavigationController!

override func viewDidLoad(){
    super.viewDidLoad();

    navController = MessagesNavigationController()
    presentViewController(self.navController, animated: true, completion: nil)
}

func showMessageForUser(user_id: Int){
    navController.showMessageForUser(user_id)
}