SwiftUI: Wie kann ich die Pfeile entfernen?

1 Antwort

Die ZStack-View ordnet ihre Kindelemente auf der z-Achse an, sodass sie sich gemäß ihrer Reihenfolge gegenseitig überdecken. Das bedeutet also, dass sich alle diese Kindelemente auch denselben Platz teilen.

Die View, die der Link üblicherweise anzeigen würde (Text mit Pfeil) wird gegen eine leere View (EmptyView) ersetzt und zu guter Letzt wird noch der Buttonstyle definiert, um die übliche blaue Umrandung zu entfernen.

In dem Snippet geht man zudem davon aus, eine Liste mit Views gleichen Typs vorliegen zu haben. Das könnte beispielsweise eine Wörterliste sein.

List {
  ZStack {
    Text("First word")
    // NavigationLink ...
  }
  ZStack {
    Text("Second word")
    // NavigationLink ...
  }
  /* etc. ... */
}

Das heißt, die Lösung lässt sich abstrahieren. Man braucht nur ein String-Array, welches alle Wörter auflistet. Über dieses iteriert man mit einer Schleife. Je Lauf wird ein Listenelement angelegt und der aktuelle String eingesetzt.

let words: [String] = [ "First word", "Second word" ]

var body: some View {
  List {
    ForEach(words) { word in
      ZStack {
        Text(word)
        // NavigationLink ...
      }
    }
  }
}

Statt dich auf ein String-Array zu beschränken, könntest du allerdings genauso gut ein Array anderen Typs anlegen und über dieses iterieren. Voraussetzung ist allerdings, dass der Typ das Identifiable-Protokoll erfüllt.

In deinem Fall wäre es z.B. sinnvoll, einen Typ zu kreieren, der sich Linkziel und Bildpfad merkt. Das könnte ungefähr so aussehen:

struct ImageLink: Identifiable {
  var id = UUID().uuidString
  var sourcePath: String
  var destinationView: View
}

struct YourView: View {
  var imageLinks = [
    ImageLink(sourcePath: "path/to/image1", destinationView: SomeView()),
    ImageLink(sourcePath: "path/to/image2", destinationView: OtherView())
  ]

  var body: some View {
    List {
      ForEach(imageLinks) { imageLink in
        ZStack {
          Image(imageLink.sourcePath)
          NavigationLink(destination: imageLink.destinationView) {
            EmptyView()
          }.buttonStyle(PlainButtonStyle())
        }
      }
    }
  }
}

PsySkill 
Fragesteller
 15.09.2022, 20:05

Wenn ich deine Lösung nutze bekomme ich nur "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions". Außerdem möchte Xcode ein any bei "ImageLink" vor View.

0
regex9  16.09.2022, 04:04
@PsySkill

Der Typ im destinationView-Property des struct ist falsch. Richtig wäre ein generischer Type Constraint:

struct ImageLink<SomeView: View>: Identifiable {
  /* ... */
  var destinationView: SomeView
}

oder alternativ könnte man auch den Typ AnyView (aus der Standardbibliothek) nutzen:

struct ImageLink: Identifiable {
  /* ... */
  var destinationView: AnyView
}

Beim Setzen des Properties müsste die jeweilige View aber auch in ein AnyView-Objekt gewrapped werden.

 var imageLinks = [
  ImageLink(sourcePath: "path/to/image1", destinationView: AnyView(SomeView())),
  ImageLink(sourcePath: "path/to/image2", destinationView: AnyView(OtherView()))
]

Dieser Weg aber nur der Vollständigkeit halber. Die erste Lösung sollte bevorzugt werden, da dabei die Typinformationen zum jeweiligen nicht durch ein Wrapping verdeckt werden.

0
regex9  16.09.2022, 05:25
@regex9

Damit es mit dem Array klappt, sind ebenso noch Änderungen notwendig.

Zunächst wird das struct noch einmal angepasst und ein Basistyp erstellt.

protocol BaseImageLink {
  var id: UUID { get }
  var sourcePath: String { get }
  var destinationView: View { get }
}

struct ImageLink<SomeView: View>: BaseImageLink {
  private var _destinationView: SomeView
  private var _sourcePath: String

  var id: UUID {
    get {
      return UUID()
    }
  }

  var sourcePath: String {
    get {
      return _sourcePath
    }
  }

  var destinationView: SomeView {
    get {
      return _destinationView
    }
  }

  init(sourcePath: String, destinationView: SomeView) {
    _sourcePath = sourcePath
    _destinationView = destinationView
  }
}

Das Array bekommt den Basistyp als Typangabe:

var imageLinks: [BaseImageLink] = [
  ImageLink(sourcePath: "path/to/image1", destinationView: SomeView()),
  ImageLink(sourcePath: "path/to/image2", destinationView: OtherView())
]

Und da keine explizite Vererbung des Identifiable-Protokolls mehr stattfindet, wird der Schleifenkopf noch geändert.

ForEach(imageLinks, id: \.id) { imageLink in

Dieser Code ist soweit für deinen konkreten Anwendungsfall ungetestet. Der Workaround für das Array mit Elementen unterschiedlichen generischen Typs ist zwar etwas umständlich, doch m.E. besser, als es als AnyObject-Array anzugeben.

0
PsySkill 
Fragesteller
 17.09.2022, 18:15
@regex9

Danke für deine Antwort. Für das Problem mit den Pfeilen habe ich nun doch selber einen eigenen Weg gefunden der um ein vielfaches einfache ist. Hab einfach mit .padding(all, -4.0) und opacity(0) hinbekommen

0