向前传递后如何将数据向后传递给视图控制器?

Posted

技术标签:

【中文标题】向前传递后如何将数据向后传递给视图控制器?【英文标题】:How to pass data backwards to a view controller after passing forward? 【发布时间】:2017-07-25 19:06:15 【问题描述】:

我正在开发一个测验应用程序,在要求您回答问题的初始视图控制器之后出现了第二个视图控制器。在第二个视图控制器上,用户必须按下一个按钮才能返回到初始视图控制器以被问到另一个问题。但是,当我从第二个视图控制器返回时,我相信正在创建初始视图控制器的新实例,并且会向用户询问他们已经回答的问题。我的初始视图控制器的 swift 文件中的代码是这样构造的,当用户被问到一个问题时,一旦该问题通过它的索引从数组中删除。我怎样才能使它的初始视图控制器的新实例不是通过从第二个视图控制器进行 segue 而创建的?或者有没有办法第二个视图控制器可以访问与初始视图控制器相同的方法?

这是我的初始视图控制器代码:

import UIKit

class ViewController: UIViewController 


var questionList = [String]()


func updateCounter() 

    counter -= 1
    questionTimer.text = String(counter)

    if counter == 0 


        timer.invalidate()
        wrongSeg()
        counter = 15

    






func randomQuestion() 



    //random question
    if questionList.isEmpty 
        questionList = Array(QADictionary.keys)

        questionTimer.text = String(counter)




    






    let rand = Int(arc4random_uniform(UInt32(questionList.count)))
    questionLabel.text = questionList[rand]


    //matching answer values to go with question keys
    var choices = QADictionary[questionList[rand]]!

      questionList.remove(at: rand)



    //create button
        var button:UIButton = UIButton()

    //variables
    var x = 1
    rightAnswerBox = arc4random_uniform(4)+1


        for index in 1...4
        



            button = view.viewWithTag(index) as! UIButton

            if (index == Int(rightAnswerBox))
            
                button.setTitle(choices[0], for: .normal)

            

            else 
                button.setTitle(choices[x], for: .normal)
                x += 1

            


            randomImage()

        
    


let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]



//wrong view segue
func wrongSeg() 

   performSegue(withIdentifier: "incorrectSeg", sender: self)



//proceed screen
func rightSeg() 

    performSegue(withIdentifier: "correctSeg", sender: self)




//variables
var rightAnswerBox:UInt32 = 0
var index = 0



//Question Label
@IBOutlet weak var questionLabel: UILabel!

//Answer Button
@IBAction func buttonAction(_ sender: AnyObject) 

if (sender.tag == Int(rightAnswerBox))



    rightSeg()


    print ("Correct!")



    if counter != 0 

        counter = 15
        questionTimer.text = String(counter)
    
else if (sender.tag != Int(rightAnswerBox)) 

    wrongSeg()
print ("Wrong!")
    timer.invalidate()
    questionList = []

    

   randomQuestion()



override func viewDidAppear(_ animated: Bool)
 
randomQuestion()








//variables
var counter = 15

var timer = Timer()

@IBOutlet weak var questionTimer: UILabel!

override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)



这是第二个视图控制器的代码:

import UIKit




 class ContinueScreen: UIViewController 




//correct answer label
@IBOutlet weak var correctLbl: UILabel!

//background photo
@IBOutlet weak var backgroundImage: UIImageView!

func backToQuiz() 

    if let nav = self.navigationController 
        nav.popViewController(animated: true)
        
    else 

        self.dismiss(animated: true, completion: nil)
        

    

@IBAction func `continue`(_ sender: Any) 


    backToQuiz()

【问题讨论】:

【参考方案1】:

先生,您需要的是代表或放松的继续。我更喜欢委托,因为它们更容易理解,而且我认为更强大。

在您的 ContinueScreen 视图控制器中定义类似于此的协议:

protocol QuizCompletedDelegate 
    func completedQuiz()
    func canceledQuiz()

同样在您的 ContinueScreen 视图控制器中,您需要声明一个 QuizCompletedDelegate 类型的可选委托

var delegate: QuizCompletedDelegate?

...并在适当的时候使用该委托调用函数:

@IBAction func didPressContinue(_ sender: Any) 
    if allQuestionsAnswered == true 
        delegate.completedQuiz()
     else 
        delegate.cancelledQuiz()
    

在您的初始视图中,控制器是您实现协议功能的地方:

extension ViewController: QuizCompletedDelegate 
    func completedQuiz() 
        //Handle quiz complete
    
    func cancelledQuiz() 
        //Handle quiz cancelled
    

那么您需要做的最后一件事是在初始视图控制器的 prepareForSegue 函数中设置 ContinueScreen 视图控制器的委托。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    if segue.identifier == "showContinueScreen" 
        let continueVC = segue.destination as! ContinueScreen
        continueVC.delegate = self
    

在您的初始视图控制器的 ViewDidAppear 上删除对 randomQuestion() 的调用,您就可以开始工作了!

【讨论】:

if 语句中的 allQuestionsAnswered 是从哪里得到的? 这只是一个占位符,用于确定是否所有问题都已回答。这不是复制和粘贴代码,您需要对其进行一些调整以适应您要执行的具体操作。这就是你如何使用委托模式来完成你想要的。 感谢您的洞察,非常感谢! 没问题!如果您发现它有帮助,请随意投票和/或接受答案!【参考方案2】:

返回时不应创建新的视图控制器。如果是这样 - 这很糟糕 - 你应该重新设计你的应用程序的流程......为了向前传递数据,我们应该使用依赖注入。要将数据传递给前一个视图控制器 - 使用 ios 委托模式。我建议花 30-40 分钟阅读(并理解这篇文章),它会让你以后更清楚:http://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/。

【讨论】:

以上是关于向前传递后如何将数据向后传递给视图控制器?的主要内容,如果未能解决你的问题,请参考以下文章

接收到本地通知数据后如何创建使用目标 c 传递给主视图控制器?

swift 使用委托和协议向前和向后传递数据

如何使用按钮在 UIPageViewController 中向前和向后导航?

在 MVC 5 中,如何将表对象数据作为列表传递给控制器​​视图模型?

如何在控制器中获取视图然后渲染数据并将其传递给 ajax

如何通过 2 个视图控制器传递 textfield.text 数据