A powerful SwiftUI text field that handles formatting and retaining number values.
NumberTextField
is package for SwiftUI
that offers live formatting of a text field. Rather than accepting a string for binding, NumberTextField
requires an optional Decimal
binding. This allows the developer to only worry about the underlying value
of the text field.
macOS(v12), iOS(v15), tvOS(v15), watchOS(v8)
* Still under review. iOS building/testing at this time. *
struct ContentView: View {
var numberFormatter: NumberFormatter {
let f = NumberFormatter()
/*
Setup your formatter.
*/
f.numberStyle = .percent
f.maximumFractionDigits = 5
return f
}
@State var value: Decimal? = 1.5
var body: some View {
NumberTextField("Enter here...", value: self.$value,
formatter: self.numberFormatter,
isActive: self.$textFieldIsActive,
onChange: { num in
// num is a Decimal? type
},
onCommit: { num in
// num is a Decimal? type
})
.inputAccessory {
HStack {
Spacer()
Button(action: { submit() }) {
Text("Submit")
}
}
.padding()
.background(Color(.secondarySystemBackground))
}
}
}
The value
parameter for the NumberTextField
is a binding to an optional Decimal
type. This binding is always updated to the current text field change. The value
can also have a value assigned prior to binding to the NumberTextField
, for if the developer wants the user to update the value
.
nil
text property to the UIOpenTextField
.The NumberTextField
requires a NumberFormatter
to operate properly. This parameter allows customization of how numbers are to be displayed and emitted. However the NumberFormatter
is setup, the NumberTextField
should always respect the attributes set via the developer.
var numberFormatter: NumberFormatter {
let f = NumberFormatter()
/*
It is recommended to set the following parameters for every formatter.
These can be of whatever value you choose, just make sure they are set.
*/
f.numberStyle = .decimal // Set the style first.
f.minimumFractionDigits = 3
f.maximumFractionDigits = 7
return f
}
The NumberTextField
expects the value
to be in a decimal format.
These attributes have a default value unique to the NumberFormatter.numberStyle
attribute. If fractional input is not performing as expected, set the .maximumFractionDigits
and .minimumFractionDigits
attributes to the desired output.
The .alwaysShowDecimalSeparator
attribute is manipulated via the Coordinator
. If the developer chooses to not allow fractional input, set the .maximumFractionalDigits
attribute to zero. This will also filter the decimal separator from user input.
When using the .keyboardType
modifier of a View
, the UIKit text field will not receive the modification. The KeyboardType
assigned to the text field is the .decimalPad
. There will be no button to call the resignFirstResponder() method. To resolve this, a keyboard accessory must be made within SwiftUI to toggle the state of the text field.
struct ContentView: View {
var numberFormatter: NumberFormatter { ... }
@State var value: Decimal? = 1.5
var body: some View {
NumberTextField("Enter here...", value: self.$value,
formatter: self.numberFormatter,
isActive: self.$textFieldIsActive,
onChange: { _ in },
onCommit: { _ in })
.uiFont(.body, weight: .semibold, design: .rounded)
.textColor(self.textFieldIsActive ? .primary : .white)
.textAlignment(.center)
}
}
Set the font to be displayed within the text field.
The modifier will accept a UIFont
for complete control of the font. It will also accept dynamic text types, including weight and design modification.
Set the text foreground color.
Set the text alignment.
Create an input accessory view that is bound to the keyboard.
In no particular order
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<NumberTextFieldViewRep>) {
// Insert a check for change in uiView.text
DispatchQueue.main.async {
context.coordinator.updateText(uiView)
}
}
.updateUIView()
method not triggering during outside state changes.
@State var value: Decimal?
to nil with a "clear" button.(Current Build: Will Update tag to reflect the upcoming changes.)
Added support for View modifiers:
Resolved view sizing
Added the KeyboardType.decimalPad as the keyboard.
Added a binding to control the state of the text field.
NumberFormatter
. The cursor will now only stay within the range of the first and last number character.NumberFormatter.Locale
should be supported..minimumFractionDigits
greater than 0, will retain the correct formatting while the text field is not active. While active, the text field allows the end-user to freely stay within the .maximumFractionDigits
.Added percent format support.
NumberFormatter
now assigns the value
and text
properties.
Enhanced tracking of text field changes to refactor unnecessary code.
Extended moveCursorWithinBounds() to support percent numbers.
Added currency format support.
Fixed issue where input is not allowing trailing zeroes within fractional numbers.