#swiftui #swiftui-list #swiftui-navigationlink #swiftui-form
#swiftui #swiftui-list #swiftui-navigationlink #swiftui-form
Вопрос:
Я вижу эту проблему, когда настроенная форма / ссылка навигации, похоже, перестает передавать данные об изменениях.
Ожидаемое поведение: галочки должны обновляться при выборе другого блюда.
Наблюдаемое поведение: вы можете видеть, как любимая еда меняется за пределами пункта назначения NavigationLink, но не внутри.
Эта настройка отражает динамическое приложение, в котором ForEach используется для отображения различных навигационных ссылок в форме на основе родительских данных. Как ни странно, это работает, если вы заменяете Form на VStack, поэтому мне любопытно, почему это не обновляется.
Я приложил два примера проектов с минимальной настройкой, которые повторяют эту проблему, когда пункт назначения NavigationLink не получает обновления при изменении данных. Один с привязкой, один с более простыми переданными свойствами.
Пример проекта # 1 с привязкой — Dropbox
Пример проекта # 2 без привязки — Dropbox
Код # 1:
//
// ContentView.swift
// Form Updating Example
//
// Created by Sahand Nayebaziz on 12/10/20.
//
import SwiftUI
struct ContentView: View {
@State var isPresentingMainView = false
@State var favoriteFood: FoodType = .bagel
var body: some View {
VStack {
Button(action: { isPresentingMainView = true }, label: {
Text("Present Main View")
})
}
.fullScreenCover(isPresented: $isPresentingMainView) {
MainView(favoriteFood: $favoriteFood)
}
}
}
struct MainView: View {
@Binding var favoriteFood: FoodType
var body: some View {
NavigationView {
HStack {
Spacer()
Text(favoriteFood.emoji)
.font(.title)
.foregroundColor(.secondary)
Spacer()
NavigationView {
Form {
List {
ForEach(["SomethingRepresentingShowingFood"], id: .self) { _ in
NavigationLink(
destination: makeDetail(),
label: {
Text("Food Randomizer")
})
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: 350)
}
.navigationTitle("Main")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
func makeDetail() -> some View {
Form {
ForEach(FoodType.allCases) { foodType in
Button(action: { favoriteFood = foodType }, label: {
HStack {
Text(foodType.emoji)
Spacer()
if favoriteFood == foodType {
Image(systemName: "checkmark")
}
}
})
}
}
}
}
enum FoodType: String, Identifiable, CaseIterable {
case bagel, pizza, broccoli
var id: String { rawValue }
var emoji: String {
switch self {
case .bagel: return "🥯"
case .pizza: return "🍕"
case .broccoli: return "🥦"
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
MainView(favoriteFood: .constant(.bagel))
MainView(favoriteFood: .constant(.bagel))
.makeDetail()
}
}
}
Код # 2:
//
// ContentView.swift
// Form Updating Example
//
// Created by Sahand Nayebaziz on 12/10/20.
//
import SwiftUI
struct ContentView: View {
@State var isPresentingMainView = false
@State var favoriteFood: FoodType = .bagel
var body: some View {
VStack {
Button(action: { isPresentingMainView = true }, label: {
Text("Present Main View")
})
}
.fullScreenCover(isPresented: $isPresentingMainView) {
MainView(currentFavoriteFood: favoriteFood, onUpdateFavoriteFood: { favoriteFood = $0 })
}
}
}
struct MainView: View {
let currentFavoriteFood: FoodType
let onUpdateFavoriteFood: (FoodType) -> Void
var body: some View {
NavigationView {
HStack {
Spacer()
Text(currentFavoriteFood.emoji)
.font(.title)
.foregroundColor(.secondary)
Spacer()
NavigationView {
Form {
List {
ForEach(["SomethingRepresentingShowingFood"], id: .self) { _ in
NavigationLink(
destination: makeDetail(),
label: {
Text("Food Randomizer")
})
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: 350)
}
.navigationTitle("Main")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
func makeDetail() -> some View {
Form {
ForEach(FoodType.allCases) { foodType in
Button(action: { onUpdateFavoriteFood(foodType) }, label: {
HStack {
Text(foodType.emoji)
Spacer()
if currentFavoriteFood == foodType {
Image(systemName: "checkmark")
}
}
})
}
}
}
}
enum FoodType: String, Identifiable, CaseIterable {
case bagel, pizza, broccoli
var id: String { rawValue }
var emoji: String {
switch self {
case .bagel: return "🥯"
case .pizza: return "🍕"
case .broccoli: return "🥦"
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
MainView(currentFavoriteFood: .bagel, onUpdateFavoriteFood: { _ in })
MainView(currentFavoriteFood: .bagel, onUpdateFavoriteFood: { _ in })
.makeDetail()
}
}
}
Комментарии:
1. Привязка плохо работает в иерархиях глубокого просмотра или между разными иерархиями. Я рекомендую использовать модель представления с шаблоном ObservableObject / ObservedObject вместо состояния / привязки для сценариев, подобных вашему.
2. Тем не менее, я все еще ищу дополнительную информацию об этом. Потому что это работает, если вы меняете форму на VStack, а состояние / привязка теоретически должны работать, не так ли?
3. Я обновил это другим примером проекта, который не использует привязку, все еще видя то же поведение. Я начинаю думать, может быть, это ожидаемое поведение NavigationLink?