I'm trying to show conditionally a List
based on showElements
value. This List
contain a ForEach
to show all the elements of the names
array and a TextField
to allow users to add a new name. Changing the value of showElements
to false should remove all the elements of names
array and empty name
string. The code above works on iOS 15 and Xcode 13 but if I run the same project with Xcode 14 the app crash. The steps to reproduce the crash are:
Toggle
TextField
Toggle
(here the crash occur)Here the code:
struct ContentView: View {
@State private var names: [String] = []
@State private var name: String = ""
@State private var showElements: Bool = false
var body: some View {
Form {
Section {
Toggle(isOn: $showElements) {
Text("ShowElements")
}
.onChange(of: showElements) { newValue in
if newValue == false {
name = ""
names = []
}
}
if showElements {
List {
ForEach(names, id: \.self) { name in
Text(name)
}
HStack {
TextField("name", text: $name)
Button {
names.append(name)
name = ""
} label: {
Text("Add")
}
}
}
}
}
}
}
}
Here the console log:
2022-09-08 23:03:26.516945+0200 test[28915:875267] *** Assertion failure in -[SwiftUI.UpdateCoalescingCollectionView _resignOrRebaseFirstResponderViewWithUpdateItems:indexPathMapping:], UICollectionView.m:11437
2022-09-08 23:03:26.524664+0200 test[28915:875267] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempt to delete item containing first responder that refused to resign.
First responder that was asked to resign (returned YES from -resignFirstResponder): <UITextField: 0x7fa25b08aa00; frame = (0 0; 274.333 22); opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x600002271c80>; placeholder = name; borderStyle = None; background = <_UITextFieldNoBackgroundProvider: 0x600002ec7c10: textfield=<UITextField 0x7fa25b08aa00>>; layer = <CALayer: 0x600002c7f540>> inside containing view: <SwiftUI.ListCollectionViewCell: 0x7fa25b054c00; baseClass = UICollectionViewListCell; frame = (20 79; 353 44); clipsToBounds = YES; layer = <CALayer: 0x600002c61400>> at index path: <NSIndexPath: 0xde7e4e75a67d6979> {length = 2, path = 0 - 1}
Current first responder: <_TtGC7SwiftUI15CellHostingViewGVS_15ModifiedContentVS_14_ViewList_ViewVS_26CollectionViewCellModifier__: 0x7fa25b012400; frame = (0 0; 353 44); autoresize = W+H; gestureRecognizers = <NSArray: 0x6000022750e0>; layer = <CALayer: 0x600002c616a0>> inside containing view: <SwiftUI.ListCollectionViewCell: 0x7fa25b054c00; baseClass = UICollectionViewListCell; frame = (20 79; 353 44); clipsToBounds = YES; layer = <CALayer: 0x600002c61400>> at index path: <NSIndexPath: 0xde7e4e75a67d6979> {length = 2, path = 0 - 1}'
*** First throw call stack:
(
0 CoreFoundation 0x00007ff800427378 __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007ff80004dbaf objc_exception_throw + 48
2 Foundation 0x00007ff800b876ac _userInfoForFileAndLine + 0
3 UIKitCore 0x000000010b195c2f -[UICollectionView _resignOrRebaseFirstResponderViewWithUpdateItems:indexPathMapping:] + 1340
4 UIKitCore 0x000000010b1939bc -[UICollectionView _updateWithItems:tentativelyForReordering:propertyAnimator:collectionViewAnimator:] + 226
5 UIKitCore 0x000000010b18a71b -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:collectionViewAnimator:] + 14344
6 UIKitCore 0x000000010b19814c -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:animator:animationHandler:] + 577
7 UIKitCore 0x000000010b197e7d -[UICollectionView performBatchUpdates:completion:] + 34
8 SwiftUI 0x000000010ec02c4f block_destroy_helper + 25991
9 SwiftUI 0x000000010ec03453 block_destroy_helper + 28043
10 SwiftUI 0x000000010f23985e __swift_memcpy56_4 + 159666
11 SwiftUI 0x000000010eda93be block_destroy_helper.23 + 66721
12 SwiftUI 0x000000010eda93d4 block_destroy_helper.23 + 66743
13 UIKitCore 0x000000010c277eff +[UIView(Animation) performWithoutAnimation:] + 84
14 SwiftUI 0x000000010f23aaa0 __swift_memcpy56_4 + 164340
15 SwiftUI 0x000000010f1298f8 objectdestroy.136Tm + 41411
16 SwiftUI 0x000000010ee4a86f block_destroy_helper + 35480
17 SwiftUI 0x000000010ee4a1d0 block_destroy_helper + 33785
18 SwiftUI 0x000000010eb35a78 __swift_assign_boxed_opaque_existential_1 + 70088
19 SwiftUI 0x000000010eb359db __swift_assign_boxed_opaque_existential_1 + 69931
20 SwiftUI 0x000000010eb35ad1 __swift_assign_boxed_opaque_existential_1 + 70177
21 CoreFoundation 0x00007ff800385fe5 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
22 CoreFoundation 0x00007ff800380952 __CFRunLoopDoObservers + 515
23 CoreFoundation 0x00007ff800380e9d __CFRunLoopRun + 1161
24 CoreFoundation 0x00007ff800380637 CFRunLoopRunSpecific + 560
25 GraphicsServices 0x00007ff809c0f28a GSEventRunModal + 139
26 UIKitCore 0x000000010bc5d425 -[UIApplication _run] + 994
27 UIKitCore 0x000000010bc62301 UIApplicationMain + 123
28 SwiftUI 0x000000010f6dbfa3 __swift_memcpy53_8 + 95801
29 SwiftUI 0x000000010f6dbe50 __swift_memcpy53_8 + 95462
30 SwiftUI 0x000000010edfbafc __swift_memcpy195_8 + 12192
31 test 0x000000010a63123e $s4test0A3AppV5$mainyyFZ + 30
32 test 0x000000010a6312c9 main + 9
33 dyld 0x000000010a87c2bf start_sim + 10
34 ??? 0x000000011700252e 0x0 + 4680852782
)
libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempt to delete item containing first responder that refused to resign.
First responder that was asked to resign (returned YES from -resignFirstResponder): <UITextField: 0x7fa25b08aa00; frame = (0 0; 274.333 22); opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x600002271c80>; placeholder = name; borderStyle = None; background = <_UITextFieldNoBackgroundProvider: 0x600002ec7c10: textfield=<UITextField 0x7fa25b08aa00>>; layer = <CALayer: 0x600002c7f540>> inside containing view: <SwiftUI.ListCollectionViewCell: 0x7fa25b054c00; baseClass = UICollectionViewListCell; frame = (20 79; 353 44); clipsToBounds = YES; layer = <CALayer: 0x600002c61400>> at index path: <NSIndexPath: 0xde7e4e75a67d6979> {length = 2, path = 0 - 1}
Current first responder: <_TtGC7SwiftUI15CellHostingViewGVS_15ModifiedContentVS_14_ViewList_ViewVS_26CollectionViewCellModifier__: 0x7fa25b012400; frame = (0 0; 353 44); autoresize = W+H; gestureRecognizers = <NSArray: 0x6000022750e0>; layer = <CALayer: 0x600002c616a0>> inside containing view: <SwiftUI.ListCollectionViewCell: 0x7fa25b054c00; baseClass = UICollectionViewListCell; frame = (20 79; 353 44); clipsToBounds = YES; layer = <CALayer: 0x600002c61400>> at index path: <NSIndexPath: 0xde7e4e75a67d6979> {length = 2, path = 0 - 1}'
terminating with uncaught exception of type NSException
CoreSimulator 857.7 - Device: iPhone 14 Pro (157A86B8-D8A5-4E19-A9B1-B6D9971F2A97) - Runtime: iOS 16.0 (20A360) - DeviceType: iPhone 14 Pro
(lldb)
How can I solve this problem?
This happens only when you try to change the TextField while it is still focused. If you add new elements and do not click/focus on a TextField and change the Toggle state you will see your code does not crash.
To solve the problem, add a @FocusState to the TextField
@FocusState private var textFieldIsFocused: Bool
TextField("name", text: $name)
.focused($textFieldIsFocused)
and lose focus before changing the Toggle state. To achieve this I added a custom Binding to the Toggle so we can release the TextField focus before changing the state.
Toggle(
isOn:
.init(
get: { showElements },
set: {
textFieldIsFocused = false
showElements = $0
}
)
) {
Text("ShowElements")
}
.onChange(of: showElements) { newValue in
if newValue == false {
name = ""
names = []
}
}
import SwiftUI
struct ContentView: View {
@State private var names: [String] = []
@State private var name: String = ""
@State private var showElements: Bool = false
@FocusState private var textFieldIsFocused: Bool
var body: some View {
Form {
Section {
Toggle(
isOn:
.init(
get: { showElements },
set: {
textFieldIsFocused = false
showElements = $0
}
)
) {
Text("ShowElements")
}
.onChange(of: showElements) { newValue in
if newValue == false {
name = ""
names = []
}
}
if showElements {
List {
ForEach(names, id: \.self) { name in
Text(name)
}
HStack {
TextField("name", text: $name)
.focused($textFieldIsFocused)
Button {
names.append(name)
name = ""
} label: {
Text("Add")
}
}
}
}
}
}
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Happy Coding,
Kolmar Kafran (:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.