【问题标题】:Swiftui listRowInsets makes unexpected changes when adding to an empty arraySwiftui listRowInsets 在添加到空数组时会发生意外更改
【发布时间】:2021-09-20 20:23:49
【问题描述】:

我在这里重新创建了一个最小的可重现示例。我正在使用 Xcode 13 beta 5。

在我的项目中,我使用 List 和 ForEach 循环呈现记分卡列表。我正在使用滑动操作来删除记分卡。在我删除记分卡,然后添加记分卡后,列表会在前缘使用额外的填充或 listRowInset 呈现。

此外,在 ContentView 中,我使用 if/else 语句在记分卡数组为空时呈现消息。我认为这是错误发生的地方,但我真的不知道如何解决它。

列表应该是这样的......

Proper rendering of list

下面是出乎意料的错误渲染...

Improper rendering after deleting the scorecard and adding a new one

要重新创建错误,首先通过从后缘滑动删除记分卡,然后单击顶部的添加记分卡,您将在记分卡的前缘看到额外的空白。我将附上下面的代码...提前感谢您的帮助!

import SwiftUI

struct ContentView: View {

@State var scorecards = [
    Scorecard(date: Date()),
    
]

var sortedScorecards: [Scorecard] {
    get {
        scorecards.sorted {
            $0.date > $1.date
        }
    }
    set {
        scorecards = newValue
    }
}
var body: some View {
    VStack {
        
        Button {
            scorecards = [
                Scorecard(date: Date()),
                
            ]
            
        } label: {
            Text("Add Scorecard")
        }
        
        
        if scorecards.count < 1 {
            
            Spacer()
            
            HStack{
                Spacer()
                Text("No previous scorecards. Start a scorecard!")
                    .font(.headline)
                    .padding(20)
                    .multilineTextAlignment(.center)
                Spacer()
            }
            Spacer()
        } else {
            List {
                ForEach(sortedScorecards, id:\.self) { scorecard in
                    if #available(iOS 15.0, *) {
                        
                        Button {
                            
                        } label: {
                            Card(scorecard: scorecard)
                            
                        }.buttonStyle(.plain)
                            .listRowInsets(.init(top:7.5,
                                                 leading:0,
                                                 bottom:7.5,
                                                 trailing:0))
                            .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                                Button("Delete Scorecard", role: .destructive) {
                                    deleteScorecard(scorecard: scorecard)
                                }
                            }
                        
                        
                    } else {
                        // Fallback on earlier versions
                    }
                }
            }
            .onAppear {
                UITableView.appearance().backgroundColor = .black
            }
        }
    }
    
}

    func deleteScorecard(scorecard:Scorecard) {
        scorecards = scorecards.filter{$0.id != scorecard.id}
    }
}



struct Card: View {
    
    var scorecard:Scorecard
    
    func scorecardDate(date:Date) -> String {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .short
        return formatter.string(from: date)
    }
    
    var body:some View {
        
        ZStack {
            Rectangle()
                .foregroundColor(Color.blue)
                .frame(height: 25)
            
            Text(scorecardDate(date:scorecard.date))
            
        }
    }
}
    
    
struct Scorecard:Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    var id = UUID().uuidString
    var date:Date
}

【问题讨论】:

  • 你能创建一个minimal reproducible example吗?没有其他人可以编译您包含的代码,因为它依赖于您未包含的类型。
  • 好主意@jnpdx,让我试试!谢谢
  • @jnpdx 我已经编辑了问题并添加了一个最小可重现示例的代码。感谢您的建议!

标签: swift listview foreach swiftui swiftui-list


【解决方案1】:

确实似乎与if/else 语句有关。当 List 从视图层次结构中删除然后重新添加时,插图似乎变得一团糟。

插入的数量对我来说看起来很熟悉——就像在前导侧有一个删除按钮时应该插入的数量一样。所以,凭直觉,我添加了一个显式调用来将编辑模式设置为.inactive。这似乎行得通。

在您的视图顶部,添加以下内容:

@Environment(\.editMode) private var editMode

那么,在你的deleteScorecard(scorecard:Scorecard)

func deleteScorecard(scorecard:Scorecard) {
    scorecards = scorecards.filter{$0.id != scorecard.id}
    editMode?.wrappedValue = .inactive //<-- here
}

这似乎明确地关闭了编辑模式,然后避免了将List 添加回视图层次结构时的错误。


更新——次要解决方案。

这是我想出的另一个解决方案,因为我的代码适用于您的示例代码,但不适用于您的应用。在此版本中,List 始终包含在视图层次结构中,但如果没有任何项目,则框架的高度设置为 0。由于您设置的背景颜色,可能会产生副作用,这是我最初没有包含此内容的原因之一,但由于最初的解决方案并不理想,现在值得包括:

var body: some View {
    VStack {
        
        Button {
            scorecards = [
                Scorecard(date: Date()),
                
            ]
            
        } label: {
            Text("Add Scorecard")
        }
        
        
        if scorecards.count < 1 {
            
            VStack {
                Spacer()
                
                HStack{
                    Spacer()
                    Text("No previous scorecards. Start a scorecard!")
                        .font(.headline)
                        .padding(20)
                        .multilineTextAlignment(.center)
                    Spacer()
                }
                Spacer()
            }
            
        }
        
        List {
            ForEach(sortedScorecards, id:\.self) { scorecard in
                if #available(iOS 15.0, *) {
                    
                    Button {
                        
                    } label: {
                        Card(scorecard: scorecard)
                        
                    }.buttonStyle(.plain)
                        .listRowInsets(.init(top:7.5,
                                             leading:0,
                                             bottom:7.5,
                                             trailing:0))
                        .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                            Button("Delete Scorecard", role: .destructive) {
                                deleteScorecard(scorecard: scorecard)
                            }
                        }
                    
                    
                } else {
                    // Fallback on earlier versions
                }
            }
        }
        .frame(maxHeight: scorecards.count == 0 ? 0 : .infinity)
        .onAppear {
            UITableView.appearance().backgroundColor = .black
        }
    }
    
}

【讨论】:

  • 您的解决方案确实适用于我的示例代码,但不适用于我的项目。我想可能是我的项目中的 bc,我使用了 ViewModel 中的已发布属性数组,而不是状态属性数组。我重构了我的示例代码以使用与我的项目相同的项目结构,并且您的解决方案仍然有效,但由于某种原因它不适用于我的项目。我得继续试验
  • 查看我的更新以获得另一个解决方案。如果这些都不起作用,我想最好尝试包含一个更准确地反映我的原始解决方案似乎不适合您的实际项目环境的示例。
  • 第二个解决方案做到了!太感谢了!非常感谢您花时间提供两个解决方案并解释您所做的事情。非常聪明和有创意的解决方案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-27
  • 2022-01-04
  • 1970-01-01
相关资源
最近更新 更多