Meu problema é que, quando edito um modelo, a interface do usuário em BeginWorkoutView não atualiza corretamente. Acredito que estou usando @ObservableObject/@Published/@Mainactor incorretamente, mas não consigo descobrir como. Todos os tutoriais parecem usar o padrão que estou usando atualmente, e funciona para eles.
Tenho uma página principal, BeginWorkoutView. Nela, você pode criar um novo treino ou criar modelos de treino. Você também pode ver todos os modelos criados e clicar para editá-los.
aqui está uma versão simplificada disso:
struct BeginWorkoutView: View {
@ObservedObject var templateManager = TemplateManager.shared
var body: some View {
NavigationView{
ScrollView{
Section("Start from scratch"){
HStack{
NavigationLink("New Workout"){
//new workout page
}
NavigationLink("New Template"){
CreateTemplateView( viewModel: CreateTemplateViewModel(), editMode: true)
}
}
}
Section("Start from template") {
ForEach(templateManager.templates, id: \.id.self) { template in
TemplateRowView(template: template)
}
}
}
}
}
}
Gerencio meus dados em um gerenciador de modelos. Consigo adicionar novos treinos e eles aparecem na minha página BeginWorkoutView sem problemas. Como eu disse, quando edito o título, ele não é atualizado na BeginWorkoutView. Pelo que posso perceber, a lista de modelos do templateManager ESTÁ sendo atualizada, mas isso não parece acionar a interface do usuário. Atualmente, estou usando a estrutura de objeto @mainactor/@Observable porque quero oferecer suporte a versões anteriores ao iOS 17.
@MainActor
final class TemplateManager: ObservableObject {
@Published private(set) var templates: [WorkoutTemplate] = []
static let shared = TemplateManager()
init(){
getTemplates()
}
func getTemplates() {
//gets templates from firebase
}
func updateTemplates(template: WorkoutTemplate) {
Task{
do {
//updates templates in firebase
//HERE is where the UI should be getting the signal to update. Obviously spot replacing the template didnt work, but replacing the entire thing wont work either.
var updated = templates
if let index = updated.firstIndex(where: { $0.id == template.id }) {
updated[index] = template
} else {
updated.append(template)
}
self.templates = updated
} catch {
print("unable to update templates")
}
}
}
}
Seja criando um novo modelo ou editando um, eu uso createtemplateview/createtemplateviewmodel. Aqui estão versões simplificadas, basicamente apenas com as chamadas de função. Não acredito que o problema esteja aqui, mas estou adicionando-as caso sejam úteis.
@MainActor
final class CreateTemplateViewModel: ObservableObject {
@Published var template: WorkoutTemplate
var templateManager = TemplateManager.shared
init(template: WorkoutTemplate) {
self.template = template
}
init(){
self.template = WorkoutTemplate(name: "Workout Template", workoutModules: [])
}
func addTemplateList() {
templateManager.updateTemplates(template: template)
}
func removeTemplateList() {
templateManager.removeTemplateList(templateId: template.id)
}
}
struct CreateTemplateView: View {
@StateObject var viewModel: CreateTemplateViewModel
var body: some View {
ScrollView{
StringTextField(preview: "Template Name", $viewModel.template.name )
.padding()
//shows editable modules for the template
Button("save"){
viewModel.addTemplateList()
}
}
}
}
Existem "truques" por aí, como usar notificações ou combinar para notificar a interface do usuário para atualização, mas acredito que isso deve funcionar se feito corretamente. Estou escrevendo isso principalmente para aprender essas estruturas de dados e estou com dificuldade para entender isso, então definitivamente gostaria de chegar ao fundo da questão em vez de aplicar uma correção paliativa.
Você está perto, substitua
@StateObject
por@EnvironmentObject
, por exemploE remova,
CreateTemplateViewModel
pois o@State
já faz esse trabalho.No seu
App
fazer:Em suas prévias faça:
Obrigado a todos, mas especialmente ao @malhal, porque finalmente consegui fazer funcionar com a correção dele! No entanto, precisei fazer algumas alterações na correção e queria atualizar para quem tiver um problema semelhante.
Primeiro, usei environmentObject e reestruturei meu BeginWorkoutView, CreateTemplateView e ContentView conforme sugerido.
Originalmente, eu tinha meu WorkoutTemplate como uma classe, mas o nome NÃO foi publicado. Tentei usar uma struct, mas não obtive sucesso. Vi uma postagem diferente (que linkarei se conseguir encontrá-la novamente) que aponta que, mesmo que uma visualização tenha um objeto que reaja a mudanças de estado (como meu objeto de ambiente em beginWorkoutView), a subvisualização ainda pode não reagir. BeginWorkoutView tem uma lista de templateRowViews que mostram os nomes dos modelos. Originalmente, a variável de modelo NÃO era um observedobject, então a atualizei. Também decidi alterar o WorkoutTemplate de volta para uma classe/conformidade com ObservableObject. Aqui estão as alterações/reversões adicionais.
Quero ressaltar que, na tela principal, onde você vê as visualizações dos modelos, o nome é a única coisa que você vê para cada modelo, e é por isso que é a única coisa que publiquei.
Espero que isso ajude! Sei que há muita coisa aqui e muita coisa que não incluí, então, por favor, me avise se eu puder esclarecer algo ou se você tiver um problema semelhante e quiser ver mais código!