Swift 5:
[我试图将在嵌入式View中调用的函数的结果传递给父ViewController,以便在父ViewController的底部更改标签。
除了描述性文字,我的主要ViewController包含四个元素。
换句话说,在屏幕上可见6个选择器组件(1个选择器具有3个组件和3个1组件选择器)。但是3个单个选择器处于嵌入式视图中。
这时,当我释放3组件选择器中3个组件中任何一个的选择器时,将调用该函数,它会根据选择器委托选择和该函数的2个结果正确地进行一组计算。相应地更改屏幕底部的两个标签。 (即1个函数传递2个结果。)
[我想做的是,当嵌入式视图中的任何选择器被释放并将这两个结果传递到主ViewController中相应的两个标签时,触发相同的函数调用。
这是主View Controller中标题为“ SecondViewController”的代码,该代码可以正常工作(我省去了数据数组,以节省空间):
@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的新手,但对我来说,选择一种新的编程语言通常比机械工挑选另一把异形钳子要困难得多。我想我要说的是,如果我有时间,我可能可以自己解决这个问题,但是时间是个问题。我正努力在病毒恢复正常生活之前做到这一点。因此,任何帮助将不胜感激。谢谢。
直截了当的方式(不好的方式)>
由于您知道FirstViewController
是SecondViewController
的父代,因此您只需在第二个视图控制器中写入(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
事件调用上执行功能,第二个视图控制器也在执行此功能,所以这里的聪明方法是使第一个视图控制器成为所有四个选择器视图的委托(如果没有,知道一个对象可以是许多其他对象的委托,这就是为什么将委托的发送者作为第一个参数传递的原因),因此,无论更改了哪个选择器视图,您始终都会在第一个中得到委托调用视图控制器,使一切变得更加容易。