SwiftUI 列表未使用 ForEach 正确更新

Posted

技术标签:

【中文标题】SwiftUI 列表未使用 ForEach 正确更新【英文标题】:SwiftUI List Not Updating Correctly with ForEach 【发布时间】:2020-05-27 16:39:46 【问题描述】:

我正在尝试使用带有两个 ForEach 循环的列表来创建收藏夹功能。当我单击按钮时,项目移动到正确的部分,但按钮图像和功能没有更新。我有什么遗漏或做错了吗?

如果我导航到另一个视图并返回,列表最初会正确呈现,但如果我单击按钮仍会显示相同的行为。

如果我使用两个具有完全相同设置的单独列表,一切正常,但在 UI 中看起来很奇怪,因为即使列表中没有项目,每个列表也占用相同数量的空间。

或者,有没有办法强制列表重绘作为按钮点击的一部分?

谢谢!

import SwiftUI

struct BusinessListView: View 

    @EnvironmentObject var state: ApplicationState

    var body: some View 
        VStack

            List 
                Section(header: Text("Favorites")) 

                    if(state.favorites.count == 0)

                        Text("No Favorites")
                    
                    else 
                        ForEach(state.favorites, id: \.self)  favorite in
                            HStack
                                Button(
                                    action: ,
                                    label:  Image(systemName: "heart.fill").foregroundColor(.red) 
                                )
                                    .onTapGesture  self.toggleFavorite(business: favorite)
                                Text("\(favorite.name)")
                            
                        
                    
                

                Section(header: Text("Other Businesses")) 
                    ForEach(state.others, id: \.self)  business in
                        HStack
                            Button(
                                action: ,
                                label:  Image(systemName: "heart") 
                            )
                                .onTapGesture  self.toggleFavorite(business: business)
                            Text("\(business.name)")
                        
                    
                
            
        
    

    func toggleFavorite(business: Business)

        if(state.favorites.contains(business))

            self.state.favorites = self.state.favorites.filter $0 != business

            self.state.others.append(business)
        
        else

            self.state.others = self.state.others.filter $0 != business

            self.state.favorites.append(business)
        

        sortBusinesses()
    


    func sortBusinesses()

        self.state.favorites.sort 
            $0.name < $1.name
        

        self.state.others.sort 
            $0.name < $1.name
        
    

【问题讨论】:

如果你给我们一个可重现的可编译的例子会很棒......***.com/help/minimal-reproducible-example @Chris - 如果您想看一下,这里是完整的代码。除了目前,它并没有做很多事情。 github.com/benbaran/tippler 。我将尝试创建一个不需要进行网络调用等的示例。 @Chris - 这是一个最小的可重现示例:github.com/benbaran/swift-ui-favorites-list。 期待回答 -> 我已经更正了 @Chris - 感谢您的帮助!但是,上面的答案实际上并不起作用。项目移动时,动作和图标不会改变。它看起来就是这样,因为 toggleFavorite() 方法将处理这两种情况。 【参考方案1】:

更新的答案,有效,但是...

1) 诀窍是每当您将“单元格”从收藏更改为非收藏时更改 id -> 我假设 Apple 使用 UITableView - 逻辑与 dequeueResubleCell 并且如果 id 相同,则不会更新,而只是重复使用/复制,因此心脏没有改变

2) 我现在在收藏夹更改时随机更改 id - 你必须在那里考虑一个更好/更清洁的解决方案。

struct BusinessListView: View 

    @EnvironmentObject var state: ApplicationState

    var body: some View 
        VStack

            List 
                Section(header: Text("Favorites")) 

                    if(state.favorites.count == 0)

                        Text("No Favorites")
                    
                    else 
                        ForEach(state.favorites, id: \.self)  favorite in
                            HStack
                                Button(
                                    action: 
                                        self.toggleFavorite(business: favorite)
                                ,
                                    label: 
                                        HStack 
                                            Image(systemName: "heart.fill").foregroundColor(.red)
                                            Text("\(favorite.name)")
                                        
                                
                                ).id(favorite.id)
                            
                        
                    
                

                Section(header: Text("Other Businesses")) 
                    ForEach(state.others, id: \.self)  business in
                        HStack
                            Button(
                                action: 
                                    self.toggleFavorite(business: business)
                            ,
                                label: 
                                    HStack 
                                        Image(systemName: "heart")
                                        Text("\(business.name)")
                                    
                            
                            )
                            .id(business.id)
                        
                    
                
            
        
    

    func toggleFavorite(business: Business)

        if(state.favorites.contains(business))

            self.state.favorites = self.state.favorites.filter $0 != business

            self.state.others.append(business)

            if let index = self.state.others.index(of: business) 
                self.state.others[index].id = Int.random(in: 10000...50000)
            
        
        else

            self.state.others = self.state.others.filter $0 != business

            self.state.favorites.append(business)

            if let index = self.state.favorites.index(of: business) 
                self.state.favorites[index].id = Int.random(in: 10000...50000)
            
        

        sortBusinesses()
    


    func sortBusinesses()

        self.state.favorites.sort 
            $0.name < $1.name
        

        self.state.others.sort 
            $0.name < $1.name
        
    

【讨论】:

嗯。这似乎对我也不起作用。你能看一下最小的例子吗?我在其中看到的是,即使在列表项移动之后,动作和颜色也会保持不变。也许我只是在某个地方犯了一个愚蠢的错误? 你指的是什么颜色? 心应该变成一个充满红色的心。 基本上我看到的行为是该项目移动到收藏夹部分,但图标和操作没有改变。如果我导航到另一个视图并返回,它会正确更新。 谢谢!!!我觉得 SwiftUI 应该处理这种情况。我将发布到开发者论坛,看看他们是否有兴趣修复它:)【参考方案2】:

您是否确认.onTapGesture 正在被呼叫?或者,您可以将self.toggleFavorite(business: favorite) 放在按钮的操作内。

                            Button(
                                action:  self.toggleFavorite(business: business) ,
                                label:  Image(systemName: "heart") 
                            )

【讨论】:

是的,如果我添加一个打印语句,我可以看到它被调用了。但它始终是添加收藏按钮的 onTapGesture。

以上是关于SwiftUI 列表未使用 ForEach 正确更新的主要内容,如果未能解决你的问题,请参考以下文章

在 NavigationView 中清除 SwiftUI 列表未正确返回默认值

带有 Xcode 11 beta 7 的 SwiftUI 未更新 List / ForEach 的内容

如果列表行在 SwiftUI 中包含文本字段,则无法从 foreach 中删除元素

在 SwiftUI 中的 ForEach 中绑定

SwiftUI:“@Published”属性更改时未刷新“带有组的动态列表”

使用 ForEach 插入的 SwiftUI 视图未随动画更新