Why is iOS16's NavigationLink disabled? - swift

I am currently trying to use iOS16's NavigationLink to show detail views for my list items, but for some reason the items seem to be disabled - does anyone know why this is happening?
NavigationLink(value: example) {
ListRowView(title: example.title,
subtitle: example.subtitle)
}
.navigationDestination(for: CollectionItem.self) { example in
Text(example.title)
}

I forgot to replace the NavigationView with the new NavigationStack.
Even though the answer is pretty obvious, it will still maybe overlooked more often. So the final code would look like this:
NavigationStack {
List {
// ...
ForEach(viewModel.examples) { example in
NavigationLink(value: example) {
ListRowView(title: example.title,
subtitle: example.subtitle)
}
}
// ...
}
.navigationDestination(for: CollectionItem.self) { example in
Text(example.title)
}
.navigationTitle(viewModel.title)
}

Related

SwiftUI – Xcode not building when using the index variable of a ForEach loop

I have a ForEach loop inside a TabView. Whenever I try to build the project using the index (for lack of a better word, here: slide) variable in a condition, it stalls and is not building; it's not showing any errors. If I'm using it interpolated, it works/builds.
TabView {
ForEach(1...3, id: \.self) { slide in
HStack {
if slide == 1 {
Text("Do something if slide is 1st slide") // doesn't work
}
Text("Test \(slide)") // works
}
.tag(slide)
}
}
Thanks for any tips!
Edit: Deleted case 2)
Edit: Added Tabview

Animate Change in SwiftUI ForEach Grid Using Array Indices

Does anyone know how I can animate a sorting change to grid items while iterating off of the array index in a ForEach?
I am showing a grid of items (LazyVGrid) from an array and the user can change the sort order of these items. When the sort order changes, I'd like to animate the change in the grid. This would work great if I were to use code similar to the following:
Button {
withAnimation {
noteModel.sortMethod = noteModel.sortMethod.next()
}
} label: {
Text("Change Sort Order")
}
ScrollView {
LazyVGrid(columns: columns) {
ForEach(self.notes, id: \.self) { note in
VStack {
Text(note.title)
Text(note.body)
}
.padding()
}
}
}
However, if my ForEach iterates off of the array index the change does not animate. So it won't animate if my code is similar to this:
Button {
withAnimation {
noteModel.sortMethod = noteModel.sortMethod.next()
}
} label: {
Text("Change Sort Order")
}
ScrollView {
LazyVGrid(columns: columns) {
ForEach(self.notes.indices, id: \.self) { idx in
VStack {
Text(self.notes[idx].title)
Text(self.notes[idx].body)
}
.padding()
}
}
}
Why don't I just do it the first way? The reason is because I found a great resource for dynamically adjusting frame sizes for items inside a grid (found here: https://swiftui-lab.com/impossible-grids/), but that requires the use of array indices in order to work. It's pretty cool and I am hoping I don't have to choose one or the other (dynamic sizing or animate changes).
Thoughts or ideas would be greatly appreciated. Thanks!
ForEach in SwiftUI isn’t the same as a for loop. Your data needs to be identifiable for structural identity to work and you certainly should not be using indices. Either implement the Identifiable protocol or tell ForEach what key path is a unique ID (or is a getter that creates one from other properties) in the item struct. Don’t ever use id:\.self.
You can learn more about structural identity in the video WWDC 2021 Demystify SwiftUI

Swift List looks too big

I have a List in my App. When I click on an Item in the List it opens another List. The First List looks fine, but the second List looks too big. Here is how it looks normal(good):
And here is the List that looks not good:
On the second Picture, the Problems I have are:
the title is too big
the list item starts too far away from the title
Thats the Code, the Code looks the same in both Lists. The only difference is that I used some different variable names in the second one.
return VStack {
ZStack {
NavigationView {
List {
ForEach(zettelArr) { x in
NavigationLink(destination: ZettelViewDetails(passedVar: x)) {
Text("\(x.name)")
}
}
}.navigationBarTitle(alertVariable)
.navigationBarItems(trailing: Button(action: {
alertView()
//self.isShown = true
}) {
Image(systemName: "plus")
})
}
//SwiftUIAlertViewWithTextBox(isShown: $isShown, message: $msg, title: $title)
}
}
My goal is, that the second List looks exactly like the Main List.
How is this possible ?
Okay I don't know why the formatting from the pictures are not working, maybe I am just dumb. The first Picture is the MainList that looks like I want it to look and the second one is the list that is formatted differently, even though its quite the same Code.
You are using NavigationView for both lists view , while only the first one should be inside a NavigationView
Your ZettelViewDetails shouldn't have NavigationView as parent
Example :
Main
NavigationView {
List {
ForEach(zettelArr) { x in
NavigationLink(destination: ZettelViewDetails(passedVar: x))
{
Text("\(x.name)")
}
}
}.navigationBarTitle(alertVariable)
}
Details
VStack {
ForEach(zettelArr) { x in
NavigationLink(destination: ZettelViewDetails(passedVar: x)) {
Text("\(x.name)")
}
}
}

SwiftUI pick a value from a list with ontap gesture

i'm try to pick some value from a swiftUI list with the ontapGesture.
I have a Searchlist of item, the user need to choose one item and then the app will send the choice to an array that later will be use for other info.
now my problem is how do I do that? how can do it?
as you can see from the code below, I want to pick the value of the item.icaoAirport corresponding to that raw and pass to an array.
List(dm.vettoreAeroporti.filter{
// $0.icaoCode.contains(searchTerm)
$0.icaoAirport.localizedCaseInsensitiveContains(searchTerm)
}) { item in
HStack {
Text(item.icaoAirport).bold()
Spacer()
Text(item.nameAirport)
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.dm.openFileJson(fileName: "data")
}
}
.onTapGesture {
// ?? I need to take the value of the item.icaoAirport corresponding to that raw
}
thanks in advance for the help.
While your, Damiano, answer is right it works only on tap on the Text.
Sometimes it is needed to have the whole row tappable, so here is the solution for this case:
List(items) { item in
HStack {
Text(item.icaoAirport).bold()
Spacer()
Text(item.nameAirport)
}
.contentShape(Rectangle())
.onTapGesture {
print("touched item \(item)")
}
}
Thanks Paul for this (https://www.hackingwithswift.com/quick-start/swiftui/how-to-control-the-tappable-area-of-a-view-using-contentshape)
Note, that if you have content only on one side of the HStack e.g:
HStack {
Text("Some text") // This text will be on the left side
}
then the .contentShape(Rectange()) will work only for the width of the text. So to enable it for the whole width just add a trailing Spacer() like this:
HStack {
Text("Some text")
Spacer() // This will take all the space from the end of the text up to the end of the whole row
}
.contentShape(Rectangle())
I solved my issue:
HStack {
Text(item.icaoAirport).bold()
Spacer()
Text(item.nameAirport)
.onTapGesture {
print("touched item \(item.icaoAirport)")
}
}
List {
ForEach(Type.allCases, id: \.self) { type in
FilterRow(Color: type.Color,
title: type.Title)
.contentShape(Rectangle())
.onTapGesture {
print("Ammu type:\(type)")
}
}
}
This one is quite old but the accepted answers are pretty strange. I was able to achieve this effect in a much simpler way. Consider:
List {
ForEach(myItems) { item in
Button {
// action
} label: {
Text(item.name)
}
}
}
Now doing it this way, the button is edge-to-edge in the row. However, the button is now the accent color of your app. That's probably not what you want! So I tried adding:
Button(...)
.buttonStyle(PlainButtonStyle())
The text of the button is now the primary foreground color, great! But there's a different problem now! The button no longer goes edge-to-edge, it's only the text. To fix this, i instead:
Button(...)
.foregroundColor(.primary)
This is edge-to-edge with primary color text and custom actions. No need to create HStacks with spacers and an onTap, it's all just handled. OP's post has text on both sides so likely still needs an HStack for their specific usecase but a Button is still probably a better choice.

Ambiguous reference with a Picker in Swift UI

I'm trying to make a Picker with SwiftUI. I've follow a tutorial but don't have the same result. There is Ambiguous reference on the self.category.count and self.category[$0]. After one entire day, I still don't know how to fix it ...
import SwiftUI
struct Picker : View {
var category = ["Aucun", "BF Glaive", "Baguette", "Negatron", "Larme", "Ceinture", "Arc", "Cotte", "Spatule"]
#State private var selectedCategory = 0
var body: some View {
VStack {
Picker(selection: $selectedCategory, label: Text("Item")) {
ForEach(0 ..< self.category.count) {
Text(self.category[$0])
.tag($0)
}
}
Text("Selected : \(category[selectedCategory])")
}
}
}
To resolve name conflicts between modules, you can either:
Rename your Picker to something else.
Use the qualified (full) name:
SwiftUI.Picker(selection: $selectedCategory, label: Text("Item")) {
The error message Ambiguous reference to member 'count’ is misleading. What you have is a naming conflict between SwiftUI.Picker and your Picker struct. Just change the name of your struct to something other than Picker. For example:
struct CategoryPicker : View {
// ...
}
Alternatively, you can resolve the naming conflict between the modules by providing the fully qualified name for SwiftUI.Picker (as Sulthan pointed out):
SwiftUI.Picker(selection: $selectedCategory, label: Text("Item")) {
// ...
}
However, I wouldn’t advise this option unless your intention is to replace SwiftUI.Picker everywhere in your app. Your code includes the category array and a Text view, so it's unlikely this is what you're after.
If the app eventually needs OtherPicker with a SwiftUI.Picker and the module name is omitted again, it’ll be even more confusing to track down the error—and you’ve already spent an “entire day” on it. So, best to avoid this possibility by not introducing the conflict at all :)