Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

154
Views
Uso de una matriz de protocolos con ForEach y sintaxis enlazable

Tengo una matriz de protocolo @Published que estoy recorriendo con un ForEach para presentar los elementos en alguna vista. Me gustaría poder usar la sintaxis enlazable de SwiftUI con ForEach para generar un enlace para mí, de modo que pueda mutar cada elemento y reflejarlo en la matriz original.

Esto parece funcionar para las propiedades que se implementan en el protocolo, pero no estoy seguro de cómo accedería a las propiedades que son exclusivas del tipo de conformidad del protocolo. En el código de ejemplo a continuación, sería la propiedad de owner del Animal o la propiedad de age del Human . Pensé que podría ser necesario algún tipo de conversión de tipo, pero no puedo entender cómo retener la referencia a la matriz subyacente a través del enlace.

Avísame si necesitas más detalles.

 import SwiftUI protocol Testable { var id: UUID { get } var name: String { get set } } struct Human: Testable { let id: UUID var name: String var age: Int } struct Animal: Testable { let id: UUID var name: String var owner: String } class ContentViewModel: ObservableObject { @Published var animalsAndHumans: [Testable] = [] } struct ContentView: View { @StateObject var vm: ContentViewModel = ContentViewModel() var body: some View { VStack { ForEach($vm.animalsAndHumans, id: \AnyTestable.id) { $object in TextField("textfield", text: $object.name) // if the object is an Animal, how can I get it's owner? } Button("Add animal") { vm.animalsAndHumans.append(Animal(id: UUID(), name: "Mick", owner: "harry")) } Button("Add Human") { vm.animalsAndHumans.append(Human(id: UUID(), name: "Ash", age: 26)) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Este es un problema espinoso con sus tipos de datos.

Si puede cambiar sus tipos de datos, puede hacer que esto sea más fácil de resolver.

Por ejemplo, tal vez pueda modelar sus datos de esta manera, usando una enum en lugar de un protocol para representar las variantes:

 struct Testable { let id: UUID var name: String var variant: Variant enum Variant { case animal(Animal) case human(Human) } struct Animal { var owner: String } struct Human { var age: Int } }

También ayudará agregar descriptores de acceso para los datos asociados de las dos variantes:

 extension Testable { var animal: Animal? { get { guard case .animal(let animal) = variant else { return nil } return animal } set { guard let newValue = newValue, case .animal(_) = variant else { return } variant = .animal(newValue) } } var human: Human? { get { guard case .human(let human) = variant else { return nil } return human } set { guard let newValue = newValue, case .human(_) = variant else { return } variant = .human(newValue) } } }

Entonces puedes escribir tu vista así:

 class ContentViewModel: ObservableObject { @Published var testables: [Testable] = [] } struct ContentView: View { @StateObject var vm: ContentViewModel = ContentViewModel() var body: some View { VStack { List { ForEach($vm.testables, id: \.id) { $testable in VStack { TextField("Name", text: $testable.name) if let human = Binding($testable.human) { Stepper("Age: \(human.wrappedValue.age)", value: human.age) } else if let animal = Binding($testable.animal) { HStack { Text("Owner: ") TextField("Owner", text: animal.owner) } } } } } HStack { Button("Add animal") { vm.testables.append(Testable( id: UUID(), name: "Mick", variant: .animal(.init(owner: "harry")) )) } Button("Add Human") { vm.testables.append(Testable( id: UUID(), name: "Ash", variant: .human(.init(age: 26)) )) } } } } }
over 4 years ago · Santiago Trujillo Report

0

Una forma simple de resolver es extender su protocolo Testable . algo le gusta

 protocol Testable { var id: UUID { get } var name: String { get set } var extra: String? { get } } struct Human: Testable { let id: UUID var name: String var age: Int var extra: String? { nil } } struct Animal: Testable { let id: UUID var name: String var owner: String var extra: String? { return owner } }

Su bloque ForEach no necesita saber el tipo concreto: Animal o Human , solo verifique el extra de Testable para decidir agregar un nuevo elemento o no.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!