Swiftui listRowInsets 在添加到空数组时进行意外更改

Swiftui listRowInsets makes unexpected changes when adding to an empty array

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

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

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

列表应该是这样的...

Proper rendering of list

下面是意想不到的错误渲染...

Improper rendering after deleting the scorecard and adding a new one

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

import SwiftUI

struct ContentView: View {

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

var sortedScorecards: [Scorecard] {
    get {
        scorecards.sorted {
            [=10=].date > .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{[=10=].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
}

确实似乎与if/else声明有关。当 List 从视图层次结构中删除然后重新添加时,插图似乎被弄乱了。

插入的数量对我来说看起来很熟悉 -- 就像如果前面有一个删除按钮时应该插入的数量一样。因此,凭直觉我添加了一个显式调用以将编辑模式设置为 .inactive。这似乎有效。

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

@Environment(\.editMode) private var editMode

然后,在您的 deleteScorecard(scorecard:Scorecard):

func deleteScorecard(scorecard:Scorecard) {
    scorecards = scorecards.filter{[=11=].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
        }
    }
    
}