Change Placeholder to a String in a CustomTextField

Hello
How do I make the default placeholder of a CustomTextField (that has a double binding) a string (onAppear)

When I run the app on the TextField I see 0 instead of "Input"

Code:

Code Block
import SwiftUI
struct BMIView: View {
    var currencyFormatter: NumberFormatter {
        let formatter = NumberFormatter()
        formatter.locale = .current
        formatter.numberStyle = .decimal
        return formatter
    }
    @State private var height: Double?
   
    var body: some View {
        NavigationView{
            Form{
                Section(header: Text("Enter your height in cm")){
                    DecimalTextField("Input", value: $height.bound, formatter: currencyFormatter)
               }
            }
            .navigationBarTitle("BMI")
        }
    }
}
struct DecimalTextField: UIViewRepresentable {
    private var placeholder: String
    @Binding var value: Double
    private var formatter: NumberFormatter
    init(_ placeholder: String,
         value: Binding<Double>,
         formatter: NumberFormatter ) {
        self.placeholder = placeholder
        self._value = value
        self.formatter = formatter
    }
    func makeUIView(context: Context) -> UITextField {
        let textfield = UITextField()
        textfield.keyboardType = .decimalPad
        textfield.delegate = context.coordinator
        textfield.placeholder = placeholder
        textfield.text = formatter.string(for: value) ?? placeholder
        textfield.textAlignment = .left
        let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textfield.frame.size.width, height: 44))
let doneButton = UIBarButtonItem(title: "Done", style: .done,
target: self, action: #selector(textfield.doneButtonTapped(button:)))
        let space = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace,
                                    target: nil,action: nil)
        toolBar.setItems([space, doneButton], animated: true)
        textfield.inputAccessoryView = toolBar
        return textfield
    }
    func updateUIView(_ uiView: UITextField, context: Context) {
        // Do nothing, needed for protocol
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: DecimalTextField
        init(_ textField: DecimalTextField) {
            self.parent = textField
        }
        func textField(_ textField: UITextField,
                       shouldChangeCharactersIn range: NSRange,
                       replacementString string: String) -> Bool {
            // Allow only numbers and decimal characters
            let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
            let withDecimal = (
                string == NumberFormatter().decimalSeparator &&
                    textField.text?.contains(string) == false
            )
            if isNumber withDecimal,
                let currentValue = textField.text as NSString?
            {
                // Update Value
                let proposedValue = currentValue.replacingCharacters(in: range, with: string) as String
                let decimalFormatter = NumberFormatter()
                decimalFormatter.locale = Locale.current
                decimalFormatter.numberStyle = .decimal
                // Try currency formatter then Decimal formatrer
                let number = self.parent.formatter.number(from: proposedValue) ?? decimalFormatter.number(from: proposedValue) ?? 0.0
                // Set Value
                let double = number.doubleValue
                self.parent.value = double
            }
            return isNumber withDecimal
        }
        func textFieldDidEndEditing(_ textField: UITextField,
                                    reason: UITextField.DidEndEditingReason) {
            // Format value with formatter at End Editing
            textField.text = self.parent.formatter.string(for: self.parent.value)
        }
    }
}
// MARK: extension for done button
extension  UITextField{
    @objc func doneButtonTapped(button:UIBarButtonItem) -> Void {
        self.resignFirstResponder()
    }
}
extension Optional where Wrapped == Double {
    
    var _bound: Double? {
        get{
            return self
            
        }
        set{
            self = newValue
            
        }
    }
    
    var bound: Double {
        get{
            return _bound ?? 0
        }
        set {
            _bound = newValue
        }
    }
}

The problem might be at line 243

(I found the struct DecimalTextField on the internet)

Thank you for your time


The worst part of this issue exists on this line (line 32):
Code Block
    @Binding var value: Double

Declaring the value as non-Optional, the textfield holds non-empty String in text and placeholder is not used.
(In textfield.text = formatter.string(for: value) ?? placeholder, formatter.string(for: value) never generates nil when value is non-Optional Double.)

Please try making the value as Double?:
Code Block
}
struct DecimalTextField: UIViewRepresentable {
private var placeholder: String
@Binding var value: Double? //<-
private var formatter: NumberFormatter
init(_ placeholder: String,
value: Binding<Double?>, //<-
formatter: NumberFormatter ) {
self.placeholder = placeholder
self._value = value
self.formatter = formatter
}
//...
}


And use it as:
Code Block
DecimalTextField("Input", value: $height, formatter: currencyFormatter) //<- no `.bound`



You can try changing line 61 as follows:
Code Block
textfield.text = value.map{formatter.string(for: $0)} ?? placeholder

But I recommend you not to put placeholder into the property text. You will see why if you run it.


You have no need to use bound any more, but the definition can be simplified:
Code Block
extension Optional where Wrapped == Double {
var bound: Double {
get{
return self ?? 0
}
set {
self = newValue
}
}
}


That seems logical ? As soon as you have a value (here 0), it replaces the placeholder.
Change Placeholder to a String in a CustomTextField
 
 
Q