【问题标题】:Using protocol with generic data type to pass data between screens使用具有通用数据类型的协议在屏幕之间传递数据
【发布时间】:2021-11-28 08:32:57
【问题描述】:

我是一名开始学习 iOS 的 Android 开发人员。我正在尝试在主从样式应用程序之间传递数据。 我得到了controller1,它有一个ToDo 项目列表,controller2 允许创建一个新的ToDo 项目并将其添加到controller1 的列表中。

我已经创建了一个协议:

protocol ListDataHolder {
    
    associatedtype T
    
    func addItem(item: T)
    
    func reloadData()
}

preparecontroller1 中分配了self

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

controller2 中声明的委托

// how do I tell to use ToDo class for generic type here
var toDoDataHolder: ListDataHolder? = nil

并像这样使用它:

@IBAction func onAddClicked(_ sender: Any) {
        let toDo = ToDo()
        ...
        toDoDataHolder?.addItem(item: toDo)
        toDoDataHolder?.reloadData()
        navigationController?.popViewController(animated: true)
    }

我在走这条路时遇到了一些错误:

委托声明:

Protocol 'ListDataHolder' can only be used as a generic constraint because it has Self or associated type requirements

使用addItem()时:

Cannot convert value of type 'ToDo' to expected argument type 'ListDataHolder.T'
Insert ' as! ListDataHolder.T'
Member 'addItem' cannot be used on value of protocol type 'ListDataHolder'; use a generic constraint instead

当我从协议中删除泛型并只拥有addItem(item: ToDo) 时,一切正常。但我希望能够将ListDataHolder 用于任何数据类型。

这对我来说只是实验,我不是在寻找在控制器之间传递数据的正确方法。

编辑:您可以在此 GitHub 存储库中找到完整代码:github.com/Sermilion/ios_learning

【问题讨论】:

  • 你能通过 GitHub 分享一个小的演示项目吗?我对您的描述有点困惑,无法自行复制
  • @Igor 嘿,Igor,我创建了一个公共仓库并将代码推送到那里:github.com/Sermilion/ios_learning

标签: ios swift uiviewcontroller protocols


【解决方案1】:

您需要做的是使用协议使第二个视图控制器通用限制符合ListDataHolder的类正在使用(或持有)的对象类型

这可以在视图控制器的声明中完成

class SecondViewController<Holder: ListDataHolder>: UIViewController where Holder.T == ToDo

那么您的方法onAddClicked 将按原样工作。

【讨论】:

  • 感谢您的回答。我得到与@Sandeep Bhandari Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[&lt;UIViewController 0x7f85fdc05550&gt; setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key nameTextField.' terminating with uncaught exception of type NSException 的答案相同的错误
  • 它必须对它做点什么。当我不在ListDataHolder 中使用泛型时,一切正常。我在上面的问题中有一个指向 git 中的 repo 的链接,如果你愿意,你可以测试它。
  • @joakim-danielson:虽然异常描述不恰当,但实际问题是故事板和带有泛型的视图控制器。如果您从SecondViewController 中删除泛型&lt;Holder: ListDataHolder&gt;,它将正常工作。这也是我在回答中提到的。故事板不是为使用泛型而设计的,它试图实例化视图控制器而不解析泛型因此崩溃
  • @Sermilion 我发现this question 可能对通用视图控制器有所帮助
  • @JoakimDanielson 我认为可以公平地说您的回答确实回答了给定的问题。泛型和 ViewController 的问题在这里不相关。我会接受你的回答。 Sandeep Bhandari 也感谢您的意见。这对我来说是一次很棒的练习,我学到了很多东西。
【解决方案2】:

如果您总是将ToDo 实例传递给addItem 方法,则不要在ListDataHolder 协议中使用关联类型,而是使用ToDo

如果您希望此协议 addItem 方法适用于任何 genericType,请使用以下代码。

protocol ListDataHolder {
    func addItem<T:Decodable>(item: T)
    func reloadData()
}

struct ToDo: Decodable {

}

class A: ListDataHolder {
   
    func addItem<T:Decodable>(item: T) {
          print("Add Item called")
    }
    
    func reloadData(){

    }
}

这里你需要实现委托设计模式。 请详细查看委托设计模式。

【讨论】:

  • 嘿,谢谢。我将阅读有关委托设计模式的内容。感谢您的提示。
猜你喜欢
  • 2020-10-10
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 2021-03-01
  • 1970-01-01
  • 2016-12-07
  • 1970-01-01
  • 2018-01-10
相关资源
最近更新 更多