带有自动布局的 NSInternalInconsistencyException

Posted

技术标签:

【中文标题】带有自动布局的 NSInternalInconsistencyException【英文标题】:NSInternalInconsistencyException with Autolayout 【发布时间】:2014-07-27 10:34:54 【问题描述】:

我正在尝试在我的表格视图控制器中为标题使用自定义标题视图。 我为我的自定义 uitableviewheaderfooterview 创建了一个类。

我的自定义标题视图中有什么?它向默认标题视图添加了一个按钮。

我可以用几个 uitableviewcontrollers 来使用这个自定义标题视图。但似乎我根本无法将它用于某些控制器。 导航控制器的第一个表视图控制器;使用我的自定义视图是可以的。 但是如果我推到下一个视图,我会很快遇到问题..

我没有在这里发布我的自定义类,因为它很长,而且也可以用更简单的变化来模拟。我使用情节提要,在情节提要中,我有一个导航控制器和表格视图控制器,并且在那里使用了我的自定义视图。一个单元格推送到另一个 tableview 控制器。这也使用了自定义类,但是当它被推送时会立即挂起:

 2014-07-27 13:10:04.365 Hours[41029:3941001] *** Terminating app due
 to uncaught exception 'NSInternalInconsistencyException', reason:
 'Modifications to the layout engine must not be performed from a
 background thread.'
 *** First throw call stack: (  0   CoreFoundation                      0x0000000108beec35 __exceptionPreprocess + 165  1   libobjc.A.dylib   
 0x000000010ab76a1c objc_exception_throw + 45   2   CoreFoundation      
 0x0000000108beeb6d +[NSException raise:format:] + 205  3   Foundation 
 0x000000010924decc -[NSISEngine withBehaviors:performModifications:] +
 89     4   UIKit                               0x0000000109f8a2d9
 __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke + 452   5   UIKit                               0x0000000109f8a0ec -[UIView(AdditionalLayoutSupport)
 _switchToLayoutEngine:] + 197  6   UIKit                               0x0000000109f89d88 -[UIView(AdditionalLayoutSupport)
 _initializeHostedLayoutEngine] + 404   7   UIKit                               0x0000000109f8a881 -[UIView(AdditionalLayoutSupport)
 _layoutEngineCreateIfNecessary] + 53   8   UIKit                               0x0000000109f7f53f -[UIView(UIConstraintBasedLayout)
 _withAutomaticEngineOptimizationDisabled:] + 22    9   UIKit                               0x0000000109f7ff20 -[UIView(UIConstraintBasedLayout) addConstraints:]
 + 263  10  Hours                               0x0000000108282db4 _TFC5Hours16EventTypesEditor9tableViewfS0_FTGSQCSo11UITableView_22viewForHeaderInSectionSi_GSQCSo6UIView_
 + 7012     11  Hours                               0x0000000108283e56 _TToFC5Hours16EventTypesEditor9tableViewfS0_FTGSQCSo11UITableView_22viewForHeaderInSectionSi_GSQCSo6UIView_
 + 70   12  UIKit                               0x0000000109a0bb7e -[UITableView _delegateViewForHeaderInSection:] + 45     13  UIKit                               0x0000000109a0f124 __96-[UITableView
 _sectionHeaderView:withFrame:forSection:floating:reuseViewIfPossible:willDisplay:]_block_invoke
 + 97   14  UIKit                               0x000000010998ab9e +[UIView(Animation) performWithoutAnimation:] + 65   15  UIKit                               0x0000000109a0efcf -[UITableView
 _sectionHeaderView:withFrame:forSection:floating:reuseViewIfPossible:willDisplay:]
 + 511  16  UIKit                               0x0000000109a0f8c0 -[UITableView _sectionHeaderViewWithFrame:forSection:floating:reuseViewIfPossible:willDisplay:]
 + 85   17  UIKit                               0x00000001099ef030 -[UITableView _updateVisibleHeadersAndFootersNow:] + 2582    18  UIKit                               0x00000001099f06a5 -[UITableView _updateVisibleCellsNow:isRecursive:]
 + 3714     19  UIKit                               0x0000000109a05fea -[UITableView layoutSubviews] + 213  20  UIKit                               0x0000000109992d25 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
 + 519  21  QuartzCore                          0x00000001097a9058 -[CALayer layoutSublayers] + 150     22  QuartzCore                          0x000000010979dc7e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE +
 380    23  QuartzCore                          0x000000010979daee
 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24     24  QuartzCore                          0x000000010970c8e6
 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242    25  QuartzCore                          0x000000010970d9f2
 _ZN2CA11Transaction6commitEv + 390     26  QuartzCore                          0x000000010970dc27 _ZN2CA11Transaction14release_threadEPv + 199     27 
 libsystem_pthread.dylib             0x000000010b45f54f
 _pthread_tsd_cleanup + 86  28  libsystem_pthread.dylib             0x000000010b45c479 _pthread_exit + 111  29  libsystem_pthread.dylib   
 0x000000010b45c3e9 pthread_exit + 30   30  Foundation                  
 0x0000000109282e51 __NSFinalizeThreadData + 0  31  Foundation         
 0x0000000109262e41 __NSThread__main__ + 1214   32 
 libsystem_pthread.dylib             0x000000010b45b899 _pthread_body +
 138    33  libsystem_pthread.dylib             0x000000010b45b72a
 _pthread_struct_init + 0   34  libsystem_pthread.dylib             0x000000010b45ffc9 thread_start + 13 ) libc++abi.dylib: terminating
 with uncaught exception of type NSException

这是导致此问题的自定义视图的变体:

    let customSection = 0

    override func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! 
        switch section 
        case customSection:
            var h = UITableViewHeaderFooterView()
//          var h = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("Header") as UITableViewHeaderFooterView // This also caused trouble

            h.contentView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
            h.contentView.setTranslatesAutoresizingMaskIntoConstraints(true)

            var t = UILabel(frame: CGRectZero)
            t.text = "Hello"
//          t.sizeToFit()
            t.setTranslatesAutoresizingMaskIntoConstraints(false)
            h.contentView.addSubview(t)

            h.layoutSubviews()

            var views = NSMutableDictionary()
            views.setValue(h.contentView, forKey: "|")
            views.setValue(t, forKey: "label")

            h.contentView.addConstraints([
                NSLayoutConstraint.constraintsWithVisualFormat("H:[label]-|", options: nil, metrics: nil, views: views)
                ])

            h.contentView.addConstraints([
                NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label]", options: nil, metrics: nil, views: views)
                ])


            return h
        default:
            return super.tableView(tableView, viewForHeaderInSection: section)
        
    

可能.. 但不确定。不熟悉线程.. 魔术是如何发生的?这就是..当您使用推送操作点击单元格时.. didselectrowatindexpath 被激活并执行以下操作:

override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) 
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        let cell = tableView.cellForRowAtIndexPath(indexPath)
        cell!.selectCell()
    

现在,选择Cell..

override func selectCell() 

        UIApplication.sharedApplication().sendAction("resignFirstResponder", to: nil, from: nil, forEvent: nil)

        if self.buttonSelector != nil 
            let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.0 * Double(NSEC_PER_MSEC)))
            dispatch_after(time, dispatch_get_main_queue(), 
                NSThread.detachNewThreadSelector(self.buttonSelector, toTarget:self.buttonTarget, withObject: self)
                )
        

    

selectCell 作为空函数包含在 uitableviewcell 的扩展中。 最后,什么设置为 buttonSelector? uitableviewcontroller 中的函数进行推送.. 在这里:

func eventTypeDetails(cell: UIMenuItemCell) NSLog("%@ 的详细信息", (eventTypes.objectAtIndex(cell.tag) as NSArray).objectAtIndex(1) as String)

let cell = self.tableView.dequeueReusableCellWithIdentifier("eventTypeCell") as SettingsActionCell
cell.eventTypesButton!.sendActionsForControlEvents(UIControlEvents.TouchUpInside)

我开始看到是我的覆盖函数 selectCell() 导致了这个问题,但是如何解决这个问题..???简单的修复可能是更改 didselectrowatindexpath 以根据索引路径进行推送。但我想知道这是否可以修复,以便我可以保持我原来的做事方式?毕竟,我有许多基于 uitableviewcell 的自定义类来帮助制作像视图和东西这样的表单。所有都使用自动布局并且都使用相同的方法(其中一些有按钮或其他类型的操作)

【问题讨论】:

您的控制器方法中是否有任何处理推送视图控制器或类似的方法在后台线程中执行? 可能……但不确定。不熟悉线程.. 根据您给出的堆栈跟踪,这肯定是我的猜测。似乎正在创建视图并将其布置在导致问题的主线程之外,因此请查看是否可以找出是否有东西将其推入后台线程。或者强制渲染进入主线程。理论上,您也可能偶然发现了一个 swift/ios 8 错误 我对此表示怀疑.. 我很确定我自己编造了这个错误 :) 【参考方案1】:

好的,我不完全确定你的代码是做什么的,但我认为你把事情复杂化了。

如果您只是尝试推送视图控制器,那么肯定是这种情况。在您的tableView:didSelectRowAtIndexPath: 方法中,您应该可以直接调用导航视图的push 方法,无需直接调用单元格上的任何内容。

如果您想按照您的方式保留代码,那么您的selectCell 函数就是问题所在,因为它似乎本质上是在启动一个后台线程来完成这项工作。在后台线程中执行任何类型的 UI 工作在 iOS 中是一个很大的禁忌。

试试改成

if self.buttonSelector != nil 
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.0 * Double(NSEC_PER_MSEC)))
    dispatch_after(time, dispatch_get_main_queue(), 
        UIApplication.sharedApplication().sendAction(self.buttonSelector, to: self.buttonTarget, from: self, forEvent: nil)
    )

对于目标,您很可能需要使用 NSInvocation,但只需确保它在主线程上

【讨论】:

我不得不为 performSelectorOnMainThread 添加一个参数,waitUntilDone: ,在 Xcode 6 beta 4 上,但它仍然不起作用.. performSelector 方法不可用.. 但这更好用:NSThread.performSelectorOnMainThread(self.buttonSelector, withObject: self, waitUntilDone: false) 但是因为它没有目标.. 发送到类 0x1022e7808 的未识别选择器出错 谢谢.. 已经更好了,但是没有 withObject 可用.. 它是 forEvent: 然后你说: var control : UIControl = UIControl() ?

以上是关于带有自动布局的 NSInternalInconsistencyException的主要内容,如果未能解决你的问题,请参考以下文章

带有 UIScrollView 的纯自动布局

带有自动布局的 uilabel 使用填充调整大小

带有自定义转场的自动布局

带有自动布局的滚动视图表单

带有自动布局(snapkit)的圆形视图?

带有故事板和自动布局的大小值