Post

Replies

Boosts

Views

Activity

Changes to the bound value update the string displayed by the text field.
Is it possible to have a TextField that validates its input on every character entered by the user so that the string displayed by the text field updates only on successful validation? The documentation on the TextField seems to suggest that this is possible by using a bound value. In my testing this is not the case. Even when validation fails, the text field always updates the string displayed. Changes to the bound value update the string displayed by the text field. Editing the text field updates the bound value, as long as the formatter can parse the text. If the format style can’t parse the input, the bound value remains unchanged. https://developer.apple.com/documentation/swiftui/textfield/init(_:value:formatter:)-9jw0i The way I understand it, in case of an error thrown by ParseStrategy/parse(_ value: String) the bound value remains unchanged. Since the bound value did not change the string displayed by the text field should not be updated. I have tried the code example used in the documentation overview of the TextField to create a sample app. @State private var nameComponents = PersonNameComponents() var body: some View { TextField( "Proper name", value: $nameComponents, format: .name(style: .medium) ) .onSubmit { validate(components: nameComponents) } .disableAutocorrection(true) .border(.secondary) Text(nameComponents.debugDescription) } The bound value doesn’t have to be a string. By using a FormatStyle, you can bind the text field to a nonstring type, using the format style to convert the typed text into an instance of the bound type. The following example uses a PersonNameComponents.FormatStyle to convert the name typed in the text field to a PersonNameComponents instance. A Text view below the text field shows the debug description string of this instance. https://developer.apple.com/documentation/swiftui/textfield
3
0
277
Sep ’24
Modifying the view hierarchy of a UITableViewCell as part of reloadData
I am trying to debug a crash due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSFrozenDictionary layoutSublayers]: unrecognized selector sent to instance. I see 2 things that I find interesting about it. The fact that the instance is a __NSFrozenDictionary tells me that the reference to a CALayer that has since been evicted from memory and re-written. The call to layoutSublayers tells me that the CALayer was dealloc-ed at some point between the call to setNeedsLayout (or layoutIfNeeded) This seemingly occurs as part of a call to -[UITableView reloadData] Furthermore, each cell created by the UITableView has a UIStackView. As part of the call to cellForRowAtIndexPath the code adds an instance of "custom view" to the stack view's subviews. As part of the call to prepareForReuse the code removes the "custom view" from the stack view's subviews. Therefore as part of the prepareForReuse the "custom view" (and its layer) is evicted from memory. My theory is that the tableview does a layout pass on the visible cell which has since had its subview removed which causes the crash. My question is what are the constraints on when/where to call reloadData and/or when/where you should definitely avoid it as it relates to this context? This is code that modifies the view hierarchy of the cell as part of its lifecycle which AFAIK this is "not supported" since prepareForReuse is meant to be used for resetting view state and cellForRowAtIndexPath to reset content. In that sense, another question, are you not allowed to modify the cell view hierarchy period as part of the cycle to draw the visible cells or is it more of a case, do not call reloadData?
2
0
508
Jul ’24
DateFormatter, fixed-format dates and iOS Simulator
I’ve been trying to debug a seemingly random issue that involves dates, timezones, formatters and locales. I wanted to make sure I got my basics right. This I a test that passes “locally” as well as on a second computer but failed on a third. It should come down to this code once I strip away all the noise. func testExample() throws { //formatter to create date from string let DateFormatterSecondsFromGmT = DateFormatter() //notice how the order is different to the other formatter DateFormatterSecondsFromGmT.dateFormat = "dd/MM/yyyy HH:mm" //format first DateFormatterSecondsFromGmT.timeZone = TimeZone(secondsFromGMT: 0) DateFormatterSecondsFromGmT.locale = Locale(identifier: "en_GB") //locale second let string = "08/03/2023 12:10" //date the test failed (approx.): 21 Apr 2023, 11:20:32 let date = DateFormatterSecondsFromGmT.date(from: string) ?? Date() //formatter to create string from date let DateFormatterUTC = DateFormatter() DateFormatterUTC.timeZone = TimeZone(identifier: "UTC") DateFormatterUTC.locale = Locale(identifier: "en_GB") DateFormatterUTC.dateFormat = "HH:mm" let actual = DateFormatterUTC.string(from: date) //On the computer that failed, this date was at 17:10 (i.e. off by 5 hours) let expected = "12:10" //One case where actual was 17:10 and expected 12:10. XCTAssertEqual(actual, expected) } According to the Technical Q&A QA1480 NSDateFormatter and Internet Dates, when using a fixed-format date "you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is 'en_US_POSIX'". Based on the TN, "On iOS, the user can override the default AM/PM versus 24-hour time setting, which causes NSDateFormatter to rewrite the format string you set". Emphasis mine. AFAIK the DateFormatter does not log a warning so I am not sure exactly what is the underlying cause for the test to fail. Also, It’s not immediately obvious to me looking at the code what could be going wrong, given that both formatters have a fixed locale (and for what is worth timezone) which leads me to believe the DateFormatter did not rewrite the format string set. On that note. I understand that setting the TimeZone to the DateFormatter will take that into account when formatting a Date to a String. Does setting the Timezone also play a role when converting a String to a Date? If so, how? Ideally, if possible, would be good for me to understand how the test ended up failing. In other words, how can I reproduce the failure which should then give me a clue into what is wrong and coming up with a solution to the root cause.
4
0
2.7k
Apr ’23
How can you create an “aligned” array of bytes and read from it?
I want to be able to use the UnsafeRawBufferPointer.load(fromByteOffset:as:) - https://developer.apple.com/documentation/swift/unsaferawbufferpointer/2632297-load method to read a number of bytes from a [UInt8] array and their corresponding type as an UnsignedInteger, FixedWidthInteger at each read. In both approaches that follow, a "Fatal error: load from misaligned raw pointer" exception is raised, since load expects the underlying data to be aligned in memory - https://bugs.swift.org/browse/SR-10273. I have tried using a ContiguousArray 				var words: ContiguousArray<UInt8> = [0x01, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0xec, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x20, 0x00, 0x00, 0xe0, 0x88, 0x47, 0xa3, 0xd6, 0x6b, 0xd6, 0x01, 0x4c, 0xff, 0x08] 				 				var offset = 0 				let byte = words.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt8.self) } 				offset += MemoryLayout<UInt8>.size 				let bytes = words.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt16.self) } 						XCTAssertEqual(byte, UInt8(littleEndian: 0x01)) 						XCTAssertEqual(bytes, UInt16(littleEndian: 0x0003)) Allocating and initialising a UnsafeMutablePointer - https://developer.apple.com/documentation/swift/unsafemutablepointer 				var words: [UInt8] = [0x01, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0xec, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x20, 0x00, 0x00, 0xe0, 0x88, 0x47, 0xa3, 0xd6, 0x6b, 0xd6, 0x01, 0x4c, 0xff, 0x08] 				 				let uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: words.count) 				uint8Pointer.initialize(from: &words, count: words.count) 				 				let rawPointer = UnsafeMutableRawPointer(uint8Pointer) 				 				var offset = 0 				let byte = UInt8(bigEndian: rawPointer.load(fromByteOffset: offset, as: UInt8.self)) 				offset += MemoryLayout<UInt8>.size 				let bytes = UInt16(bigEndian: rawPointer.load(fromByteOffset: offset, as: UInt16.self)) 				rawPointer.deallocate() 				uint8Pointer.deinitialize(count: words.count) 				XCTAssertEqual(byte, UInt8(littleEndian: 0x01)) 				XCTAssertEqual(bytes, UInt16(littleEndian: 0x0003)) Can you please point out where my misunderstanding lies in each and provide a working example?
6
0
1.9k
Aug ’20