带有自动布局的 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:以上是关于带有自动布局的 NSInternalInconsistencyException的主要内容,如果未能解决你的问题,请参考以下文章