Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An implementation of a `NumberTextField` based on `TextField` which should work with any `BinaryInteger` based type.
import SwiftUI
struct NumberTextField<NumberType: BinaryInteger>: View {
private let title: LocalizedStringKey
private let range: ClosedRange<NumberType>?
private let format: IntegerFormatStyle<NumberType>
@Binding private var value: NumberType?
@State private var valueString: String = ""
private init(
title: LocalizedStringKey,
valueBinding: Binding<NumberType?>, range: ClosedRange<NumberType>?,
format: IntegerFormatStyle<NumberType>
) {
self.title = title
self.range = range
self._value = valueBinding
self.format = format
}
var body: some View {
TextField(title, text: $valueString)
.onChange(of: valueString) { [valueString] newString in
if newString == "-" {
// do nothing
//} else if var newValue = NumberType(newString) {
} else if var newValue = NumberType(exactly: (newString as NSString).integerValue) {
//} else if var newValue = try? Int(newString, format: format, lenient: true) {
//} else if var newValue = try? NumberType(newString, format: format, lenient: true) {
if let range = range {
newValue = min(range.upperBound, max(range.lowerBound, newValue))
}
value = newValue
self.valueToString()
} else if newString.isEmpty {
value = nil
self.valueToString()
} else {
self.valueString = valueString
}
}
.onChange(of: value, perform: { newValue in
self.valueToString()
})
.onAppear(perform: valueToString)
}
private func valueToString() {
valueString = value?.formatted(format) ?? ""
}
}
extension NumberTextField {
init(_ title: LocalizedStringKey,
value valueBinding: Binding<NumberType?>, range: ClosedRange<NumberType>? = nil,
format: IntegerFormatStyle<NumberType> = IntegerFormatStyle(locale: .autoupdatingCurrent)
) {
self.init(title: title, valueBinding: valueBinding, range: range, format: format)
}
init(_ title: LocalizedStringKey,
value valueBinding: Binding<NumberType>, range: ClosedRange<NumberType>? = nil,
format: IntegerFormatStyle<NumberType> = IntegerFormatStyle(locale: .autoupdatingCurrent)
) {
let wrapperBinding: Binding<NumberType?> = Binding(get: {
valueBinding.wrappedValue
}, set: { newValue, _ in
if let newValue = newValue {
valueBinding.wrappedValue = newValue
}
})
self.init(title: title, valueBinding: wrapperBinding, range: range, format: format)
}
}
struct NumberTextField_Previews: PreviewProvider {
@State static private var value: Int?
static var previews: some View {
VStack {
NumberTextField("Value", value: $value, range: 1...10)
.frame(maxWidth: 70)
Text("\(value ?? 0, format: .number)")
}
.padding()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment