I am attempting to format the data in a SwiftUI TextField with a pattern or mask. (For clarity - NOT a UITextField). One example would be a US phone number. So the user can type 1115551212 and the result in the view is 111-555-1212. I'll use a numberPad in this case, but for future, with the full keyboard, if the user types a non-number, I'd like to be able to replace it with something, say 0. So entering 111abc1212 would result in 111-000-1212. Even though the code below converts the string to numbers, ideally, I want the mask to operate on a String not a number - giving the flexibility to format part numbers etc.
I have been able to do this with a func that operates from a button, but of course I want it to be automatic. I have been completely unsuccessful with SwiftUI modifiers to do the same. And using the built in .textContentType(.telephoneNumber) does absolutely nothing in my tests.
I would expect to have some modifier like .onExitCommand that can execute when the focus leaves the TextField but I don't see a solution.
This code works with the button (I will later add rules to filter for numbers when numbers are expected):
struct ContentView: View {
@State private var phoneNumber = ""
@State private var digitArray = [1]
var body: some View {
VStack {
Group {//group one
Text("Phone Number with Format").font(.title)
.padding(.top, 40)
TextField("enter phone number", text: $phoneNumber)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.keyboardType(.numberPad)
}//Group one
Group {//group two
Button(action: {self.populateTextFieldWithPhoneFormat(phoneString: self.phoneNumber)}) {
Text("Convert to Phone Format")
}
}//group two
Spacer()
}//VStack
}
func populateTextFieldWithPhoneFormat(phoneString: String) {
var digitArray = [Int]()
let padded = phoneNumber.padding(toLength: 10, withPad: "0", startingAt: 0)
let paddedArray = padded.map {String($0)}
for char in paddedArray {
digitArray.append(Int(char) ?? 0)
}
phoneNumber = format(digits: digitArray)
}//populate
func format(digits: [Int]) -> String {
var phone = digits.map(String.init)
.joined()
if digits.count > 3 {
phone.insert("-", at: phone.index(
phone.startIndex,
offsetBy: 3)
)
}
if digits.count > 7 {
phone.insert("-", at: phone.index(
phone.startIndex,
offsetBy: 7)
)
}
return phone
}
}
I also attempted to make my own ViewModifier, but got nowhere.
Xcode Version 11.2.1 (11B500)
Isn't this constructor of what you are looking for?
/// Creates an instance with a `Text` label generated from a title string.
///
/// - Parameters:
/// - title: The title of `self`, describing its purpose.
/// - text: The text to be displayed and edited.
/// - onEditingChanged: An `Action` that will be called when the user
/// begins editing `text` and after the user finishes editing `text`,
/// passing a `Bool` indicating whether `self` is currently being edited
/// or not.
/// - onCommit: The action to perform when the user performs an action
/// (usually the return key) while the `TextField` has focus.
public init<S>(_ title: S, text: Binding<String>,
onEditingChanged: @escaping (Bool) -> Void = { _ in },
onCommit: @escaping () -> Void = {}) where S : StringProtocol