SWIFT - 如何更改另一个 ViewController 上计算函数输出的标签?

SWIFT - How to change a label from a calculated function output on another ViewController?

Swift 5:

我正在尝试将在嵌入式视图中调用的函数的结果传递给 parent ViewController,以便更改 [=53= 底部的标签] ViewController.

除了描述性文字,我的主要 ViewController 包含四个元素。

  1. 3 组件选择器视图
  2. 包含 3 个独立的 1 元素选择器视图的嵌入式视图
  3. 包含从函数计算和输出的 2 个数字中的第一个的第一个标签
  4. 包含从函数计算和输出的 2 个数字中的第 2 个数字的第 2 个标签

换句话说,屏幕上有 6 个可见的拾取器组件(1 个拾取器有 3 个组件和 3 个 1-组件拾取器)。但是3个单选器在嵌入式视图中。

此时,当我在3组件选择器中释放3个组件中的任何一个的选择器时,函数被调用,它正确地进行了一组计算,基于选择器委托选择和2个结果该功能的相应更改屏幕底部的两个标签。 (即 1 个函数传递 2 个结果。)

我想做的是触发相同的函数调用,当嵌入式视图中的任何选择器被释放并将这两个结果传递给主 ViewController 中适当的两个标签时。

这是主视图控制器中名为 "SecondViewController" 的有效代码(我省略了数据数组,以保存 space):

    @IBOutlet weak var spendingPickerView: UIPickerView!
    @IBOutlet weak var taxRateLbl: UILabel!
    @IBOutlet weak var taxPaidLbl: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        spendingPickerView.delegate = self
        spendingPickerView.dataSource = self
        spendingPickerView.selectRow(5, inComponent: 2, animated: true)

    }
}

extension SecondViewController: UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 3
        }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 10
    }

    /*func pickerView(_ spendingPickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
        return NSAttributedString(string: digits[row], attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
    }*/
}

extension SecondViewController: UIPickerViewDelegate {

    /*func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView.tag == 2{
        return adults[row]
        }
        if pickerView.tag == 3{
        return minors[row]
        }
        if pickerView.tag == 4{
        return region[row]
        }
        return ""
     }*/

    func pickerView(_ spendingPickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        // let view = UIView(frame: CGRect(x: 0, y: 0, width: 82, height: 28))

        let digitsLbl = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 107))
        digitsLbl.text = digits[row]
        digitsLbl.textColor = .white
        digitsLbl.font = UIFont.boldSystemFont(ofSize: 24)

        return digitsLbl

    }

    func pickerView(_ spendingPickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        spendingPickerView.reloadComponent(1)

        // Calculation functions for output.
        taxPaidLbl.text = "$\(rateAndTaxCalc(retailSpend: retailDollar, adultIH: adultInHome, minorIH: minorInHhome, regionOfUSA: regionOfUSA).1)"

        taxRateLbl.text = "\(rateAndTaxCalc(retailSpend: retailDollar, adultIH: adultInHome, minorIH: minorInHhome, regionOfUSA: regionOfUSA).2)%"

    }

这是标题为 SecondPickerViewController 的嵌入式视图中的代码,我试图从中将数据传递给 "SecondViewController":

    @IBOutlet weak var adultPickerView: UIPickerView!
    @IBOutlet weak var minorPickerView: UIPickerView!
    @IBOutlet weak var regionPickerView: UIPickerView!


    override func viewDidLoad() {
        super.viewDidLoad()

        adultPickerView.delegate = self
        adultPickerView.dataSource = self
        minorPickerView.delegate = self
        minorPickerView.dataSource = self
        regionPickerView.delegate = self
        regionPickerView.dataSource = self

    }
}

extension SecondPickerViewController: UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return 1
        }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

        var wheel = 0
        if pickerView.tag == 2 {
            wheel = 4 }
        else if pickerView.tag == 3 {
            wheel = 11 }
        else if pickerView.tag == 4 {
            wheel = 3 }
        return wheel
    }
}

extension SecondPickerViewController: UIPickerViewDelegate {
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

        let returnLbl = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 107))

        if pickerView.tag == 2 {
            returnLbl.text = adults[row]
            returnLbl.textColor = .white
            returnLbl.textAlignment = .center
            returnLbl.font = UIFont.boldSystemFont(ofSize: 24)
        }
        else if pickerView.tag == 3 {
            returnLbl.text = minors[row]
            returnLbl.textColor = .white
            returnLbl.textAlignment = .center

            returnLbl.font = UIFont.boldSystemFont(ofSize: 24)
        }
            else if pickerView.tag == 4 {
            returnLbl.text = region[row]
            returnLbl.textColor = .white
            returnLbl.textAlignment = .center

            returnLbl.font = UIFont.boldSystemFont(ofSize: 24)
        }

        // This output needs to be sent to "SecondViewController". /////////////////
        // This output needs to be sent to "SecondViewController". /////////////////
        // This output needs to be sent to "SecondViewController". /////////////////
        taxPaidTmp = "$\(rateAndTaxCalc(retailSpend: retailDollar, adultIH: adultInHome, minorIH: minorInHhome, regionOfUSA: regionOfUSA).1)"

        taxRateTmp = "\(rateAndTaxCalc(retailSpend: retailDollar, adultIH: adultInHome, minorIH: minorInHhome, regionOfUSA: regionOfUSA).2)%"

        return returnLbl
    }
}

被调用的函数 (rateAndTaxCalc) 在一个单独的文件中,当从 "SecondViewController".

调用时它输出正确的数据

两个视图中的所有 6 个选择器组件似乎都运行正常。我只需要获取嵌入式视图上 3 个选择器的两个函数调用的输出,传递给 "SecondViewController",更改 "taxRateLbl" 和 "taxPaidLbl".

完全披露:我是 Swift(2 周)的新手,但我一生中使用过 19 种其他编程语言,一直回到 FortranIV、COBOL 和 8080 机器语言。因此,尽管我是 Swift 的新手,但学习一门新的编程语言对我来说通常并不比机械师学习一把不同形状的钳子更难。我想我想说的是,如果我有时间的话,我可能可以自己解决这个问题,但时间是个问题。我正努力在恢复正常生活之前完成这项工作 post-virus。所以任何帮助将不胜感激。谢谢。

如果我没理解错的话,你想要实现的是调用系统为大选择器视图调用的相同函数,但为小选择器调用。

对于这种情况,您可以通过多种方式完成此操作,我将在下面列出一些:

直截了当的方式(坏方式)

因为你知道 FirstViewControllerSecondViewController 的父级,你可以简单地在第二个视图控制器中编写 (parent as? FirstViewController)?.myMethod() ,这会将方法传递给你的第一个视图控制器.

但是,您几乎不应该编写这样的代码,因为它假设 SecondaryViewController 始终是 FirstViewController 的子控制器并且做出这样的假设并进行固定类型转换将大大减少代码的灵活性。(无论使用哪种语言,如果您编码时间长,您应该已经知道这一点)

正道

在 Swift 或 iOS 系统中传递任何事件的正确方法是通过以下两种方法之一:委托或通知。不同之处在于,虽然委托可以一对一地传递事件,但使用通知可以让您将事件广播到多个对象。根据此处的用例,我们将坚持使用委托方法,因为一对一正是您所需要的。

为此,您首先要声明一个协议,例如 SecondViewControllerDelegate 并添加一个方法,例如 secondViewController(_ viewController: SecondViewController, pickerDidChange: UIPickerView)。您可以看到此委托方法的样式与 iOS API 中找到的样式相匹配,其中第一个参数始终是发送调用的对象。然后,您将在 SecondViewController 中添加一个名为 delegate 的 属性 并使其类型为 SecondViewControllerDelegate?,然后使 FirstViewController 符合此委托并设置本身作为第二个视图控制器的委托。

在代码中:

// the `class` here means only classes(not structs or enums) can conform to it
// so we later have a weak reference to it
protocol SecondViewControllerDelegate: class {
    func secondViewController(_ viewController: SecondViewController, pickerDidChange: UIPickerView)
}


class FirstViewController: UIViewController, SecondViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        mySecondViewController.delegate = self
    }

    func secondViewController(_ viewController: SecondViewController, pickerDidChange: UIPickerView) {
        // Here SecondViewController tell me about the change
    }
}

class SecondViewController: UIViewController {

    // use weak to avoid reference cycle and memory leaks
    weak var delegate: SecondViewControllerDelegate?

    func pickerChangedSomehow() {
        // when your picker changes, call your delegate
        delegate?.secondViewController(self, pickerDidChange: myPicker)
    }
}

奖金:聪明的方法

由于在第一个视图控制器中执行 UIPickerDelegate 事件调用的功能,第二个视图控制器也是如此,这里聪明的方法是让你的第一个视图控制器成为所有四个选择器视图的委托(如果你不知道,一个对象可以是许多其他对象的委托,这就是委托的发送者作为第一个参数传递的原因),所以无论哪个选择器视图发生变化,你总是会得到在您的第一个视图控制器中委托调用,使一切变得更加容易。