Gargoyle
Gargoyle

Reputation: 10294

Link @Binding to @Published with SwiftUI

I'm trying to figure out how to link the @Binding passed into a custom View to an @Published from that view's model. Essentially I'm trying to create a reusable integer only TextField. I'm using the below code, which works to set the integer value into the text field, but what I can't figure out is how to update the binding when the text changes.

private class IntegerTextFieldValue: ObservableObject {
    @Published var value = "" {
        didSet {
            let numbersOnly = value.filter { $0.isNumber }
            if value != numbersOnly {
                value = numbersOnly
            }
        }
    }
}

struct IntegerTextField: View {
    @Binding var value: Int?
    @StateObject private var fieldValue = IntegerTextFieldValue()

    var placeholder = ""

    var body: some View {
        TextField(placeholder, text: $fieldValue.value)
            .keyboardType(.numberPad)
            .onAppear {
                if let value = value {
                    fieldValue.value = "\(value)"
                }
            }
    }
}

Upvotes: 3

Views: 2653

Answers (2)

user8354447
user8354447

Reputation:

If I understand you correctly

.onChange (of: fieldValue.value) { vl in
    value = vl
}

this modifier updates the binding value to $fieldValue.value

Upvotes: 2

Asperi
Asperi

Reputation: 257493

Here is modified code to demo a possible approach (tested with Xcode 12.1 / iOS 14.1):

private class IntegerTextFieldValue: ObservableObject {
    @Published var value = "" {
        didSet {
            let numbersOnly = value.filter { $0.isNumber }
            if value != numbersOnly {
                value = numbersOnly
            }

            if let number = Int(value) {
                numberValue = number
            }
        }
    }
    
    @Published var numberValue: Int = 0
}

struct IntegerTextField: View {
    @Binding var value: Int?
    @StateObject private var fieldValue = IntegerTextFieldValue()

    var placeholder = ""

    var body: some View {
        TextField(placeholder, text: $fieldValue.value)
            .keyboardType(.numberPad)
            .onAppear {
                if let value = value {
                    fieldValue.value = "\(value)"
                }
            }
            .onChange(of: fieldValue.numberValue) {
                if $0 != self.value {
                    self.value = $0
                }
            }
    }
}

Upvotes: 1

Related Questions