I don't see iOS 14 in the list of simulators either, looking in Preferences | Components. I guess that means we can't use the SwifUI App life-cycle
Post
Replies
Boosts
Views
Activity
Thanks
.keyboardType(.decimalPad)
is exactly what I wanted.
But there doesn't seem to be a way to intercept keystrokes the way I am using
textField(_:shouldChangeCharactersIn:replacementString:)
I think that provides a better user experience.
No, sorry travis78701. I know about that handler. I need to intercept each character. onChange only deals with changes to the whole work when you leave the TextField.
Thanks for your answers, guys. It may be that SwiftUI has been debugged or improved in some way since I ran into this, but in the end, I was able to simply add this line to the TextField:
TextField("0.0", text: $entry)
.onChange(of: entry, perform: checkEntry)
given these declarations:
@AppStorage("entry") private var entry = "0"
(Of course the @AppStorage is not part of this solution)
and
func checkEntry(value: String) { entryChecker.check(entry: $entry) }
Thanks. I got it working somehow. Not sure if it was a change in Xcode.
As you requested, here is the code for deleting:
func deleteConversion(at offsets: IndexSet) { conversions.remove(atOffsets: offsets) }
conversions is defined like this:
@AppStorage("savedConversions") var conversions: [IDLine] = []
IDLine is this:
struct IDLine: Identifiable, Codable { // To allow SwiftUI List to correctly edit with some strings repeated
let id: Int
let line: String
}// IDLine
@jonasmw:
I am able to reject invalid strings - which can be created by adding an invalid character from the keyboard or pasting in, in my function entryChecker.check(entry)
In my code I have this:
TextField("0.0", text: $entry)
.focused($entryFocused)
.onChange(of: entry, perform: checkEntry)
func checkEntry(value: String) { entryChecker.check(entry: $entry) }
private var entryChecker = FloatEntryValidator(maxlen: 10)
class FloatEntryValidator {
init(maxlen: Int? = nil) { self.maxlen = maxlen}
private let maxlen: Int?
private var prevValidEntry = ""
func check(entry: Binding<String>) {
let value = entry.wrappedValue
if value.isValidFloatEntry(maxlen: maxlen) {
prevValidEntry = value
} else {
entry.wrappedValue = prevValidEntry // Reset
}
}// check
}// FloatEntryValidator
public let minusSign: Character = NumberFormatter().minusSign?.first ?? "-"
public let decimalSep: Character = NSLocale.current.decimalSeparator?.first ?? "."
extension String {
func isValidFloatEntry(maxlen: Int? = nil) -> Bool { // Values that a TextEdit can be allowed to have as the user enters a floating point number
if let maxlen = maxlen, count > maxlen {
return false
}
var parser = StringParser(self)
parser.skipOptional(char: minusSign)
parser.skipDecimals()
parser.skipOptional(char: decimalSep)
parser.skipDecimals()
return parser.finished
}// isValidFloatEntry
}
struct StringParser { // For verifying that a string conforms to specific pattern of sequence. It is valid if finished is true at the end
public init(_ s: String) { string = s; index = s.startIndex }
public let string: String
private var index: String.Index
public var finished: Bool { return index == string.endIndex }
public mutating func skipOptional(char: Character) { // Skip single character that is permitted at the
if !finished && string[index] == char {
index = string.index(after: index)
}
}// skipOptional(Character)
public mutating func skipDecimals() { // Advances index to first place that is not a decimal digit
while !finished {
let uss = string[index].unicodeScalars
if uss.count > 1 || !CharacterSet.decimalDigits.contains(uss.first!) {
return
}
index = string.index(after: index)
}
}// skipDecimal
}// StringParser
Claude31: Notices that in the working version, the second one, I am using the modifier .foregroundColor on both the Button and the Image. That allows me to use the expanded tappable area that Buttons offer. In the second one, Button effectively occupies all the available space after the Image and Spacer have been allocated their minimal required space. In the first code block, the Text and Spacer are allocated their space and the Button takes up what it left.
It just seems counter-intuitive to be giving a Button the colour of a Text, and an Image the colour of a Button.
Thanks, Bryan. I'll wait for the next version of Xcode and then file a radar.
Hah! You were right, but you shouldn't have been, BabyJ. I added 4 parentheses and it worked:
struct ContentView: View {
var body: some View {
VStack {
HStack {
Button("Hi") { print("Hi!") }
Button("Hello") { print("Hello!") }
Button("Good evening") { print("Good evening!") }
}
(EqualWidthHStack()) {
Button("Hi") { print("Hi!") }
Button("Hello") { print("Hello!") }
Button("Good evening") { print("Good evening!") }
}
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
}
}