Protocol 'GetSignQuestions' 只能用作泛型约束,因为它有 Self 或关联类型要求
Protocol 'GetSignQuestions' can only be used as a generic constraint because it has Self or associated type requirements
我有一个协议和两个不同的扩展。当打开 PoliceSignalsView 时,当我用 PoliceSignQuestion() 更改 SignQuestionProvider 中的共享变量时,它 returns 与警察相关的问题。如果我用 TrafficSignQuestion() 替换它,它会 return 我提出与交通相关的问题。这工作正常。但是,有一个问题。
模型 1:
struct TrafficSignQuestion: Codable, Hashable {
var trafficQuestions: [TrafficSign]?
}
enum TrafficSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
}
struct TrafficSign: Codable, Hashable {
var id: Int?
var image: String?
var sections: [TrafficSignSectionType.RawValue : String]?
var correct: String?
}
模型 2:
struct PoliceSignQuestion: Codable, Hashable {
var policeQuestions: [PoliceSign]?
}
enum PoliceSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
}
struct PoliceSign: Codable, Hashable {
var id: Int?
var image: String?
var sections: [PoliceSignSectionType.RawValue : String]?
var correct: String?
}
协议:
protocol GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [Self]?
}
**SignQuestionProvider**
class SignQuestionProvider<T: Any>: ObservableObject {
var shared: GetSignQuestions?
@Published var allQuestions: T?
func getQuestions() {
let questions = shared?.getQuestions(jsonService: JSONService())
allQuestions = questions as? T
print("\(allQuestions)")
}
}
分机:
extension PoliceSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign]? {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
extension TrafficSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign]? {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
JSON 服务:
class JSONService: ObservableObject {
func getQuestion<T: Codable>(fileName: String, using modelType: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(modelType, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
}
PoliceSignalsView(FirstView):
如何将从此处获取的数据传输到 QuestionCardView()?我无法构建通用绑定。
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSignQuestion>()
var body: some View {
VStack {
QuestionCardView()
}
.onAppear {
questionProvider.shared = PoliceSignQuestion()
// When I change it to "TrafficSignQuestion", the TrafficSign function in the protocol works and returns TrafficSigns questions. I want to pass the result returned from here to QuestionCardView. How can I do that ?
questionProvider.getQuestions()
}
.environmentObject(questionProvider)
}
}
第二个视图(问题卡视图):
我不想在此处显示来自 policeSignalsView 的数据时看到 policeQuestions 属性,因为它可能位于此页面的 trafficQuestions 中。如果我这样写,我会得到一个错误。我该如何解决这个问题?
struct QuestionCardView: View {
@EnvironmentObject var optionConfigure: OptionConfigure
@EnvironmentObject var questionProvider: SignQuestionProvider<PoliceSignQuestion>
var body: some View {
VStack {
ForEach(questionProvider.allQuestions?.policeQuestions?.indices ?? 0..<0, id: \.self) { item in
// here.. I don't want the "policeQuestions" property here. Because it should be a generic construct.
Text("")
.animation(.spring())
}
}
}
}
如我所说,我不希望 QuestionCardView 屏幕上 ForEach 中的 policeQuestions 属性。我是按照这个方法去掉的
协议:
protocol GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [Self] // Array
}
分机:
extension PoliceSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign] {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
extension TrafficSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign] {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
警察信号视图:
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSign>()
var body: some View {
VStack {
QuestionCardView()
}
.onAppear {
questionProvider.shared = PoliceSign()
questionProvider.getQuestions()
}
.environmentObject(questionProvider)
}
}
当我以这种方式进行更改时,出现如图所示的错误。
我重新组织了一些代码并重新添加了我之前给您的回答中的 Question
协议。我还稍微编辑了您的模型,使 TrafficSign
和 PoliceSign
具有非可选的 id
属性。否则,在 ForEach
中使用它们会出问题。
struct TrafficSignQuestion: Codable, Hashable {
var trafficQuestions: [TrafficSign]?
}
enum TrafficSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
}
struct TrafficSign: Codable, Hashable, Question {
var id: Int
var image: String?
var sections: [TrafficSignSectionType.RawValue : String]?
var correct: String?
}
struct PoliceSignQuestion: Codable, Hashable {
var policeQuestions: [PoliceSign]?
}
enum PoliceSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
}
struct PoliceSign: Codable, Hashable, Question {
var id: Int
var image: String?
var sections: [PoliceSignSectionType.RawValue : String]?
var correct: String?
}
protocol Question {
var id: Int { get set }
var image: String? { get set }
}
protocol GetSignQuestions {
associatedtype QuestionType : Question
func getQuestions(jsonService: JSONService) -> [QuestionType]?
}
class SignQuestionProvider<T: GetSignQuestions>: ObservableObject {
@Published var allQuestions : [T.QuestionType] = []
func getQuestions(getter: T) {
if let questions = getter.getQuestions(jsonService: JSONService()) {
self.allQuestions = questions
}
print("\(allQuestions)")
}
}
struct PoliceSignGetter : GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign]? {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
struct TrafficSignGetter: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign]? {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
class JSONService: ObservableObject {
func getQuestion<T: Codable>(fileName: String, using modelType: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(modelType, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
}
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSignGetter>()
var body: some View {
VStack {
QuestionCardView<PoliceSign>(questions: questionProvider.allQuestions)
}
.onAppear {
questionProvider.getQuestions(getter: PoliceSignGetter())
}
}
}
struct QuestionCardView<T: Question>: View {
var questions: [Question]
var body: some View {
VStack {
ForEach(questions, id: \.id) { item in
Text("")
.animation(.spring())
}
}
}
}
我有一个协议和两个不同的扩展。当打开 PoliceSignalsView 时,当我用 PoliceSignQuestion() 更改 SignQuestionProvider 中的共享变量时,它 returns 与警察相关的问题。如果我用 TrafficSignQuestion() 替换它,它会 return 我提出与交通相关的问题。这工作正常。但是,有一个问题。
模型 1:
struct TrafficSignQuestion: Codable, Hashable {
var trafficQuestions: [TrafficSign]?
}
enum TrafficSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
}
struct TrafficSign: Codable, Hashable {
var id: Int?
var image: String?
var sections: [TrafficSignSectionType.RawValue : String]?
var correct: String?
}
模型 2:
struct PoliceSignQuestion: Codable, Hashable {
var policeQuestions: [PoliceSign]?
}
enum PoliceSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
}
struct PoliceSign: Codable, Hashable {
var id: Int?
var image: String?
var sections: [PoliceSignSectionType.RawValue : String]?
var correct: String?
}
协议:
protocol GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [Self]?
}
**SignQuestionProvider**
class SignQuestionProvider<T: Any>: ObservableObject {
var shared: GetSignQuestions?
@Published var allQuestions: T?
func getQuestions() {
let questions = shared?.getQuestions(jsonService: JSONService())
allQuestions = questions as? T
print("\(allQuestions)")
}
}
分机:
extension PoliceSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign]? {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
extension TrafficSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign]? {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
JSON 服务:
class JSONService: ObservableObject {
func getQuestion<T: Codable>(fileName: String, using modelType: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(modelType, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
}
PoliceSignalsView(FirstView):
如何将从此处获取的数据传输到 QuestionCardView()?我无法构建通用绑定。
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSignQuestion>()
var body: some View {
VStack {
QuestionCardView()
}
.onAppear {
questionProvider.shared = PoliceSignQuestion()
// When I change it to "TrafficSignQuestion", the TrafficSign function in the protocol works and returns TrafficSigns questions. I want to pass the result returned from here to QuestionCardView. How can I do that ?
questionProvider.getQuestions()
}
.environmentObject(questionProvider)
}
}
第二个视图(问题卡视图):
我不想在此处显示来自 policeSignalsView 的数据时看到 policeQuestions 属性,因为它可能位于此页面的 trafficQuestions 中。如果我这样写,我会得到一个错误。我该如何解决这个问题?
struct QuestionCardView: View {
@EnvironmentObject var optionConfigure: OptionConfigure
@EnvironmentObject var questionProvider: SignQuestionProvider<PoliceSignQuestion>
var body: some View {
VStack {
ForEach(questionProvider.allQuestions?.policeQuestions?.indices ?? 0..<0, id: \.self) { item in
// here.. I don't want the "policeQuestions" property here. Because it should be a generic construct.
Text("")
.animation(.spring())
}
}
}
}
如我所说,我不希望 QuestionCardView 屏幕上 ForEach 中的 policeQuestions 属性。我是按照这个方法去掉的
协议:
protocol GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [Self] // Array
}
分机:
extension PoliceSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign] {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
extension TrafficSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign] {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
警察信号视图:
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSign>()
var body: some View {
VStack {
QuestionCardView()
}
.onAppear {
questionProvider.shared = PoliceSign()
questionProvider.getQuestions()
}
.environmentObject(questionProvider)
}
}
当我以这种方式进行更改时,出现如图所示的错误。
我重新组织了一些代码并重新添加了我之前给您的回答中的 Question
协议。我还稍微编辑了您的模型,使 TrafficSign
和 PoliceSign
具有非可选的 id
属性。否则,在 ForEach
中使用它们会出问题。
struct TrafficSignQuestion: Codable, Hashable {
var trafficQuestions: [TrafficSign]?
}
enum TrafficSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
}
struct TrafficSign: Codable, Hashable, Question {
var id: Int
var image: String?
var sections: [TrafficSignSectionType.RawValue : String]?
var correct: String?
}
struct PoliceSignQuestion: Codable, Hashable {
var policeQuestions: [PoliceSign]?
}
enum PoliceSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
}
struct PoliceSign: Codable, Hashable, Question {
var id: Int
var image: String?
var sections: [PoliceSignSectionType.RawValue : String]?
var correct: String?
}
protocol Question {
var id: Int { get set }
var image: String? { get set }
}
protocol GetSignQuestions {
associatedtype QuestionType : Question
func getQuestions(jsonService: JSONService) -> [QuestionType]?
}
class SignQuestionProvider<T: GetSignQuestions>: ObservableObject {
@Published var allQuestions : [T.QuestionType] = []
func getQuestions(getter: T) {
if let questions = getter.getQuestions(jsonService: JSONService()) {
self.allQuestions = questions
}
print("\(allQuestions)")
}
}
struct PoliceSignGetter : GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign]? {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
struct TrafficSignGetter: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign]? {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
class JSONService: ObservableObject {
func getQuestion<T: Codable>(fileName: String, using modelType: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(modelType, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
}
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSignGetter>()
var body: some View {
VStack {
QuestionCardView<PoliceSign>(questions: questionProvider.allQuestions)
}
.onAppear {
questionProvider.getQuestions(getter: PoliceSignGetter())
}
}
}
struct QuestionCardView<T: Question>: View {
var questions: [Question]
var body: some View {
VStack {
ForEach(questions, id: \.id) { item in
Text("")
.animation(.spring())
}
}
}
}