我是 SwiftUI 的新手,首先尝试创建一个简单的视图,让您可以使用 SwiftData 将项目添加到 NavigationStack。到目前为止,这是我的代码:
import SwiftData
import SwiftUI
struct ItemList: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
@State private var isAdding = false
@State private var newItemName = ""
@FocusState private var focusedField: String?
var body: some View {
NavigationStack {
List {
Section(header: Text("Items")) {
ForEach(items) { item in
NavigationLink {
Text("Placeholder")
} label: {
Text(item.name)
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
modelContext.delete(item)
} label: {
Label("Delete Item", systemImage: "trash.fill")
}
}
}
if isAdding {
TextField("Title", text: $newItemTitle, onCommit: {
if !newItemTitle.isEmpty {
let newItem = Item(name: newItemTitle)
modelContext.insert(newItem)
}
isAdding = false
newItemTitle = ""
focusedField = nil
})
.focused($focusedField, equals: "newItem")
.onAppear {
focusedField = "newItem"
}
.onDisappear {
isAdding = false
newItemTitle = ""
focusedField = nil
appearedCount = 0
}
}
}
}
.listStyle(.insetGrouped)
.toolbar {
ToolbarItem {
Button {
isAdding = true
} label: {
Label("New Item", systemImage: "plus")
}
}
}
}
}
}
#Preview {
ItemList()
.modelContainer(for: Item.self, inMemory: true)
}
正如您所看到的,它创建了存储在 SwiftData 中的所有项目的列表,显示为
NavigationLink
s,并且还有一个工具栏按钮可让您添加新项目。当您单击该按钮时,它会在列表底部显示一个隐藏的文本字段并将其聚焦,从而调出键盘。这部分很棒并且按预期工作。
由于
onCommit
处理程序,如果用户按键盘上的 Return 键,它将关闭文本字段(如果他们没有填写任何内容),或者在关闭字段之前创建一个新项目(如果他们已经填写) 。但缺少的是,如果用户点击文本字段之外的任何位置,它仍然保持活动状态。我希望发生的是,如果他们点击视图中该文本字段之外的任何其他位置,并且如果他们尚未输入任何内容,则应该简单地关闭文本字段。
我尝试在视图中添加 TapGesture 来执行此操作,但它不起作用,所以现在我陷入困境。我怎样才能做到这一点?
最快的方法是使用可点击视图来
overlay
整个列表视图。这意味着当键盘显示时,可点击视图将填满整个屏幕,如果键盘隐藏则消失。你可以试试这个技巧:
List {
...
}
.overlay {
Color.clear
.contentShape(Rectangle()) //<- Added shape to make Color.clear tappable
.frame(maxWidth: focusedField != nil ? .infinity : 0,
maxHeight: focusedField != nil ? .infinity : 0)
.onTapGesture {
focusedField = nil
isAdding = false
}
}