在 iOS 13 中,tableView 中 UIDatePicker 的无效更新在旋转时崩溃

Posted

技术标签:

【中文标题】在 iOS 13 中,tableView 中 UIDatePicker 的无效更新在旋转时崩溃【英文标题】:Invalid update of a UIDatePicker inside tableView crashing in iOS 13 on rotation 【发布时间】:2020-05-12 08:16:04 【问题描述】:

我正在添加一个 UIDatePicker 作为表格的页脚。我有 UITableView 的另一个扩展,它实现了 traitCollectionDidChange,并且只执行 beginUpdates 和 endUpdates。我在更改方向时遇到以下崩溃。

*** Assertion failure in -[UIPickerTableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:],
/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3901.4.2/UITableView.m:2407
2020-05-12 13:35:28.729340+0530 MyApplication[41892:1575639] *** Terminating app due to uncaught exception 
'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. 
The number of rows contained in an existing section after the update (0) must be equal to the number of 
rows contained in that section before the update (10000), plus or minus the number of rows inserted or 
deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into 
or out of that section (0 moved in, 0 moved out).'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff23c7127e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x00007fff513fbb20 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff23c70ff8 +[NSException raise:format:arguments:] + 88
    3   Foundation                          0x00007fff256e9b51 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
    4   UIKitCore                           0x00007fff4825d508 -[UITableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:] + 193
    5   UIKitCore                           0x00007fff4825cdc1 -[UITableView _endCellAnimationsWithContext:] + 16657
    6   UIKitCore                           0x00007fff48276e0b -[UITableView endUpdatesWithContext:] + 112
    7   MyApplication                        0x0000000103d13ed1 -[UITableView(RIAdditions) traitCollectionDidChange:] + 97
    8   UIKitCore                           0x00007fff4854ccc4 -[UIView _traitCollectionDidChangeInternal:] + 977
    9   UIKitCore                           0x00007fff4854d5c4 -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 161
    10  UIKitCore                           0x00007fff4854d80a -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 743
    11  UIKitCore                           0x00007fff4854d80a -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 743
    12  UIKitCore                           0x00007fff4854d80a -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 743
    13  UIKitCore                           0x00007fff4854d80a -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 743
    14  UIKitCore                           0x00007fff4854d80a -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 743
    15  UIKitCore                           0x00007fff4854d7ae -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 651
    16  UIKitCore                           0x00007fff4854d935 -[UIView _processDidChangeRecursivelyFromOldTraits:toCurrentTraits:forceNotification:] + 140
    17  UIKitCore                           0x00007fff485783d2 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2243
    18  QuartzCore                          0x00007fff2b131db1 -[CALayer layoutSublayers] + 255
    19  QuartzCore                          0x00007fff2b137fa3 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 517
    20  UIKitCore                           0x00007fff48563635 -[UIView(Hierarchy) layoutBelowIfNeeded] + 980
    21  UIKitCore                           0x00007fff47978038 -[UINavigationController _layoutViewController:] + 1465
    22  UIKitCore                           0x00007fff479710e9 -[UINavigationController _layoutTopViewControllerLookForNested:] + 589
    23  UIKitCore                           0x00007fff47966e9a __105-[UINavigationController _repositionPaletteWithNavigationBarHidden:duration:shouldUpdateNavigationItems:]_block_invoke + 718
    24  UIKitCore                           0x00007fff47966b9c -[UINavigationController _repositionPaletteWithNavigationBarHidden:duration:shouldUpdateNavigationItems:] + 308
    25  UIKitCore                           0x00007fff47970339 -[UINavigationController _updateBarsForCurrentInterfaceOrientationAndForceBarLayout:] + 174
    26  UIKitCore                           0x00007fff47980763 __84-[UINavigationController willTransitionToTraitCollection:withTransitionCoordinator:]_block_invoke + 639
    27  UIKitCore                           0x00007fff47a42124 -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 294
    28  UIKitCore                           0x00007fff47a3e446 -[_UIViewControllerTransitionContext __runAlongsideAnimations] + 263
    29  UIKitCore                           0x00007fff4856bde6 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 528
    30  UIKitCore                           0x00007fff4856c395 +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 99
    31  UIKitCore                           0x00007fff47a5665a __58-[_UIWindowRotationAnimationController animateTransition:]_block_invoke_2 + 278
    32  UIKitCore                           0x00007fff4856ff44 +[UIView(Internal) _performBlockDelayingTriggeringResponderEvents:forScene:] + 174
    33  UIKitCore                           0x00007fff47a563e8 __58-[_UIWindowRotationAnimationController animateTransition:]_block_invoke + 164
    34  UIKitCore                           0x00007fff4856bde6 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 528
    35  UIKitCore                           0x00007fff4856c395 +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 99
    36  UIKitCore                           0x00007fff47a562c0 -[_UIWindowRotationAnimationController animateTransition:] + 491
    37  UIKitCore                           0x00007fff480d2e9d -[UIWindow _rotateToBounds:withAnimator:transitionContext:] + 525
    38  UIKitCore                           0x00007fff480d58e0 -[UIWindow _rotateWindowToOrientation:updateStatusBar:duration:skipCallbacks:] + 2331
    39  UIKitCore                           0x00007fff480d5ed3 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 633
    40  UIKitCore                           0x00007fff480d4e27 -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 119
    41  UIKitCore                           0x00007fff480d3ca4 __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 111
    42  UIKitCore                           0x00007fff480d3bcd -[UIWindow _updateToInterfaceOrientation:duration:force:] + 455
    43  CoreFoundation                      0x00007fff23b9b5bc __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    44  CoreFoundation                      0x00007fff23b9aa35 _CFXRegistrationPost1 + 421
    45  CoreFoundation                      0x00007fff23b9a7a1 ___CFXNotificationPost_block_invoke + 193
    46  CoreFoundation                      0x00007fff23c988b3 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1811
    47  CoreFoundation                      0x00007fff23b9a0f6 _CFXNotificationPost + 950
    48  Foundation                          0x00007fff2574bbf7 -[NSNotificationCenter postNotificationName:object:userInfo:] + 59
    49  UIKitCore                           0x00007fff47cd5955 -[UIDevice setOrientation:animated:] + 224
    50  UIKitCore                           0x00007fff477c83f1 __134-[_UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:]_block_invoke + 137
    51  UIKitCore                           0x00007fff47bd8c83 +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:actions:completion:] + 865
    52  UIKitCore                           0x00007fff47cd2dff _UISceneSettingsDiffActionPerformChangesWithTransitionContext + 240
    53  UIKitCore                           0x00007fff477c8336 -[_UIWindowSceneDeviceOrientationSettingsDiffAction _updateDeviceOrientationWithSettingObserverContext:windowScene:transitionContext:] + 248
    54  UIKitCore                           0x00007fff477c8231 __163-[_UIWindowSceneDeviceOrientationSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke + 112
    55  UIKitCore                           0x00007fff47cd2d02 _UISceneSettingsDiffActionPerformActionsWithDelayForTransitionContext + 84
    56  UIKitCore                           0x00007fff477c80c1 -[_UIWindowSceneDeviceOrientationSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 216
    57  UIKitCore                           0x00007fff476206e7 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke + 657
    58  UIKitCore                           0x00007fff4761f26c -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 248
    59  UIKitCore                           0x00007fff47620411 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 210
    60  UIKitCore                           0x00007fff47bfac66 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 504
    61  FrontBoardServices                  0x00007fff365d6c07 -[FBSSceneImpl updater:didUpdateSettings:withDiff:transitionContext:completion:] + 565
    62  FrontBoardServices                  0x00007fff365fc99f __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_block_invoke_2 + 123
    63  FrontBoardServices                  0x00007fff365e0c45 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 220
    64  FrontBoardServices                  0x00007fff365fc8dc __88-[FBSWorkspaceScenesClient sceneID:updateWithSettingsDiff:transitionContext:completion:]_block_invoke + 196
    65  libdispatch.dylib                   0x000000010610ed48 _dispatch_client_callout + 8
    66  libdispatch.dylib                   0x0000000106111cb9 _dispatch_block_invoke_direct + 300
    67  FrontBoardServices                  0x00007fff3662237e __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    68  FrontBoardServices                  0x00007fff3662206c -[FBSSerialQueue _queue_performNextIfPossible] + 441
    69  FrontBoardServices                  0x00007fff3662257b -[FBSSerialQueue _performNextFromRunLoopSource] + 22
    70  CoreFoundation                      0x00007fff23bd4471 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    71  CoreFoundation                      0x00007fff23bd439c __CFRunLoopDoSource0 + 76
    72  CoreFoundation                      0x00007fff23bd3b74 __CFRunLoopDoSources0 + 180
    73  CoreFoundation                      0x00007fff23bce87f __CFRunLoopRun + 1263
    74  CoreFoundation                      0x00007fff23bce066 CFRunLoopRunSpecific + 438
    75  GraphicsServices                    0x00007fff384c0bb0 GSEventRunModal + 65
    76  UIKitCore                           0x00007fff48092d4d UIApplicationMain + 1621
    77  MyApplication                        0x00000001041a68db main + 107
    78  libdyld.dylib                       0x00007fff5227ec25 start + 1
    79  ???                                 0x0000000000000002 0x0 + 2
)
libc++abi.dylib: terminating with uncaught exception of type NSException

我发现这个崩溃是由于 datePicker(或者可能是其中的表),即 UIPickerTableView 而发生的。 UIPickerTableView 的 self.endUpdates() 行代码崩溃。请找到我创建的虚拟应用的代码:

class ViewController: UITableViewController 


override func viewDidLoad() 
        super.viewDidLoad()

        let datePicker = UIDatePicker()
        datePicker.datePickerMode = .date

        self.tableView.tableFooterView = datePicker

    


extension UITableView 

    open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) 
        super.traitCollectionDidChange(previousTraitCollection)

        self.beginUpdates()
        self.endUpdates()
    




如果有人能帮助我了解这次崩溃的根本原因,我会很高兴。

【问题讨论】:

看看minimal reproducible example ...看看您是否可以在一个简单的、精简的示例中重现崩溃。如果是这样,请编辑您的问题并包含所需的代码。 感谢@DonMag 的建议。我创建了一个带有基本表和 datePicker 的小应用程序来复制崩溃。用所需的代码更新了问题。 嗯...好奇...使用您发布的代码对我来说不会崩溃(Xcode 11.4.1、Swift 5、iPhone 8 模拟器 ios 13.4.1)。更奇怪的是,当我添加你的extension UITableView 时,甚至没有调用traitCollectionDidChange OK - 在装有 iOS 13.3 的 iPhone 7(设备)上,我确实遇到了崩溃。您的分机中的 traitCollectionDidChange 被调用了 10 次......这意味着对 begin/end Updates 的调用也是如此。评论那些 - 不再崩溃。不过,开始使用该扩展名似乎有点奇怪。你确定你需要它吗?或者这是实现它的最佳方式? 一般来说,你调用开始/结束更新来让 tableView 知道你已经改变了一些东西,它需要重新渲染受影响的单元格。如果这对特征改变是必要的(甚至完成了任何事情),我会感到非常惊讶。 【参考方案1】:

在 cmets 和一些测试之后......

崩溃发生在 iOS 13.3 上,但不在 iOS 13.4 上 在 iOS 13.4 上,扩展中的 traitCollectionDidChange 甚至没有被调用 在iOS 13.3上,调用,实际上被调用了10次(我的快速测试观察) 反复调用beginUpdates() endUpdates() 导致崩溃 这些电话可能(几乎可以肯定)一开始就不需要

关于beginUpdates()endUpdates()的一点澄清...

它们告诉表格视图处理/动画对表格的更改而不重新加载单元格。一个常见的用法是使用“回调”闭包来展开/折叠单元格。

假设有一个包含各种 UI 元素的自定义单元格,包括“展开/折叠”按钮。 cellForRowAt 中的代码可能如下所示:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let c = tableView.dequeueReusableCell(withIdentifier: "sCell", for: indexPath) as! SimpleCell
    c.myLabel.text = "\(indexPath)"
    c.callback = 
        c.myHeightConstraint.constant = c.myHeightConstraint.constant == 21 ? 60 : 21
        tableView.beginUpdates()
        tableView.endUpdates()
    
    return c

当在单元格中点击按钮时,它会回调闭包,从而在 21 和 60 之间切换单元格的 myHeightConstraint有了这些调用,我们看到行的高度动画。 没有这些调用,行不会改变高度,直到有其他东西告诉表视图重新计算/重新布局行。

请注意,Apple 的文档声明这些调用正在逐步淘汰/弃用,以支持 performBatchUpdates()

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let c = tableView.dequeueReusableCell(withIdentifier: "sCell", for: indexPath) as! SimpleCell
    c.myLabel.text = "\(indexPath)"
    c.callback = 
        c.myHeightConstraint.constant = c.myHeightConstraint.constant == 21 ? 60 : 21
        tableView.performBatchUpdates(nil, completion: nil)
    
    return c

会完成同样的事情。

因此,由于这些调用没有做任何事情 - 除了导致崩溃 - 听起来它们应该被删除。

【讨论】:

以上是关于在 iOS 13 中,tableView 中 UIDatePicker 的无效更新在旋转时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

一个额外的 UITableViewCellContentView 覆盖出现在 iOS 14 上的 TableView 中,防止点击,但在 iOS 13 上工作正常

在 iOS 13 和 xcode 11 中,tableview 单元格的“setSelected”方法不返回完美的单元格大小

iOS 14 UICollectionView新功能之TableView

iOS UI08_TableView界面传值

iOS开发-UI TableView

iOS开发UI篇-tableView在编辑状态下的批量操作(多选)