Swift 4.2:无法让简单的委托协议工作

Posted

技术标签:

【中文标题】Swift 4.2:无法让简单的委托协议工作【英文标题】:Swift 4.2: Can't get simple delegate protocol working 【发布时间】:2019-01-29 12:41:21 【问题描述】:

YouTube Screenrecord of my workflow

FirstViewController

import UIKit

class FirstViewController: UIViewController 
    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() 
        super.viewDidLoad()

        let secondViewController = SecondViewController()
        secondViewController.delegate = self
    


extension FirstViewController: DataEnteredDelegate 
    func userDidEnterInformation(info: String) 
        label.text = info
        print("label changed")
    

SecondViewController

import UIKit

protocol DataEnteredDelegate: class 
    func userDidEnterInformation(info: String)


class SecondViewController: UIViewController 
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(_ sender: UIButton) 
        print("button tapped")
        delegate?.userDidEnterInformation(info: textField.text!)
    

【问题讨论】:

你还没有实现协议方法 先删除as! DataEnteredDelegate 你把第二个视图控制器推到哪里了? ***.com/a/37203430/6630644 Passing Data between View Controllers***.com/questions/31075116/…的可能重复 【参考方案1】:

除了缺少的委托收养

class FirstViewController: UIViewController, DataEnteredDelegate 

如果视图控制器是在情节提要中设计的,它无法工作。

线

let secondViewController = SecondViewController()

创建一个全新的SecondViewController 实例,它不是故事板中的实例,并且所有出口都连接。

您必须使用segue 或者您必须使用instantiate 并呈现第二个视图控制器以获得对目标控制器的真正引用。

注意:

在控制器之间共享数据的 Swift 协议/委托方面非常objective-c-ish。 回调闭包更轻量级并且更快捷

【讨论】:

【参考方案2】:

将您的协议实现到您的第一个视图控制器。好的做法是扩展控制器,然后将协议的委托方法放到这个扩展中

extension FirstViewController: DataEnteredDelegate 
    func userDidEnterInformation(info: String) 
        ...
    

接下来,您设置其委托的第二个视图控制器可能不是您稍后要呈现的控制器。要正确设置委托,请设置将要呈现的控制器的委托。如果您正在使用 segue,请覆盖 prepare(for:sender:) 并设置向下转换的 segue 目的地的委托。如果您手动呈现视图控制器或正在推送它,请设置您正在呈现/推送的此控制器的委托。

【讨论】:

非常感谢!我已将代码 sn-p 移至扩展程序并修复了其他错误。我不推送或继续(参见更新后的视频),我只想从第二个手动更新第一个 viewController。 @AmandaGarcia 更仔细地阅读了答案。我描述了为什么你的代码不起作用【参考方案3】:

您必须将第一个视图控制器更改为

class FirstViewController: UIViewController, DataEnteredDelegate 
...

原因是第一个视图控制器没有实现协议,所以不会发生强制转换

【讨论】:

谢谢!!我错过了,已修复,但仍然无法正常工作(请参阅更新后的帖子)【参考方案4】:

FirstView 控制器使用下面的代码

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate 

    @IBOutlet weak var label: UILabel!

    func userDidEnterInformation(info: String) 
        label.text = info
    

    override func viewDidLoad() 
        super.viewDidLoad()

        let secondViewController = SecondViewController()
        secondViewController.delegate = self
    

    func userDidEnterInformation(info: String)
      // do your stuff here
    


【讨论】:

非常感谢!我看到我最初错过了作为超类的 DataEnteredDelegate。我修复了它,但它似乎不起作用(请参阅更新后的帖子)【参考方案5】:

检查并更新。

class FirstViewController: UIViewController, DataEnteredDelegate 

    @IBOutlet weak var label: UILabel!

    func userDidEnterInformation(info: String) 
        label.text = info
    

    override func viewDidLoad() 
        super.viewDidLoad()

        let secondViewController = SecondViewController()
        secondViewController.delegate = self 
    

【讨论】:

非常感谢!我看到我最初错过了作为超类的 DataEnteredDelegate。我修复了它,但它似乎不起作用(请参阅更新后的帖子)【参考方案6】:

我相信您正面临这个问题,因为您将委托设置为 nil。

尝试这样设置:

    weak var delegate: DataEnteredDelegate? = FirstViewController.self as! DataEnteredDelegate

【讨论】:

【参考方案7】:

这里不接受SecondViewController的直接初始化。另外,在这两个步骤之后,secondViewController 将被释放并且不存在。

   override func viewDidLoad() 
    super.viewDidLoad()

    let secondViewController = SecondViewController() // Not the right way to initialize!
    secondViewController.delegate = self. //secondViewController  will be released!
    

【讨论】:

【参考方案8】:

问题是,您正在创建一个新的SecondViewController,而不是标签栏使用的实际SecondViewController。您需要访问 UITabBarController 正在使用的第二个视图控制器,而不是分配一个新实例,以便将您的 viewDidLoad 实现更改为:

override func viewDidLoad() 
    super.viewDidLoad()
    let secondViewController      = self.tabBarController?.viewControllers?.last as? SecondViewController
    secondViewController.delegate = self

【讨论】:

【参考方案9】:

您的 UIWndow 的 rootViewController 是 UITabBarController,它在您分享的 YouTube 视频链接上可见,因此您需要使用此代码 sn-p 从该位置获取 SecoundViewController

class FirstViewController: UIViewController 

@IBOutlet weak var label: UILabel!

override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let rootController = appDelegate.window?.rootViewController as! UITabBarController
    if let viewControllers = rootController.viewControllers 
        if viewControllers.count > 0 
            if let secondViewController = viewControllers[1] as? SecondViewController 
                secondViewController.delegate = self
            
        
    

这样您的委托就不会为零。

在您当前的解决方案中,您的 SecondViewController 获取新实例但未从情节提要中加载,并且您在委托中获得了 nil,因此委托方法无法调用。

// you have nil refrence to delegate check it here
if (self.delegate != nil) 
    self.delegate?.userDidEnterInformation(info: textField.text ?? "")

希望有帮助。

【讨论】:

以上是关于Swift 4.2:无法让简单的委托协议工作的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 委托协议结构

在 Swift 上委托 Objective-C 协议

Swift学习第八枪--协议

将键盘选择为中文(简体)时不调用 UITextView 委托 shouldChangeTextInRange - Swift 4.2 中的手写

委托和协议无法正常工作

如何使用代码连接数据源和委托 - iOS Swift