Я столкнулся со странным поведением .refreshable, которое иллюстрируется приведенным ниже кодом:
struct ContentView: View {
@State private var cntrl=ContentViewController()
@State private var counter=0
var body: some View {
VStack {
Button {
counter+=1
cntrl.setValue(counter)
} label: {
Text("Increase value")
}
Text("Selected value local: \(counter)")
Text("Selected value cntrl: \(cntrl.model.selectedValue)")
NewView(model: cntrl.model)
}
}
}
struct NewView:View {
let model:Model
@State private var valueSetByFunc:Int=0
var body: some View {
VStack {
Button {
whatIsTheValue()
} label: {
Text("Call func")
}
List {
Text("Value set by func: \(valueSetByFunc)")
}.refreshable {
whatIsTheValue()
}
}
}
private func whatIsTheValue() {
valueSetByFunc=model.selectedValue
}
}
struct Model {
var selectedValue=0
mutating func setValue(_ value:Int) {
self.selectedValue=value
}
}
@Observable
class ContentViewController {
var model=Model()
func setValue(_ value:Int) {
self.model.setValue(value)
}
}
Здравствуйте, я пытаюсь понять следующее поведение .refreshable. Если вы попробуете опубликованный код и нажмете «увеличить значение», вы получите правильное обновление соответствующих полей. Если вы затем нажмете кнопку «вызов функции», которая обновляет значение SetByFunc, она правильно обновит значение в «NewView», вызвав WhatIsTheValue(). Но если вы «потянете, чтобы обновить», вызывая ту же функцию WhatIsTheValue(), внезапно возвращаемое значение станет другим.
Кто-нибудь знает, правильное ли это поведение?
Возможно, причина этого непоследовательного поведения заключается в том, что .refreshable предназначен для использования в асинхронном контексте, например, для вызова API-интерфейса отдыха или выборки из базы данных.
Но, по моему мнению, это все еще не объясняет, почему я не могу получить доступ к «let» «модели» представления. Проблема в том, что я хочу вызвать действие с помощью .refresh на основе модели. Итак, «обновление» смотрит на модель и запрашивает данные с сервера на основе состояния модели, что сейчас кажется невозможным... Если я когда-нибудь получу ответ от Apple, я напишу здесь.





Вам нужно передать Binding своему NewView.
Попробуйте этот подход, чтобы обновить cntrl.model во всех представлениях:
struct ContentView: View {
@State private var cntrl = ContentViewController()
@State private var counter = 0
var body: some View {
VStack {
Button {
counter += 1
cntrl.setValue(counter)
} label: {
Text("Increase value")
}
Text("Selected value local: \(counter)")
Text("Selected value cntrl: \(cntrl.model.selectedValue)")
NewView(model: $cntrl.model) // <-- here $
}
}
}
struct NewView:View {
@Binding var model: Model // <-- here
@State private var valueSetByFunc: Int = 0
var body: some View {
VStack {
Button {
whatIsTheValue()
} label: {
Text("Call func")
}
List {
Text("Value set by func: \(valueSetByFunc)")
}.refreshable {
whatIsTheValue()
}
}
}
private func whatIsTheValue() {
valueSetByFunc = model.selectedValue
}
}
Альтернатива, которая также работает, — сохранить исходный код, но изменить Model на:
@Observable class Model {
var selectedValue = 0
func setValue(_ value: Int) {
selectedValue = value
}
}
Если это решение, то почему оно сработало при нажатии кнопки в NewView?
why did it work when the button was pressed in NewView, я точно не знаю, почему вызов функции действия кнопки ведет себя иначе, чем вызов той же функции в модификаторе представления. Я подозреваю, что это связано с асинхронной природой .refreshable из документации «... асинхронный обработчик, который SwiftUI выполняет, когда пользователь запрашивает обновление».
Спасибо за ваши комментарии. Поскольку «Модель» в исходном коде представляет собой структуру, и сделать ее классом на данный момент невозможно, я подожду еще немного, если кто-нибудь предложит объяснение. Я отправил это как ошибку в Apple. «Кажется» неправильным, что вызов одной и той же функции, запрашивающей «let value» из разных частей одного и того же представления, приводит к разным результатам. Но спасибо за ваше время.
ок, заметьте, мой ответ хорошо работает с struct Model
Привет, служба поддержки Workdog, Украина. Я принял ваш ответ с помощью @Binding, поскольку он решает проблему. Спасибо за ваш обзор. Посмотрим, поступит ли от Apple отзыв о том, что такое поведение предназначено для .refreshable. Спасибо. Либор
Хм,
NewViewне перерисовывался при обновлении, однако @State был сброшен до нуля, аmodel.selectedValueвсе еще имеет некоторое значение. Я не могу дать вам объяснение, но ответ @workingdog может это исправить.