SwiftUI - 警报

问题描述 投票:0回答:1

我不确定这是否是一个错误,或者我是否做错了什么,因为我想在我的人没有项目数组时显示警报。当我删除项目时,会显示警报,但有时警报只会显示一秒钟,然后就会消失。有时,直到我按下按钮才会显示警报,但整个文本都会变成粗体。 我已经多次启动模拟器并对其进行了测试,但我找不到任何相关性。 我的猜测是,警报被激活或显示多次,但我不知道在哪里或为什么。

这是我的代码,警报位于“SectionRowView”中。

class Person: Identifiable, ObservableObject {
    let id = UUID()
    @Published var name: String
    @Published var item: [TodolistItem]
    
    init(name: String, item: [TodolistItem]) {
        self.name = name
        self.item = item
    }
}

class TodolistItem: Identifiable, ObservableObject {
    @Published var todoName: String
    @Published var priority: String
    
    init(todoName: String, priority: String) {
        self.todoName = todoName
        self.priority = priority
    }
}

struct Todolist: View {
    
    @State private var personen: [Person] = [
    Person(name: "Michi", item: [
        TodolistItem(todoName: "Reifenwechsel", priority: "Niedrig"),
        TodolistItem(todoName: "Irgendwas", priority: "Mittel")
    ]),
    Person(name: "Tina", item: [
        TodolistItem(todoName: "Haushalt", priority: "Dringend!")
    ])
    ]
    @State private var addSheet: Bool = false
    
    let navTitle: String
    let listInfo: ListInfo
    
    var body: some View {
        
        NavigationStack {
            List {
                ForEach(personen) { person in
                    SectionRowView(person: person)
                }
            }
            .listStyle(.sidebar)
        }
        .navigationTitle(navTitle)
        .navigationBarItems(trailing:
                                Button(action: { addSheet.toggle() }, label: {
            Image(systemName: "plus.circle")
        })
        )
        
        .sheet(isPresented: $addSheet) {
            AddTodoSectionView(personen: $personen)
                .presentationDetents([.fraction(0.4)])
        }
        
        .toolbarBackground(listInfo.backgroundColor.opacity(0.6), for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
    }
}

struct SectionRowView: View {
    
    @State private var isExpanded: Bool = true
    @State private var editSheet: Bool = false
    @State private var showAlert: Bool = false
    
    @ObservedObject var person: Person
    
    var body: some View {
        
        Section(isExpanded: $isExpanded) {
            ForEach(person.item) { item in
                HStack {
                    TodoRowView(item: item)
                    
                    Button { editSheet.toggle() } label: {
                        Image(systemName: "info.circle")
                    }
                    .buttonStyle(BorderlessButtonStyle())
                    .foregroundStyle(.primary)
                }
            }
            .onDelete { offSet in
                person.item.remove(atOffsets: offSet)
                if person.item.isEmpty {
                    showAlert = true
                }
            }

            .sheet(isPresented: $editSheet) {
                EditView()
            }
            
            Button {
                let newItem = TodolistItem(todoName: "Opfer", priority: "Niedrig")
                withAnimation {
                    person.item.append(newItem)
                }
            } label: {
                HStack {
                    Image(systemName: "plus")
                    Text("Neues Todo hinzufügen")
                }
                .foregroundStyle(.blue)
            }
            
        } header: {
            Text(person.name)
        }
        
        .alert("Person löschen?", isPresented: $showAlert) {
                    Button("Behalten", role: .cancel) {
                        showAlert = false
                    }
                    Button("Löschen", role: .destructive) {
                        showAlert = false
                    }
                } message: {
                    Text("Die Person hat alle Aufgaben erledigt, möchtest du das diese gelöscht wird?")
                }
    }
}

struct TodoRowView: View {
    
    @State private var priority: [String] = [
    "Niedrig", "Mittel", "Hoch", "Dringend!"
    ]
    
    @State private var isDone: Bool = false
    @State private var textFieldText: String = ""
    @ObservedObject var item: TodolistItem
    
    var body: some View {
        VStack {
            HStack {
                Button(action: {
                    guard textFieldText != "" else { return }
                        withAnimation {
                            isDone.toggle()
                        }
                }, label: {
                    Image(systemName: isDone ? "checkmark.circle.fill" : "checkmark.circle")
                        .foregroundStyle(isDone ? .green : .red)
                })
                
                TextField(item.todoName, text: $textFieldText, prompt: Text("Todo eintragen"))
                    .strikethrough(isDone ? true : false)
                    .foregroundStyle(isDone ? Color.gray.opacity(0.7) : Color.primary)
                Spacer()
                
                if item.priority == "Niedrig" {
                    Menu(item.priority) {
                        ForEach(priority, id: \.self) { prio in
                            Button(prio) {
                                item.priority = prio
                            }
                        }
                    }
                    .foregroundStyle(.green)
                } else if item.priority == "Mittel" {
                    Menu(item.priority) {
                        ForEach(priority, id: \.self) { prio in
                            Button(prio) {
                                item.priority = prio
                            }
                        }
                    }
                    .foregroundStyle(.blue)
                } else if item.priority == "Hoch" {
                    Menu(item.priority) {
                        ForEach(priority, id: \.self) { prio in
                            Button(prio) {
                                item.priority = prio
                            }
                        }
                    }
                    .foregroundStyle(.orange)
                } else if item.priority == "Dringend!" {
                        Menu(item.priority) {
                            ForEach(priority, id: \.self) { prio in
                                Button(prio) {
                                    item.priority = prio
                                }
                            }
                        }
                        .foregroundStyle(.red)
                }

            }
        }
    }
}

struct EditView: View {
    
    var body: some View {
        
        Text("Hi")
    }
}

struct AddTodoSectionView: View {
    
    @State private var sectionTextField: String = ""
    @Binding var personen: [Person]
    
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        
        NavigationStack {
            VStack {
                TextField("", text: $sectionTextField, prompt: Text("Füge eine neue Person hinzu"))
                    .frame(width: 250, height: 50)
                    .padding(.horizontal)
                    .background(Color.gray.opacity(0.3).cornerRadius(10))
                    .padding(.horizontal)
                
                Button("Save") { addItem() }
                    .frame(width: 250, height: 50)
                    .padding(.horizontal)
                    .background(Color.gray.opacity(0.3).cornerRadius(10))
                    .padding(.horizontal)
            }
            .navigationTitle("Neue Person")
        }
    }
    
    func addItem() {
        let newItem = Person(name: sectionTextField, item: [TodolistItem(todoName: "Test1", priority: "Niedrig")])
        personen.append(newItem)
        dismiss()
    }
}

#Preview {
    Todolist(navTitle: "Todoliste", listInfo: ListInfo(listName: "", backgroundColor: .blue, accentColor: .white))
}
swiftui alert
1个回答
0
投票

当某个项目从某人的项目列表中删除时,这会导致

SectionRowView
的刷新。这是因为,这个视图正在观察
person
的变化。如果同时设置了标志
showAlert
(当删除最后一个项目时会发生这种情况),那么我猜想刷新视图和显示警报之间存在竞争条件。有时,刷新期间标志的更改会丢失。

我发现一种修复方法是通过在一小段延迟后异步执行来推迟设置标志(0.5 秒似乎就足够了)。但我认为这不是一个好的解决方案。

从更大的角度来看,如果在

SectionRowView
内部执行删除该人的按钮操作,您打算如何实现?该视图没有人员数组的句柄。所以:

👉 我建议将警报移至父视图

Todolist

  • 当孩子发出信号表明项目列表为空时,会触发警报。
  • 这需要将绑定传递给子级。
  • 如果用户确认应该删除该人,父视图可以更新数组
    personen

这些改变让它以这种方式工作:

// Todolist

@State private var personIdWithoutItems: UUID?
@State private var showAlert: Bool = false

// ...

List {
    // ...
}
.listStyle(.sidebar)
.onChange(of: personIdWithoutItems) { oldVal, newVal in
    if newVal != nil {
        showAlert = true
    }
}
.alert("Person löschen?", isPresented: $showAlert) {
    Button("Behalten", role: .cancel) {
        personIdWithoutItems = nil
        showAlert = false
    }
    Button("Löschen", role: .destructive) {
        personIdWithoutItems = nil
        showAlert = false
    }
} message: {
    Text("Die Person hat alle Aufgaben erledigt, möchtest du das diese gelöscht wird?")
}
// SectionRowView

// @State private var showAlert: Bool = false // -> delete
@Binding var personIdWithoutItems: UUID?

// ...

ForEach(person.item) { item in
    // ...
}
.onDelete { offSet in
    person.item.remove(atOffsets: offSet)
    if person.item.isEmpty {
        personIdWithoutItems = person.id
    }
}

// .alert("Person löschen?", isPresented: $showAlert) { // -> delete
© www.soinside.com 2019 - 2024. All rights reserved.