SwiftUI中的TextField在输入字符时失去焦点

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

当我在TextField(在ExerciseSetView内)中输入字符时,我有一个问题,我必须重新单击文本框以使其成为文本,以便我可以输入另一个字符。如果我从“文本字段”中删除了绑定,则可以流畅地输入文本。

我认为这与我的presenter类和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{$0.id.uuidString != exerciseId}
    }

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

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

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

            return $0
        }
    }

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

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

                    return oldExerciseSet
                }

                return $0
            }

            return $0;
        }
    }

    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 ($0 as String?) != nil {
                    self.onUpdate(WorkoutExerciseSetVM(id: self.set.id, reps: $0 , weight: self.set.weight))
                }
            }
        )

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

        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()
    }
}
swift swiftui swift4.2 swiftui-bug
1个回答
0
投票
我已经更新了您的观点,这似乎可行。您没有使用ObservableObject,应使用它们。只需传递ObservableObjects,让他们为您完成绑定即可,而不是设置自定义绑定。

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) } } }

让我知道这是否适合您。

© www.soinside.com 2019 - 2024. All rights reserved.