The picker doesn't update the number of segments it has when a value is changed during execution - xcode

I have recently started learning swiftUI and I'm facing some issues here. This is the code:
struct ContentView: View {
#State private var measurementType = 0
#State private var inputValue = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
var outputValue = ""
let measurementTypes = ["Temp", "Length", "Time", "Volume"]
var typeDictionary = [
["Celsius", "Fahrenheit", "Kelvin"],
["Meters", "Kilometers", "Feet", "Yards", "Miles"],
["Seconds", "Minutes", "Hours", "Days"],
["Milliliters", "Liters", "Cups", "Pints", "Gallons"]
]
var body: some View {
NavigationView {
Form {
Section(header: Text("Choose the type of measurement")) {
Picker("The type of measurement", selection: $measurementType) {
ForEach(0 ..< measurementTypes.count) {
Text("\(measurementTypes[$0])")
}
}
.id(measurementType)
.pickerStyle(SegmentedPickerStyle())
}
Section {
TextField("Enter the value", text: $inputValue)
.keyboardType(.decimalPad)
}
Section(header: Text("Choose the input unit")) {
Picker("The input unit", selection: self.$inputUnit) {
ForEach(0 ..< typeDictionary[measurementType].count) {
Text("\(typeDictionary[measurementType][$0])")
}
}
.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("Choose the output unit")) {
}
Section(header: Text("Converted Value")) {
Text("")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Whenever I change the measurementType while the code is running, the values of the picker in the input units section update, but the number of segments don't. If I start with the value of measurementType as 1, then the app simply crashes when I choose Temp or Time from the picke.

When using ForEach, you need to provide an id so SwiftUI can uniquely identify each element in the array and know when to redraw the view. You can use id: \.self to use the item's value as its id.
Here's an updated working version:
import SwiftUI
struct ContentView: View {
#State private var measurementType = 0
#State private var inputValue = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
var outputValue = ""
let measurementTypes = ["Temp", "Length", "Time", "Volume"]
var typeDictionary = [
["Celsius", "Fahrenheit", "Kelvin"],
["Meters", "Kilometers", "Feet", "Yards", "Miles"],
["Seconds", "Minutes", "Hours", "Days"],
["Milliliters", "Liters", "Cups", "Pints", "Gallons"]
]
var body: some View {
NavigationView {
Form {
Section(header: Text("Choose the type of measurement")) {
Picker("The type of measurement", selection: $measurementType) {
ForEach(0 ..< measurementTypes.count, id: \.self) {
Text("\(measurementTypes[$0])")
}
}
.id(measurementType)
.pickerStyle(SegmentedPickerStyle())
}
Section {
TextField("Enter the value", text: $inputValue)
.keyboardType(.decimalPad)
}
Section(header: Text("Choose the input unit")) {
Picker("The input unit", selection: self.$inputUnit) {
ForEach(0 ..< typeDictionary[measurementType].count, id: \.self) {
Text("\(typeDictionary[measurementType][$0])")
}
}
.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("Choose the output unit")) {
}
Section(header: Text("Converted Value")) {
Text("")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related

Work out time 1.5 hours ahead using current time and display this in the list

I have the following code someone presses on the Table and its displays the current time which is the arrival time of a customer.
I want to display the time they must leave by, next to the current time this will always be 1.5 hours ahead I can not work out how to do this. everything I have tried comes back with an error.
Still new to Xcode
any help would be great
import SwiftUI
struct TimeListView: View {
#State var tableOne = false
#State var tableTwo = false
#State var tableThree = false
var body: some View {
// Title
VStack {
Text("Arrival Times")
.font(.title)
.fontWeight(.bold)
// List View
List {
// Table 1
HStack {
Button(action: {
self.tableOne.toggle()
}, label: {
Text("Table 1 -")
})
if tableOne {
Text(getCurrentTime())
}
}
// Table 2
HStack {
Button(action: {
self.tableTwo.toggle()
}, label: {
Text("Table 2 -")
})
if tableTwo {
Text(getCurrentTime())
}
}
// Table 3
HStack {
Button(action: {
self.tableThree.toggle()
}, label: {
Text("Table 3 -")
})
if tableThree {
Text(getCurrentTime())
}
}
}
}
}
}
struct TimeListView_Previews: PreviewProvider {
static var previews: some View {
TimeListView()
}
}
// Get Current Time Function
func getCurrentTime() -> String {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK_POSIX")
dateFormatter.dateFormat = "HH:mm"
return dateFormatter.string(from: Date())
you need make a date and add 1.5 hour to it, also you forgot create 3 deferent State for them.
import SwiftUI
struct ContentView: View {
var body: some View {
TimeListView()
}
}
struct TimeListView: View {
#State private var table1: Bool = false
#State private var table2: Bool = false
#State private var table3: Bool = false
#State private var timeForShow1: String?
#State private var timeForShow2: String?
#State private var timeForShow3: String?
var body: some View {
VStack {
Text("Arrival Times")
.font(.title)
.fontWeight(.bold)
List {
HStack {
Text("Table 1 -")
.onTapGesture {
if table1 { table1.toggle() }
else { timeForShow1 = getCurrentTime; table1.toggle() }
}
if table1 { Text(timeForShow1 ?? "not available!") }
}
HStack {
Text("Table 2 -")
.onTapGesture {
if table2 { table2.toggle() }
else { timeForShow2 = getCurrentTime; table2.toggle() }
}
if table2 { Text(timeForShow2 ?? "not available!") }
}
HStack {
Text("Table 3 -")
.onTapGesture {
if table3 { table3.toggle() }
else { timeForShow3 = getCurrentTime; table3.toggle() }
}
if table3 { Text(timeForShow3 ?? "not available!") }
}
}
}
}
}
var getCurrentTime: String? {
if let date = Calendar.current.date(byAdding: .minute, value: 90, to: Date()) {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK_POSIX")
dateFormatter.dateFormat = "HH:mm"
return dateFormatter.string(from: date)
}
else {
return nil
}
}
You'll probably get a half dozen different ways of doing this but this version allows for adaptive/reusable code. You can easily add a Table by adding one to the var tables: [Table] everything would adjust automatically.
import SwiftUI
class Table: ObservableObject {
let id: UUID = UUID()
#Published var name: String
#Published var status: Bool
#Published var entryTime: Date
var exitTime: Date{
return entryTime.addingTimeInterval(60*60*1.5)
}
init(name: String, status: Bool = false, entryTime: Date = Date.init(timeIntervalSince1970: 0)) {
self.name = name
self.status = status
self.entryTime = entryTime
}
}
struct TimeListView: View {
#State var tables: [Table] = [Table(name: "Table 1 -"), Table(name: "Table 2 -"), Table(name: "Table 3 -")]
var body: some View {
VStack{
Text("Arrival Times")
.font(.title)
.fontWeight(.bold)
List {
ForEach(tables, id: \.id, content: { table in
TableView(table: table)
})
}
}
}
}
struct TableView: View {
#ObservedObject var table: Table
var body: some View {
HStack {
Button(action: {
table.status.toggle()
table.entryTime = Date()
}, label: {
Text(table.name)
})
if table.status{
Text(table.entryTime, formatter: dateFormatter)
Text(table.exitTime, formatter: dateFormatter)
}
}
}
}
struct TimeListView_Previews: PreviewProvider {
static var previews: some View {
TimeListView()
}
}
var dateFormatter: DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK_POSIX")
dateFormatter.dateFormat = "HH:mm"
return dateFormatter
}

SwiftUI 2.0 App crash when Picker selectedValue changed

Here is my sample code:
import SwiftUI
final class ViewModel: ObservableObject {
#Published var countries: [Country?] = [
Country(id: 0, name: "country1", cities: ["c1 city1", "c1 city2", "c1 city3"]),
Country(id: 1, name: "country2", cities: ["c2 city1", "c2 city2", "c2 city3"]),
Country(id: 2, name: "country3", cities: ["c3 city1", "c3 city2", "c3 city3"])
]
}
struct ContentView: View {
#ObservedObject var viewModel = ViewModel()
#State private var selectedCountry: Country? = nil
#State private var selectedCity: String? = nil
var body: some View {
VStack {
Picker("", selection: $selectedCountry) {
ForEach(viewModel.countries, id: \.self) { country in
Text(country!.name).tag(country)
}
}
.pickerStyle(SegmentedPickerStyle())
Text(selectedCountry?.name ?? "no selection")
if selectedCountry != nil {
Picker("", selection: $selectedCity) {
ForEach(selectedCountry!.cities, id: \.self) { city in
Text(city!).tag(city)
}
}
.pickerStyle(SegmentedPickerStyle())
Text(selectedCity ?? "no selection")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Country: Codable, Hashable, Identifiable {
var id: Int
var name: String
var cities: [String?]
}
It works at first but when select a country then select another country then go back to the first choice it crashes,
I am using latest Xcode beta I don't know if it is the cause or my approach is wrong.
The problem is in cached binding. We need to recreate picker if source of data changed.
Find below a fix. Tested with Xcode 12.4 / iOS 14.4
if selectedCountry != nil {
Picker("", selection: $selectedCity) {
ForEach(selectedCountry!.cities, id: \.self) { city in
Text(city!).tag(city)
}
}
.pickerStyle(SegmentedPickerStyle())
.id(selectedCountry!) // << here !!
Text(selectedCity ?? "no selection")
}

SwiftUI - ObservableObject, EnvironmentObject, SummaryView

After creating the code, i would like to create a summary view that allows me to view the values that have been chosen in the picker.
How can I do? I read some forums about #ObservableObject and about #EnvironmentObject, but I can't understand ...
Thanks very much :)
import SwiftUI
//SUMMARYPAGE
struct SummaryView: View {
var body: some View {
NavigationView {
Form {
VStack(alignment: .leading, spacing: 6) {
Text("First Animal: \("firstAnimalSelected")")
Text("First Animal: \("secondAnimalSelected")")
}
}
}
}
}
struct SummaryView_Previews: PreviewProvider {
static var previews: some View {
SummaryView()
}
}
enum Animal: String, CaseIterable {
case select
case bear
case cat
case dog
case lion
case tiger
}
struct ContentView: View {
#State private var firstAnimal = Animal.allCases[0]
#State private var secondAnimal = Animal.allCases[0]
var body: some View {
NavigationView {
Form {
Section(header: Text("Animals")
.foregroundColor(.black)
.font(.system(size: 15))
.fontWeight(.bold)) {
Picker(selection: $firstAnimal, label: Text("Select first animal")) {
ForEach(Animal.allCases, id: \.self) { element in
Text(element.rawValue.capitalized)
}
}
Picker(selection: $secondAnimal, label: Text("Select second animal")) {
ForEach(Animal.allCases.filter { $0 != firstAnimal || firstAnimal == .select }, id: \.self) { element2 in
Text(element2.rawValue.capitalized)
}
}
}.font(.system(size: 15))
}.navigationBarTitle("List", displayMode: .inline)
}
}
}
You can move your #State properties to an ObservableObject:
class ViewModel: ObservableObject {
#Published var firstAnimal = Animal.allCases[0]
#Published var secondAnimal = Animal.allCases[0]
}
and access them from an #EnvironmentObject:
struct ContentView: View {
#EnvironmentObject var viewModel: ViewModel
var body: some View {
...
Picker(selection: $viewModel.firstAnimal, label: Text("Select first animal")) {
ForEach(Animal.allCases, id: \.self) { element in
Text(element.rawValue.capitalized)
}
}
}
}
struct SummaryView: View {
#EnvironmentObject var viewModel: ViewModel
var body: some View {
NavigationView {
Form {
VStack(alignment: .leading, spacing: 6) {
Text("First Animal: \(viewModel.firstAnimal.rawValue)")
Text("Second Animal: \(viewModel.secondAnimal.rawValue)")
}
}
}
}
}
Remember to inject your ViewModel to your root view:
ContentView().environmentObject(ViewModel())

SwiftUI Passing arguments over to View is not always available

I have a problem with passing arguments over to a View in SwiftUI when calling it. I have this View
import SwiftUI
struct GoodsItemFilterView: View {
#Environment(\.presentationMode) var presentationMode
#State var ref1Array: [String] = []
#State var ref2Array: [String] = []
#State var ref3Array: [String] = []
#State var stockStatusArray: [String] = []
#State var zoneArray: [String] = []
#State var selectorRef1 = 0
#State var selectorRef2 = 0
#State var selectorRef3 = 0
#State var selectorStockStatus = 0
#State var selectorZone = 0
var body: some View {
NavigationView {
Form{
Section(header: Text("Zone"), content: {
Picker(selection: $selectorZone, label:
Text("Zone")) {
ForEach(0 ..< zoneArray.count, id:\.self) {
Text(self.zoneArray[$0])
}
}
})
Section(header: Text("References"), content: {
Picker(selection: $selectorRef1, label:
Text("Reference 1")) {
ForEach(0 ..< ref1Array.count, id:\.self) {
Text(self.ref1Array[$0])
}
}
Picker(selection: $selectorRef2, label:
Text("Reference 2")) {
ForEach(0 ..< ref2Array.count, id:\.self) {
Text(self.ref2Array[$0])
}
}
Picker(selection: $selectorRef3, label:
Text("Reference 3")) {
ForEach(0 ..< ref3Array.count, id:\.self) {
Text(self.ref3Array[$0])
}
}
})
Section(header: Text("Status"), content: {
Picker(selection: $selectorStockStatus, label:
Text("Condition")) {
ForEach(0 ..< stockStatusArray.count, id:\.self) {
Text(self.stockStatusArray[$0])
}
}
})
Button(action: {
self.selectorZone = 0
self.selectorRef1 = 0
self.selectorRef2 = 0
self.selectorRef3 = 0
self.selectorStockStatus = 0
}, label: {
HStack(){
Spacer()
Image(systemName: "return")
Text("Reset filters")
Spacer()
}
})
}.navigationBarTitle("Filter")
.navigationBarItems(leading: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}
)
), trailing: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Done")
}
)
))
}.onAppear{
self.ref1Array.insert("***ALL***", at: 0)
self.ref2Array.insert("***ALL***", at: 0)
self.ref3Array.insert("***ALL***", at: 0)
self.stockStatusArray.insert("***ALL***", at: 0)
self.zoneArray.insert("***ALL***", at: 0)
}
}
}
struct GoodsItemFilter_Previews: PreviewProvider {
static var previews: some View {
GoodsItemFilterView(ref1Array: ["MAX100", "MAX101", "MAX102"], ref2Array: ["REF2_100", "REF2_101"], ref3Array: ["REF3_100", "REF3_101"])
}
}
and when I call it I can pass over the values of the arrays as arguments:
GoodsItemFilterView(ref1Array: ["MAX100", "MAX101", "MAX102"], ref2Array: ["REF2_100", "REF2_101"], ref3Array: ["REF3_100", "REF3_101"])
Now I have another view which is basically a copy of this one with a few changed names etc
//
// OrderHeaderFilter.swift
// WMS Toolbox
//
// Created by Max on 2020-01-24.
// Copyright © 2020 Max. All rights reserved.
//
import SwiftUI
//import Combine
struct OrderHeaderFilterView: View {
#Environment(\.presentationMode) var presentationMode
#State var orderTypeArray: [String] = []
#State var carrierArray: [String] = []
#State var fromStatus2 = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.fromStatus")
// #State private var fromStatus2 = "040"
#State private var direction = ""
#State private var fromStatus = ""
#State private var toStatus = ""
#State private var orderType = ""
#State var selectorOrderType = 0
#State var selectorCarrier = 0
#State private var selectorIndex = 1
#State private var fromStatusSelectorIndex = 6
#State private var toStatusSelectorIndex = 2
#State private var directions = ["Inbound","Outbound","Both"]
private var orderStatusFromArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
private var orderStatusToArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
#State var orderStatus = OrderStatus.s05
enum OrderStatus: String, CaseIterable, Identifiable {
case s05 = "005"
case s10 = "010"
case s22 = "022"
case s25 = "025"
case s30 = "030"
case s35 = "035"
case s40 = "040"
case s45 = "045"
case s46 = "046"
case s60 = "060"
var id: String { rawValue }
}
enum Direction: String, CaseIterable{
case outbound = "1"
case inbound = "2"
case both = "3"
init(type: String) {
switch type {
case "1": self = .outbound
case "2": self = .inbound
case "3": self = .both
default: self = .both
}
}
var text: String {
switch self {
case .outbound: return "Outbound"
case .inbound: return "Inbound"
case .both: return "Both"
}
}
}
init(){
//nothing here
}
var body: some View {
return NavigationView{
Form{
HStack{
Text("Direction")
Spacer()
Picker(selection: $direction, label:
Text("Direction")) {
ForEach(directions, id:\.self) {
status in
Text(status)
}
}
.pickerStyle(SegmentedPickerStyle())
}
Picker(selection: $fromStatus, label:
Text("From Status")) {
ForEach(orderStatusFromArray, id:\.self) {
status in
Text(status)
}
}
Picker(selection: $toStatus, label:
Text("To Status")) {
ForEach(orderStatusFromArray, id:\.self) {
status in
Text(status)
}
}
}.navigationBarTitle("Filter")
.navigationBarItems(leading: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}
)
), trailing: (
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Done")
}
)
))
}.onAppear{
self.direction = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.direction")!
self.fromStatus = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.fromStatus")!
self.toStatus = UserDefaults.standard.string(forKey: "view.orderHeaderFilter.toStatus")!
self.orderTypeArray.insert("***ALL***", at: 0)
self.carrierArray.insert("***ALL***", at: 0)
}
}
}
struct OrderHeaderFilter_Previews: PreviewProvider {
static var previews: some View {
OrderHeaderFilterView()
}
}
and when I call it, it is not prompting me to pass over the arrays as arguments:
OrderHeaderFilterView()
What is the difference between those 2 views that the one is asking for arguments on initilization and the other one isn't? To be clear, in the end I want to pass over the arguments, so GoodsItemFilterView() is doing exactly what I need.
EnvironmentObjects are not passed via init method they are implicitly injected. The orderTypeArray and carrierArray have already initial values. So OrderHeaderFilterView() does not prompt you for arguments.
Found the issue(s):
I had this piece in the code
init(){
//nothing here
}
This needs to be removed, otherwise it will not ask for any variables.
The other issue is the one I don't understand:
private var orderStatusFromArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
private var orderStatusToArray: [String] = ["005", "010", "022", "025", "030", "035", "040", "045", "046", "047", "060"]
If I change the var to let, it works as expected. Another option is to remove the private at the beginning. So it looks like as soon as you have a
private var ...
in your code, all arguments become private. Maybe I am missing something here but that seems like a bug to me.

How to access value from an item in ForEach list

How to access values from particular item on the list made with ForEach?
As you can see I was trying something like this (and many other options):
Text(item[2].onOff ? "On" : "Off")
I wanted to check the value of toggle of 2nd list item (for example) and update text on the screen saying if it's on or off.
And I know that it's something to do with #Binding and I was searching examples of this and trying few things, but I cannot make it to work. Maybe it is a beginner question. I would appreciate if someone could help me.
My ContentView:
struct ContentView: View {
// #Binding var onOff : Bool
#State private var onOff = false
#State private var test = false
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOff ? "On" : "Off")
// Text(item[2].onOff ? "On" : "Off")
}
ForEach((1...15), id: \.self) {item in
ListItemView()
}
}
.navigationBarTitle(Text("List"))
}
}
}
And ListItemView:
import SwiftUI
struct ListItemView: View {
#State private var onOff : Bool = false
// #Binding var onOff : Bool
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: self.$onOff) {
Text("Label")
}
.labelsHidden()
}
}
}
I don't know what exactly you would like to achieve, but I made you a working example:
struct ListItemView: View {
#ObservedObject var model: ListItemModel
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: self.$model.switchedOnOff) {
Text("Label")
}
.labelsHidden()
}
}
}
class ListItemModel: ObservableObject {
#Published var switchedOnOff: Bool = false
}
struct ContentView: View {
#State private var onOff = false
#State private var test = false
#State private var list = [
(id: 0, model: ListItemModel()),
(id: 1, model: ListItemModel()),
(id: 2, model: ListItemModel()),
(id: 3, model: ListItemModel()),
(id: 4, model: ListItemModel())
]
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOff ? "On" : "Off")
// Text(item[2].onOff ? "On" : "Off")
}
ForEach(self.list, id: \.id) {item in
ListItemView(model: item.model)
}
}
.navigationBarTitle(Text("List"))
}.onReceive(self.list[1].model.$switchedOnOff, perform: { switchedOnOff_second_item in
self.onOff = switchedOnOff_second_item
})
}
}
The #Published basically creates a Publisher, which the UI can listen to per onReceive().
Play around with this and you will figure out what these things do!
Good luck :)
import SwiftUI
struct ContentView: View {
#State private var onOffList = Array(repeating: true, count: 15)
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOffList[1] ? "On" : "Off")
}
ForEach((onOffList.indices), id: \.self) {idx in
ListItemView(onOff: self.$onOffList[idx])
}
}
.navigationBarTitle(Text("List"))
}
}
}
struct ListItemView: View {
#Binding var onOff : Bool
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: $onOff) {
Text("Label")
}
.labelsHidden()
}
}
}
I understand that you are directing me to use ObservableObject. And probably it's the best way to go with final product. But I am still thinking about #Binding as I just need to pass values better between 2 views only. Maybe I still don't understand binding, but I came to this solution.
struct ContentView: View {
// #Binding var onOff : Bool
#State private var onOff = false
// #State private var test = false
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOff ? "On" : "Off")
// Text(self.item[2].$onOff ? "On" : "Off")
// Text(item[2].onOff ? "On" : "Off")
}
ForEach((1...15), id: \.self) {item in
ListItemView(onOff: self.$onOff)
}
}
.navigationBarTitle(Text("List"))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and ListItemView:
import SwiftUI
struct ListItemView: View {
// #State private var onOff : Bool = false
#Binding var onOff : Bool
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: self.$onOff) {
Text("Label")
}
.labelsHidden()
}
}
}
What is happening now is text is being updated after I tap toggle. But I have 2 problems:
tapping 1 toggle changes all of them. I think it's because of this line:
ListItemView(onOff: self.$onOff)
I still cannot access value of just one row. In my understanding ForEach((1...15), id: .self) make each row have their own id, but I don't know how to access it later on.

Resources