如何在Swift中从父视图控制器传递数据到嵌入式视图控制器?

5
我有一个视图控制器嵌套在另一个VC中。我想从主VC中获取变量的值并将其传递到嵌入式VC中。具体而言,我想根据label1的值更改label2的文本。
我尝试使用“prepareForSegue”,但似乎它不会触发嵌入式视图控制器。我尝试在测试项目中隔离问题:

enter image description here

主VC的代码:

class MyViewController: UIViewController {
    @IBOutlet weak var label1: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = "Hello"
    }
}

嵌入式VC的代码:

class EmbeddedVC: UIViewController {
    @IBOutlet weak var label2: UILabel!

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

感谢您的帮助 :)


1
这不是嵌入式系统。在将标签添加到问题之前,请阅读标签描述。 - Lundin
3个回答

8
首先,在prepareForSegue中不能直接设置EmbeddedVC的lable2.text,因为调用顺序如下:
  1. MainVC的prepareForSeque此时EmbeddedVC的label2为nil
  2. EmbeddedVC的viewDidLoad被调用,然后label2被加载
  3. MainVC的viewDidLoad被调用,然后label1被加载
因此,如果你在prepareForSeque中将MainVC的label1.text分配给EmbeddedVC的label2.text,那么label1和label2都是nil,所以不起作用。
解决这个问题有两种方法:
第一种解决方案: MainViewController有EmbeddedVC,当MainVC的viewDidLoad被调用时,将label1.text分配给embeddedVC.label2.text。
class MyViewController: UIViewController {
    @IBOutlet weak var label1: UILabel!
    var embeddedVC: EmbeddedViewController? = nil

    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = "Hello"
        embeddedVC?.label2.text = label1.text
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let embeddedVC = segue.destination as? EmbeddedViewController {
            self.embeddedVC = embeddedVC
        }
    }
}

class EmbeddedViewController: UIViewController {
    @IBOutlet weak var label2: UILabel!

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

第二种解决方案是,在MainVC的viewWillAppear或viewDidAppear方法中获取协议,并获取标签文本(稍后调用viewDidLoad)。
protocol EmbeddedVCDelegate: class {
    func labelText() -> String?
}

class MyViewController: UIViewController, EmbeddedVCDelegate {
    @IBOutlet weak var label1: UILabel!

    // MARK: EmbeddedVCDelegate
    func labelText() -> String? {
        return label1.text
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = "Hello"
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let embeddedVC = segue.destination as? EmbeddedViewController {
            embeddedVC.delegate = self
        }
    }
}

class EmbeddedViewController: UIViewController {
    @IBOutlet weak var label2: UILabel!
    weak var delegate: EmbeddedVCDelegate? = nil
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        label2.text = delegate?.labelText()
    }
}

1
很好的解决方案,但embeddedVC应该被标记为weak。MyViewController已经拥有它了。没有必要有2个所有者。 - Arda Oğul Üçpınar

8
一种实现这个目标的方法是在父视图控制器的viewDidLoad方法中获取子视图控制器的实例。看起来父视图控制器的viewDidLoad方法会在子视图控制器的viewDidLoad方法之后被调用,这意味着标签已经在子视图中创建了。
override func viewDidLoad() {
    super.viewDidLoad()

    if let childVC = self.childViewControllers.first as? ChildVC {
        childVC.someLabel.text = "I'm here. Aye-aye."
    }   
}

Cruz的解决方案也可以工作,但这个似乎更加直接,谢谢 ;) - Eric

3

你应该尝试这样使用 prepareForSegue:

if segue.identifier == "identifier" {
        guard let destinationViewController = segue.destination as? VC2 else { return }

        destinationViewController.label2.text = mytext
    }

在故事板中分配的segue标识符


1
这应该是正确的解决方案。它是最直接的,并且具有OP想要的效果。 - craft

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