当我输入字符时,SwiftUI 中的 TextField 失去焦点

TextField in SwiftUI loses focus when I enter a character

我在 TextField(在 ExerciseSetView 中)输入字符时遇到问题,我必须重新单击文本框才能输入另一个字符.如果我从文本字段中删除绑定,我可以流畅地输入文本。

我认为这与我的演示者 class 和 updateSet 函数重新创建一个集合实例有关,因为我必须替换一个深两层的一些值数组。

//
//  ContentView.swift
//  test
//
//

import SwiftUI
import Combine
import CoreData

class WorkoutExerciseSetVM: Hashable, ObservableObject {
    @Published public var id: Int
    @Published public var reps: String
    @Published public var weight: String

    init(id: Int, reps: String, weight: String) {
        self.id = id
        self.reps = reps
        self.weight = weight
    }

    static func ==(lhs: WorkoutExerciseSetVM, rhs: WorkoutExerciseSetVM) -> Bool {
        return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    }

    func hash(into hasher: inout Hasher) { return hasher.combine(ObjectIdentifier(self)) }
}

class WorkoutExerciseVM: Hashable, ObservableObject {
    @Published public var id: UUID
    @Published public var name: String
    @Published public var sets: [WorkoutExerciseSetVM]

    init(id: UUID, name: String, sets: [WorkoutExerciseSetVM]) {
        self.id = id
        self.name = name
        self.sets = sets
    }

    static func ==(lhs: WorkoutExerciseVM, rhs: WorkoutExerciseVM) -> Bool {
        return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    }

    func hash(into hasher: inout Hasher) { return hasher.combine(ObjectIdentifier(self)) }
}

class WorkoutVM: Hashable, ObservableObject {
    @Published public var id = UUID()
    @Published public var name: String
    @Published public var exercises: [WorkoutExerciseVM]
    @Published public var started: Date? = Date()
    @Published public var completed: Date? = Date()

    init(id: UUID, name: String, exercises: [WorkoutExerciseVM], started: Date?, completed: Date?) {
        self.id = id
        self.name = name
        self.exercises = exercises
        self.started = started
        self.completed = completed
    }

    static func ==(lhs: WorkoutVM, rhs: WorkoutVM) -> Bool {
        return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    }

    func hash(into hasher: inout Hasher) { return hasher.combine(ObjectIdentifier(self)) }
}

class WorkoutPresenter: ObservableObject {
    @Published public var id: UUID
    @Published public var exercises: [WorkoutExerciseVM]
    @Published public var name: String
    @Published public var started: Date?
    @Published public var completed: Date?

    init(routine: WorkoutVM) {

        self.id = UUID()
        self.name = routine.name
        self.started = Date()
        self.completed = nil
        self.exercises = routine.exercises.map{ exercise in
            return WorkoutExerciseVM(
                id: UUID(),
                name: exercise.name,
                sets: [
                    WorkoutExerciseSetVM(id: 1, reps: "0", weight: "0")
                ]
            )
        }
    }

    func removeExercise(id: UUID) {
        let exerciseId = id.uuidString;
        self.exercises = self.exercises.filter{[=10=].id.uuidString != exerciseId}
    }

    func addSet(id: UUID) {
        let exerciseId = id.uuidString;

        self.exercises = self.exercises.map {
            if ([=10=].id.uuidString == exerciseId) {
                if ([=10=].sets.count == 0) {
                    [=10=].sets.append(WorkoutExerciseSetVM(id: 1, reps: "0", weight: "0"))
                }

                if let lastSet = [=10=].sets.last {
                    [=10=].sets.append(WorkoutExerciseSetVM(id: lastSet.id + 1, reps: lastSet.reps, weight: lastSet.weight))
                }
            }

            return [=10=]
        }
    }

    func updateSet(id: UUID, set: WorkoutExerciseSetVM) {
        let exerciseId = id.uuidString

        self.exercises = self.exercises.map{
            if [=10=].id.uuidString == exerciseId {
                [=10=].sets = [=10=].sets.map{(oldExerciseSet) -> WorkoutExerciseSetVM in
                    if oldExerciseSet.id == set.id {
                        return set
                    }

                    return oldExerciseSet
                }

                return [=10=]
            }

            return [=10=];
        }
    }

    func removeSet(id: UUID) {
        let exerciseId = id.uuidString;

        self.exercises = self.exercises.map{(exercise) -> WorkoutExerciseVM in
            if exercise.id.uuidString == exerciseId {
                let newExercise = exercise

                if newExercise.sets.count > 1 {
                    newExercise.sets.removeLast()
                }

                return newExercise
            }

            return exercise;
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            WorkoutView(presenter: WorkoutPresenter(routine: WorkoutVM(id: UUID(), name: "Test", exercises: [WorkoutExerciseVM(id: UUID(), name: "Exercise", sets: [WorkoutExerciseSetVM(id: 1, reps: "0", weight: "0")])], started: nil, completed: nil)))
        }

    }
}

struct WorkoutView: View {
    @ObservedObject var presenter: WorkoutPresenter
    var body: some View {
        return GeometryReader { geo in
            ZStack {
                VStack {
                    ScrollView {
                        ForEach(self.presenter.exercises, id: \.self) { exercise in
                            ExerciseView(presenter: self.presenter, exercise: exercise)
                        }
                    }
                }
            }
        }
    }
}

struct ExerciseView: View {
    @ObservedObject var presenter: WorkoutPresenter
    var exercise: WorkoutExerciseVM
    var body: some View {
        VStack {
            VStack(alignment: .leading) {
                VStack {
                    VStack {
                        ForEach(exercise.sets, id: \.self) { exerciseSet in
                            ExerciseSetView(
                                set: exerciseSet,
                                onUpdate: { newExerciseSet in
                                    self.presenter.updateSet(id: self.exercise.id, set: newExerciseSet)
                                }
                            )
                        }
                    }
                }
            }
            HStack {
                Button(action: {
                    self.presenter.addSet(id: self.exercise.id)
                }) {
                    HStack {
                        Image(systemName: "plus")
                        Text("Add Set")
                    }

                }
                Button(action: {
                    self.presenter.removeSet(id: self.exercise.id)
                }) {
                    HStack {
                        Image(systemName: "minus")
                        Text("Remove Set")
                    }

                }
            }

        }

    }
}

struct ExerciseSetView: View {
    var set: WorkoutExerciseSetVM
    var onUpdate: (_ set: WorkoutExerciseSetVM) -> Void
    var body: some View {
        let repBinding = Binding(
            get: {
                String(self.set.reps)
            },
            set: {
                if ([=10=] as String?) != nil {
                    self.onUpdate(WorkoutExerciseSetVM(id: self.set.id, reps: [=10=] , weight: self.set.weight))
                }
            }
        )

        let weightBinding = Binding(
            get: {
                String(self.set.weight)
            },
            set: {
                if ([=10=] as String?) != nil {
                    self.onUpdate(WorkoutExerciseSetVM(id: self.set.id, reps: self.set.reps, weight: [=10=] ))
                }
            }
        )

        return HStack {
            Spacer()
// textfield that isn't working
            TextField("", text: repBinding)
            Spacer()
// textfield that isn't working
            TextField("", text: weightBinding)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

您的代码有一些基本错误。在使用此代码投入生产之前,请研究 ObservableObjectPublished 值。不然以后处理这段代码会相当吃力。

我已经更新了您的观点,这似乎有效。您没有按应使用的方式使用 ObservableObject。只需传递 ObservableObjectss 让他们为您进行绑定,而不是设置自定义绑定。

struct ExerciseView: View {

    @ObservedObject var presenter: WorkoutPresenter
    @ObservedObject var exercise: WorkoutExerciseVM

    var body: some View {
        VStack {
            VStack(alignment: .leading) {
                ForEach(exercise.sets, id: \.self) { exerciseSet in
                    ExerciseSetView(set: exerciseSet)
                }
            }
            HStack {
                Button(action: {
                    self.presenter.addSet(id: self.exercise.id)
                }) {
                    HStack {
                        Image(systemName: "plus")
                        Text("Add Set")
                    }
                }
                Button(action: {
                    self.presenter.removeSet(id: self.exercise.id)
                }) {
                    HStack {
                        Image(systemName: "minus")
                        Text("Remove Set")
                    }
                }
            }
        }
    }
}

struct ExerciseSetView: View {

    @ObservedObject var set: WorkoutExerciseSetVM

    var body: some View {
        HStack {
            Spacer()
            TextField("", text: $set.reps)
            Spacer()
            TextField("", text: $set.weight)
        }
    }
}

让我知道这是否适合你。