SwiftUI NavigationSplitView не меняет детали/пункт назначения

Подробный вид/назначение NavigationSplitView не меняется при выборе другого элемента на боковой панели.

Учитывая модель данных Swift

@Model final class MyModel {
    var text: String
    init(text: String) {self.text = text}
}

И NavigationSplitView, который должен отображать все эти модели плюс представление редактирования.

struct MyModelEditView: View {
    @Environment(\.modelContext) var modelContext
    @State var myModel: MyModel
    var body: some View {
       TextField("Edit Text", text: $myModel.text)
    }
}
 
struct ContentView: View {
    @Environment(\.modelContext) var modelContext
    @Query var models: [MyModel]
    var body: some View {
        NavigationSplitView {
           List {
               ForEach(myModels) { model in
                   NavigationLink {
                      MyModelEditView(myModel: model)
                          .modelContext(modelContext)
                   } label: {
                       Text(model.text)
                   }
               }
           }
        } detail: {
            Text("Tap plus for a new model")
        }
        // we pretend that here is a button in a toolbar that adds a new model
    }       

При первом нажатии на любой элемент в списке отображается подробный вид этого элемента! Но при нажатии на другую модель подробное представление просто остается там, где оно было с предыдущей моделью.

Я также пытался создать @State var selectedModel: MyModel и используя инициализатор списка List(selection: $selectedModel) , помечая каждый элемент в ForEach (.tag(model)) вместо использования NavigationLink, а затем в блоке подробностей:

detail: {
    if let selectedModel {
        MyModelEditView(myModel: myModel)
            .modelContext(modelContext)
    } else {
Text("Tap plus for a new model")
    }
}

... Но и это не помогло.

Я обнаружил, что .navigationDestination(for:) ограничен NavigationStack, поэтому понятия не имею, как создать работающий стек навигации для моих моделей SwiftData.

! Обратите внимание, что это не характерно для моделей SwiftData как объектов, это также происходит с любыми структурами или классами. Я пробовал это со структурой, содержащей только одну строку, например с классом MyModel.

Измените переменную состояния на let или Bindable.

lorem ipsum 14.08.2024 23:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я нашел решение после долгих проб и ошибок. Решением было просто сделать это в списке:

ForEach(myModels) { model in 
    NavigationLink {
        MyModelEditView(myModel: model)
            .modelContext(modelContext)
            .id(model.id) // <-- added .id(model.id)
    } label: {
        Text(model.text)
    }
}

Судя по тому, что я прочитал, .id заставляет каждый раз заново рисовать подробное представление.

Я не уверен, что это самый элегантный способ сделать это, но он работает.

Это не исправление, а обходной путь. State создает глубокую копию Observable. Привязываемый нет. Кроме того, государство всегда должно быть частным.

lorem ipsum 19.08.2024 21:56
Ответ принят как подходящий

Как было предложено в комментариях, @Bindable устранит проблему без использования другого модификатора представления.

struct MyModelEditView: View {
    @Bindable var myModel: MyModel
    var body: some View {
       TextField("Edit Text", text: $myModel.text)
    }
}

Я попробовал, и это действительно решает проблему, спасибо. Хотя мне кажется немного странным, что оболочка свойства в целевом представлении меняет поведение NavigationSplitView.

diepzee 15.08.2024 11:41

Речь идет о том, как SwiftUI получает уведомление об обновлении. Состояние является внутренним для представления, в котором оно объявлено, но Bindable уведомит родительское представление.

Joakim Danielson 15.08.2024 11:43

Другие вопросы по теме