首页 > 解决方案 > 模型更新前 SwiftUI 视图重新渲染

问题描述

为什么我的视图在更新完成之前呈现模型更新?或者至少看起来是这样。看起来,它是根据我模型的先前值重新绘制的。

我在做什么:当您点击一个人单元格时,它会将那个人标记为“已选中”,然后将前景色更改为绿色。如果未选择它们,则前景色为白色。

发生了什么:重要的是要注意这并不总是发生。似乎是随机的。但偶尔,当我点击一个人的单元格时,颜色不会改变。而且我已经深入挖掘,那是因为它(看起来)在我的PeopleStore模型对象中的值发生变化之前重绘了视图。

这是代码:

struct PersonCell: View {
    
    @EnvironmentObject var peopleStore: PeopleStore
    var personId: UUID
    
    var person: Person {
        return peopleStore.people.filter({ $0.id == personId }).first!
    }
    
    var body: some View {
        Button(action: {
            print("Tapped Cell: " + peopleStore.personName(id: personId))
            peopleStore.toggleSelectionOfPerson(with: personId)
        }) {
            GeometryReader { geo in
                ZStack {
                    Rectangle().foregroundColor(peopleStore.personIsSelected(id: personId) ? .tGreen : .white).cornerRadius(geo.size.width / 2).frame(height: 70)

当我点击一个单元格时会发生以下情况:

class PeopleStore: ObservableObject {
    @Published var people : [Person]
    
    init(people: [Person]){
        self.people = people
    }
    
    func personIsSelected(id: UUID) -> Bool {
        let index = people.firstIndex(where: { $0.id == id })!
        print("ZSTACK: \(people[index].name) is selected: \(people[index].isSelected.description)")
        return people[index].isSelected
    }
    
    func toggleSelectionOfPerson(with id: UUID) {
        let index = people.firstIndex(where: { $0.id == id })!
        print("BEFORE \(people[index].name) is selected: \(people[index].isSelected.description)")
        people[index].isSelected = !people[index].isSelected
        print("AFTER \(people[index].name) is selected: \(people[index].isSelected.description)")
    }

当我查看我的控制台时,您可以看到在我选择一个人后,视图会重新绘制,但它具有以前的值(在这种情况下,我点击了 Alexandra 单元格):

Tapped Cell: Alexandra R.
BEFORE Alexandra R. is selected: false
ZSTACK: Jose G. is selected: false
ZSTACK: Jimmy T. is selected: false
ZSTACK: Alexandra R. is selected: false
ZSTACK: Dominic R. is selected: false
AFTER Alexandra R. is selected: true
ZSTACK: Franny E. is selected: false
ZSTACK: Jon B. is selected: true

ZStack:在重新绘制单元格时打印。正如你所看到的,Alexandra 被选中:当我期望它为真时,出现了假。

重新抽签的时间似乎有点不对劲,而且似乎是随机的。我错过了什么?

编辑:人员结构

struct Person: Identifiable {
    let id = UUID()
    var name: String
    var photo: Image
    var isFavorite: Bool
    var dateFavorited = Date()
    var isSelected: Bool = false
}

编辑 2:如果有帮助,PersonCell 是一个名为 PersonList 的子视图,它使用 ForEach 生成 PersonCell。

struct PeopleList: View {
    @EnvironmentObject var peopleStore: PeopleStore
    var title: String {
        return isFavorites ? "Favorites" : "All Employees"
    }
    var isFavorites: Bool

    var body: some View {
        LazyVStack(alignment: .leading, spacing: 15) {
            Text(title).padding(.top, 18).padding(.horizontal, 5).font(.regular())
            if isFavorites {
                ForEach(peopleStore.people.sorted(by: { $0.dateFavorited < $1.dateFavorited }).filter({ $0.isFavorite })) { person in
                    PersonCell(personId: person.id).frame(height: 70.0)
                }

标签: iosswiftswiftui

解决方案


我不确定你到底出了什么问题,我已经测试了你的代码,它工作得很好。我所做的唯一更改是设置var isFavorites: Bool = truePeopleList因为我不知道您如何在代码中切换该属性。所以,我为它提供了一个常量值。

下面是代码-:

struct Person: Identifiable {
    let id = UUID()
    var name: String
    var photo: Image
    var isFavorite: Bool
    var dateFavorited = Date()
    var isSelected: Bool = false
}

class PeopleStore: ObservableObject {
    @Published var people : [Person]
    
    init(people: [Person]){
        self.people = people
    }
    
    func personIsSelected(id: UUID) -> Bool {
        let index = people.firstIndex(where: { $0.id == id })!
        print("ZSTACK: \(people[index].name) is selected: \(people[index].isSelected.description)")
        return people[index].isSelected
    }
    
    func personName(id: UUID) -> String {
        let index = people.firstIndex(where: { $0.id == id })!
        return people[index].name
    }
    
    func toggleSelectionOfPerson(with id: UUID) {
        let index = people.firstIndex(where: { $0.id == id })!
        print("BEFORE \(people[index].name) is selected: \(people[index].isSelected.description)")
        people[index].isSelected = !people[index].isSelected
        print("AFTER \(people[index].name) is selected: \(people[index].isSelected.description)")
    }
}

struct PeopleList: View {
    @EnvironmentObject var peopleStore:PeopleStore
    
    var title: String {
        return isFavorites ? "Favorites" : "All Employees"
    }
    var isFavorites: Bool = true
    
    var filteredPersons:[Person]{
        peopleStore.people.sorted(by: { $0.dateFavorited < $1.dateFavorited }).filter({ $0.isFavorite })
    }
    
    var body: some View {
        LazyVStack(alignment: .leading, spacing: 15) {
            Text(title).padding(.top, 18)
            if isFavorites {
                ForEach(filteredPersons) { person in
                    PersonCell(personId: person.id).frame(height: 70.0)
                }
            }
        }
    }
}

struct PersonCell: View {
    
    @EnvironmentObject var peopleStore:PeopleStore
    var personId: UUID
    
    var body: some View {
        
            Button(action: {
                print("Tapped Cell: " + peopleStore.personName(id: personId))
                peopleStore.toggleSelectionOfPerson(with: personId)
            }) {
                GeometryReader { geo in
                    ZStack {
                        Rectangle().foregroundColor(peopleStore.personIsSelected(id: personId) ? .green : .gray).cornerRadius(geo.size.width / 2).frame(height: 70)
                    }
                }
            
        }
    }
}

主视图-:

import SwiftUI

@main
struct Test: App {
    
    @StateObject var store:PeopleStore
    
    init() {
        let person = Person(name: "foo", photo: Image(""), isFavorite: true)
        let person1 = Person(name: "foo1", photo: Image(""), isFavorite: true)
        let person2 = Person(name: "foo2", photo: Image(""), isFavorite: true)
        let obj = PeopleStore(people: [person,person1,person2])
        
        _store = StateObject(wrappedValue: obj)
    }
    
    var body: some Scene {
            WindowGroup {
                PeopleList().environmentObject(store)
            }
        }
}

输出-:

Tapped Cell: foo
BEFORE foo is selected: false
AFTER foo is selected: true
ZSTACK: foo2 is selected: false
ZSTACK: foo is selected: true
ZSTACK: foo1 is selected: false

注意-:我已经测试了多次,没有发现所选单元格不改变颜色的任何问题。


推荐阅读