Reputation: 9979
I am new to SwiftUI and starting out by trying to create a simple view that lets you add items to a NavigationStack using SwiftData. Here's my code so far:
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)
}
So as you can see, it creates a list of any items stored in SwiftData shown as NavigationLink
s, and there's also a toolbar button that lets you add a new item. When you click that button, it reveals a hidden text field at the bottom of the list and focuses it, bringing up the keyboard. That part is great and working as intended.
Because of the onCommit
handler, if the user presses Return on the keyboard, it will either dismiss the text field (if they haven't filled anything out), or create a new item before dismissing the field (if they have). But what's missing is that if the user taps anywhere outside of the text field, it still stays active. What I would like to have happen is if they tap anywhere else in the view, outside of that text field, and if they haven't typed anything yet, it should simply dismiss the text field.
I tried adding a TapGesture to the view to do this, but it didn't work, so now I'm stuck. How can I accomplish this?
Upvotes: 0
Views: 63
Reputation: 8866
The quickest way is to overlay
the entire List view with a tappable view. It means that when the keyboard shows up, the tappable view will fill up whole screen, and disappear if the keyboard hides. You can try this trick:
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
}
}
Upvotes: 0