In Swift UI I’m struggling to discover a answer to enhancing Int16
and Double
values primarily linked to CoreData entities.
The SwiftUI TextField("key", worth: $int16, format: .quantity)
crashes at any time when a quantity too massive for the Int16 kind is typed in, understandibly.
What I would love my textfield to do is
- Bind to a
Int16?
andString
- When there’s a worth inside the textfield the
appending
string is appended to the tip but it surely not selectable or editable in anyway - Any main 0s needs to be eliminated and by no means proven (except the .allowZero choice is getting used)
- AutoFill/Pasting ought to solely be allowed in the event that they’re numbers (if thats not doable then by no means)
- When all values inside the string are eliminated the textfield needs to be clean, the appending string ought to disappear and the Int16 binding ought to turn into nil. (This permits for the view to validate the Int16 as if there’s a suitable worth Int16 is not going to be nil)
In the mean time I’ve created an enum which lets you choose further guidelines for the TextField, resembling enable unfavourable values and permit 0. These haven’t been built-in but.
My present issues are to do with pasting/autofill.
- Autocomplete is ready to enter values that I are not looking for resembling letters after which I’m unable to take away them, this shouldn’t be doable it ought to both filter out all characters that are not numbers or ignore the brand new string solely
- Managing the cursor is proving tough for me, when choosing a spread of values for instance the
234
within the Int1612345
and change it with one other worth, say1
. The cursor seems right here 115| as an alternative of right here 11|5 the place I imagine it will be anticipated. - the textfield would not scale with dynamic kind font sizes
I’m inexperienced with the UIViewRepresentable and UITextField and so there could also be different issues seen to those that understand it properly. All assistance is drastically appreciated, in my present venture that is proving to be fairly the hurdle for common security.
Under is an instance of the code I’ve to date.
struct Int16TextField: UIViewRepresentable {
let titleKey: String
@Binding var textual content: String
@Binding var int16: Int16?
let appending: String
let choices: Set
init(_ titleKey: String, textual content: Binding, int16: Binding, choices: Set = [], appending: String = "") {
let x = NSLocalizedString(titleKey, remark: "")
self.titleKey = x
self._text = textual content
self._int16 = int16
self.appending = appending
self.choices = choices
}
func makeUIView(context: Context) -> UITextField {
let textField = getTextField()
textField.delegate = context.coordinator
return textField
}
//SwiftUI to UIKit
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.textual content = textual content
}
personal func getTextField() -> UITextField{
let textField = UITextField()
let placeHolder: NSAttributedString = NSAttributedString(string: titleKey, attributes: [:])
textField.attributedPlaceholder = placeHolder
textField.keyboardType = .numberPad
return textField
}
//UIKit to SwiftUI
func makeCoordinator() -> Coordinator {
return Coordinator(textual content: $textual content, int16: $int16, choices: choices, appending: appending)
}
class Coordinator: NSObject, UITextFieldDelegate {
@Binding var textual content: String
@Binding var int16: Int16?
let appending: String
let choices: Set
init(textual content: Binding, int16: Binding, choices: Set, appending: String) {
self._text = textual content
self._int16 = int16
self.appending = appending
self.choices = choices
}
func textField(_ textField: UITextField, shouldChangeCharactersIn vary: NSRange, replacementString string: String) -> Bool {
var oldValue = textField.textual content ?? ""
var newValue = string
print(oldValue.debugDescription, newValue.debugDescription)
if oldValue.hasSuffix(appending){
oldValue = String(oldValue.dropLast(appending.depend))
}
if newValue.isEmpty && oldValue.depend == 1 {
int16 = nil
textField.textual content = ""
textual content = ""
return false
}
// Guarantee solely numbers are allowed
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: newValue)
guard allowedCharacters.isSuperset(of: characterSet) else {
return false
}
//place new digit(s) wherever the cursor is vary.location == cursour place
guard let textRange = Vary(vary, in: oldValue) else {
return false
}
//If cursor is in the beginning and we're including solely 0's, dont!
if vary.location == 0{
if newValue.hasPrefix("0"){
whereas newValue.hasPrefix("0") {
newValue.removeFirst()
}
if newValue.isEmpty{
return false
}
}
}
var updatedText = oldValue.replacingCharacters(in: textRange, with: newValue)
//Trim main 0's
whereas updatedText.hasPrefix("0") {
updatedText.removeFirst()
}
//guarantee Int16 conformance
if updatedText != ""{
guard let newInt16 = Int16(updatedText), newInt16 <= Int16.max else {
return false
}
int16 = newInt16
}
else {
int16 = nil
}
updatedText += appending
if updatedText == appending {
int16 = nil
textField.textual content = ""
textual content = ""
return false
}
textField.textual content = updatedText
textual content = updatedText
// Calculate the brand new cursor place
let newCursorPosition: Int = (string.depend == 0 ? vary.location : vary.location + string.depend - vary.size)
if let newPosition = textField.place(from: textField.beginningOfDocument, offset: newCursorPosition) {
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
return false
}
func textFieldDidChangeSelection(_ textField: UITextField) {
if !appending.isEmpty {
guard let selectedRange = textField.selectedTextRange else { return }
let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.begin)
let textLength = textField.textual content?.depend ?? 0
if selectedRange.begin == textField.beginningOfDocument && selectedRange.finish == textField.endOfDocument {
let newPosition = textField.place(from: textField.endOfDocument, offset: -appending.depend)
if let newPosition = newPosition{
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: newPosition)
}
}
for decrement in 0...(appending.depend - 1) {
if cursorPosition == (textLength - decrement) {
let newPosition = textField.place(from: textField.endOfDocument, offset: -appending.depend)
if let newPosition = newPosition {
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
break
}
}
}
}
}
}
And here’s a ContentView and the Int16TextFieldOptions
enum for use in previews.
struct ContentView: View {
@State var textual content: String = ""
@State var int16: Int16?
@State var appending: String = "cm"
@State var choices: Set = [.allowMinus]
func testInt16() -> String {
guard let new = int16 else {return "nil"}
return new.description
}
var physique: some View {
VStack {
Type{
HStack{
Textual content("String:")
Spacer()
Textual content(textual content.debugDescription)
}
HStack{
Textual content("Int16:")
Spacer()
Textual content(testInt16())
}
Int16TextField(choices.rangeString(), textual content: $textual content, int16: $int16, choices: choices, appending: appending)
}
}
}
}
enum Int16TextFieldOptions{
case allowMinus, allowZero
}
extension Set {
func rangeString() -> String {
var x = "Vary: "
if self.accommodates(.allowZero){
if self.accommodates(.allowMinus){
//each
x += "-32768...32767"
}
else {
//simply zero
x += "0...32767"
}
}
else if self.accommodates(.allowMinus){
//minus
x += "-32768...-1, 1...32767"
}
else {
x += "1...32767"
}
guard x != "Vary: " else { fatalError() }
return x
}
}
Any assist can be drastically appreciated as I really feel I’ve come to a lifeless finish on this downside.
Thanks