I have a SwiftUI app with a basic List/Detail structure. A new item is created from a modal sheet. When I create a new item and save it I want THAT list item to be selected. As it is, if no item is selected before an add, no item is selected after an add. If an item is selected before an add, that same item is selected after the add.
I'll include code for the ContentView, but this is really the simplest example of List/Detail.
struct ContentView: View {
@ObservedObject var resortStore = ResortStore()
@State private var addNewResort = false
@State private var coverDeletedDetail = false
@Environment(\.presentationMode) var presentationMode
var body: some View {
List {
ForEach(resortStore.resorts) { resort in
NavigationLink(destination: ResortView(resort: resort)) {
HStack(spacing: 20) {
Image("FlatheadLake1")
//bunch of modifiers
VStack(alignment: .leading, spacing: 10) {
//the cell contents
}
}
}
}
.onDelete { indexSet in
self.removeItems(at: [indexSet.first!])
self.coverDeletedDetail.toggle()
}
if UIDevice.current.userInterfaceIdiom == .pad {
NavigationLink(destination: WelcomeView(), isActive: self.$coverDeletedDetail) {
Text("")
}
}
}//list
.onAppear(perform: self.selectARow)
.navigationBarTitle("Resorts")
.navigationBarItems(leading:
//buttons
}//body
func removeItems(at offsets: IndexSet) {
resortStore.resorts.remove(atOffsets: offsets)
}
func selectARow() {
//nothing that I have tried works here
print("selectARow")
}
}//struct
And again - the add item modal is extremely basic:
struct AddNewResort: View {
//bunch of properties
var body: some View {
VStack {
Text("Add a Resort")
VStack {
TextField("Enter a name", text: $resortName)
//the rest of the fields
}
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(EdgeInsets(top: 20, leading: 30, bottom: 20, trailing: 30))
Button(action: {
let newResort = Resort(id: UUID(), name: self.resortName, country: self.resortCountry, description: self.resortDescription, imageCredit: "Credit", price: Int(self.resortPriceString) ?? 0, size: Int(self.resortSizeString) ?? 0, snowDepth: 20, elevation: 3000, runs: 40, facilities: ["bar", "garage"])
self.resortStore.resorts.append(newResort)
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Save Trip")
}
.padding(.trailing, 20)
}
}
}
To show the issue - The list with a selection:
The list after a new item created showing the previous selection:
Any guidance would be appreciated. Xcode 11.4
I tried to reconstitute your code as closely as could so that it builds. Here is what I have in the end. We have a list of resorts and when a new resort is saved in the AddNewResort sheet, if we are currently in split view (horizontalSizeClass is regular), we will select the new resort, otherwise just dismiss the sheet.
import SwiftUI
class ResortStore: ObservableObject {
@Published var resorts = [Resort(id: UUID(), name: "Resort 1")]
}
struct ContentView: View {
@ObservedObject var resortStore = ResortStore()
@State private var addingNewResort = false
@State var selectedResortId: UUID? = nil
var navigationLink: NavigationLink<EmptyView, ResortView>? {
guard let selectedResortId = selectedResortId,
let selectedResort = resortStore.resorts.first(where: {$0.id == selectedResortId}) else {
return nil
}
return NavigationLink(
destination: ResortView(resort: selectedResort),
tag: selectedResortId,
selection: $selectedResortId
) {
EmptyView()
}
}
var body: some View {
NavigationView {
ZStack {
navigationLink
List {
ForEach(resortStore.resorts, id: \.self.id) { resort in
Button(action: {
self.selectedResortId = resort.id
}) {
Text(resort.name)
}
.listRowBackground(self.selectedResortId == resort.id ? Color.gray : Color(UIColor.systemBackground))
}
}
}
.navigationBarTitle("Resorts")
.navigationBarItems(trailing: Button("Add Resort") {
self.addingNewResort = true
})
.sheet(isPresented: $addingNewResort) {
AddNewResort(selectedResortId: self.$selectedResortId)
.environmentObject(self.resortStore)
}
WelcomeView()
}
}
}
struct ResortView: View {
let resort: Resort
var body: some View {
Text("Resort View for resort name: \(resort.name).")
}
}
struct AddNewResort: View {
//bunch of properties
@Binding var selectedResortId: UUID?
@State var resortName = ""
@Environment(\.presentationMode) var presentationMode
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@EnvironmentObject var resortStore: ResortStore
var body: some View {
VStack {
Text("Add a Resort")
VStack {
TextField("Enter a name", text: $resortName)
//the rest of the fields
}
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(EdgeInsets(top: 20, leading: 30, bottom: 20, trailing: 30))
Button(action: {
let newResort = Resort(id: UUID(), name: self.resortName)
self.resortStore.resorts.append(newResort)
self.presentationMode.wrappedValue.dismiss()
if self.horizontalSizeClass == .regular {
self.selectedResortId = newResort.id
}
}) {
Text("Save Trip")
}
.padding(.trailing, 20)
}
}
}
struct WelcomeView: View {
var body: some View {
Text("Welcome View")
}
}
struct Resort {
var id: UUID
var name: String
}
I started writing a series of articles about navigation in SwiftUI List view, there are a lot of points to consider while implementing programmatic navigation. Here is the one that describes this solution that I'm suggesting: SwiftUI Navigation in List View: Programmatic Navigation. This solution works at the moment on iOS 13.4.1. SwiftUI is changing rapidly, so we have to keep on checking.
And here is my previous article that explains why a more simple solution of adding a NavigationLink to each List row has some problems at the moment SwiftUI Navigation in List View: Exploring Available Options
Let me know if you have questions, I'd be happy to help where I can.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With