Skip to content

Instantly share code, notes, and snippets.

@disc0infern0
Created October 30, 2021 13:49
Show Gist options
  • Save disc0infern0/de857b628e5c44b40b4cee30c9689249 to your computer and use it in GitHub Desktop.
Save disc0infern0/de857b628e5c44b40b4cee30c9689249 to your computer and use it in GitHub Desktop.
Working example demonstrating how to control focus to individual rows in a List #SwiftUI
import SwiftUI
struct TaskList: View {
@StateObject var vm = TaskListVM()
@FocusState private var focusedField: RowID?
var body: some View {
List() {
ForEach($vm.tasks) { $task in
TaskRow(task: $task)
.focused($focusedField, equals: .row(id: task.id))
.onSubmit { vm.newTask() }
}
}
.sync($vm.focusedField, $focusedField) //mirror changes to focus in view model
}
}
enum RowID : Hashable { case row(id: String) }
class TaskListVM : ObservableObject{
@Published var tasks: [Task]
@Published var focusedField: RowID?
func newTask() {
let newTask = Task()
tasks.append(newTask)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 ) {
self.focusedField = .row(id: newTask.id). // set focus to the new Task
}
}
init() { tasks = Task.examples }
}
struct Task: Identifiable, Codable {
var id = UUID().uuidString
var text: String = ""
static var examples = [ Task(text: "one"), Task(text: "two")]
}
struct TaskRow: View {
@Binding var task: Task
var body: some View {
HStack{
TextField("Task name", text: $task.text )
}
.swipeActions() {
Button(role: .destructive) {} // -To-do: add code to delete task
label: { Label("Delete", systemImage: "delete.left.fill" ) }
}
}
}
extension View {
/// Mirror changes between an @Published variable (typically in your View Model) and an @FocusedState variable in a view
func sync<T: Equatable>(_ field1: Binding<T>, _ field2: FocusState<T>.Binding ) -> some View {
self
.onChange(of: field1.wrappedValue) { field2.wrappedValue = $0 }
.onChange(of: field2.wrappedValue) { field1.wrappedValue = $0 }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment