SwiftUI: Wie kann ich die Pfeile entfernen?
Wie entferne ich in meiner Liste diese Pfeile rechts zum Navigieren?
Ich habe online eine potenzielle Lösung gefunden, welche vielleicht funktioniert. Allerdings verstehe ich diesen Code nicht ganz und kann ihn deshalb nicht auf meinen anwenden.
Außerdem quäle ich mich mit dem Problem, das ich mehrere dieser Bilder habe und diese alle zu einem anderen Ziel führen sollen. Allerdings weiß ich nicht wie.
Ich habe daran gedacht, dass über eine JSON-Datei zu machen, so wie ich es mit anderen Sachen auch gemacht habe. Aber ich habe das Problem die verschiedenen Ziele einzutragen, so das SwiftUI z.B. bei Bild 1 zu View 1 geht und Bild 2 zu View 2.
Code aus dem Internet:
List {
ForEach(elements) { element in
ZStack {
CustomView(element: element)
NavigationLink(destination: DestinationView()) {
EmptyView()
}.buttonStyle(PlainButtonStyle())
}
}
}
Mein Code:
List {
Section {
NavigationLink {
Mechanik()
}
label: {
Image("mechanik")
.resizable()
.aspectRatio(16 / 7, contentMode: .fit)
.buttonStyle(PlainButtonStyle())
}
.listRowInsets(EdgeInsets())
.buttonStyle(PlainButtonStyle())
}
}
Bild:
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())
}
}
}
}
}
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.
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.
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.