gpt4 book ai didi

cocoa-touch - 为什么表格 View 单元格中的自动布局会导致表格的内容大小错误地更改?

转载 作者:行者123 更新时间:2023-12-01 05:22:50 25 4
gpt4 key购买 nike

我正在使用Xcode 4.6.1,并且有一个iOS 6.1应用程序。

它具有一个表视图,可深入到另一个表视图。第二个表视图使用自定义单元格显示其数据。但是,第二个表视图存在一个问题:它的内容大小通常是错误的(特别是高度,它太高或太短)。这要么导致底部有多余的空间,要么没有足够的空间可以滚动到底部。

下图显示了我制作的测试应用程序的四个屏幕截图,该截图再现了该问题。我将逐步介绍导致我遇到问题的步骤。




该应用会加载,并显示第一个表格。它具有三行,可深入到第二个表。
我选择第一行,即“列表:0至10”。导航控制器将第二张桌子推到屏幕上。它具有11行,并且(正确)具有11行的内容大小。
然后,我返回并选择了下一行,即“列表:A至P”。第二个表再次出现在屏幕上,现在有16行。但是,它只有11行的内容大小。其他5行仍位于下方,但内容大小不断使我跳回第11行。
然后我返回并选择了最后一行,即“列表:a至f”。第二个表再次出现在屏幕上,它有6行。但是,它仍然具有11行的内容大小。表格视图底部的空间太大,底部显示5个单元格,其中没有任何内容。


这里的模式是内容大小保持最初加载时的大小。因此,我选择的第一行将具有正确的内容大小,但是所有其他行现在将保持该大小,而不管它们有多少行。这也意味着如果我首先选择了“ List:a to f”(具有6行),那么所有其他内容的内容大小将为6行,因为它的大小与我选择的第一行的大小相同。

在我的代码中,我从未更改自己的内容大小;这都是自动发生的。为了弄清楚何时以及为什么表视图更改其内容大小,我在第二个表的“ contentSize”属性上设置了一个观察者。完成此操作后,我意识到内容大小并没有保持不变,但是当我选择一行时,它实际上被更改了两次:首先更改为正确的大小(重新加载表格视图时),然后又恢复为正确的大小。旧尺寸(出现表格视图时)。

因此,我试图弄清为什么将其还原为旧大小。为了了解幕后情况,每次观察到内容大小变化时,我都将其打印出堆栈轨迹。

在下面,您可以查看控制台日志,以了解之前完成的所有步骤。但是,为了节省空间并使其更具可读性,我将长堆栈跟踪替换为“(堆栈跟踪类型A)”之类的消息。稍后我将向您显示它们的详细信息,但现在我仅将它们称为堆栈跟踪类型A,B或C。

first table: selected row 0 (List: 0 to 10)
second table: viewDidLoad
OBSERVED CHANGE: contentSize.height = 484 (row count = 11)
(Stack Trace Type A)
OBSERVED CHANGE: contentSize.height = 484 (row count = 11)
(Stack Trace Type B)
second table: viewWillAppear
OBSERVED CHANGE: contentSize.height = 484 (row count = 11)
(Stack Trace Type C)
second table: viewDidAppear
*going back to first table*
second table: viewWillDisappear
second table: viewDidDisappear

first table: selected row 1 (List: A to P)
OBSERVED CHANGE: contentSize.height = 704 (row count = 16)
(Stack Trace Type A)
second table: viewWillAppear
OBSERVED CHANGE: contentSize.height = 484 (row count = 16)
(Stack Trace Type C)
second table: viewDidAppear
*going back to first table*
second table: viewWillDisappear
second table: viewDidDisappear

first table: selected row 2 (List: a to f)
OBSERVED CHANGE: contentSize.height = 264 (row count = 6)
(Stack Trace Type A)
second table: viewWillAppear
OBSERVED CHANGE: contentSize.height = 484 (row count = 6)
(Stack Trace Type C)
second table: viewDidAppear
*going back to first table*
second table: viewWillDisappear
second table: viewDidDisappear


如您所见,它正在将内容大小恢复为旧大小。在这种情况下,它将是484,这足够显示11个高度为44的单元格的空间。如果一切正常,则观察到的与Stack Trace Type C关联的更改将不会发生。因此,换句话说,类型C是导致问题的原因。

现在,我将向您展示堆栈跟踪的实际外观。请注意,前两个(A和B)与问题无关。我只显示它们,以便您可以将它们与Type C进行比较/对比,这似乎是导致问题的原因。

(堆栈跟踪类型A)
当表视图由于重新加载而更改了其内容大小时,堆栈跟踪看起来像这样。

(
0 CustomTableCellTest 0x00002fe0 -[MasterViewController observeValueForKeyPath:ofObject:change:context:] + 320
1 Foundation 0x00b0d417 NSKeyValueNotifyObserver + 357
2 Foundation 0x00b26b24 NSKeyValueDidChange + 456
3 Foundation 0x00adbd60 -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 131
4 Foundation 0x00b48b80 _NSSetSizeValueAndNotify + 185
5 UIKit 0x000b57ef -[UITableView(_UITableViewPrivate) _updateContentSize] + 782
6 UIKit 0x000c3974 -[UITableView noteNumberOfRowsChanged] + 154
7 UIKit 0x000c32dc -[UITableView reloadData] + 769
8 CustomTableCellTest 0x00004d97 -[MetaMasterViewController tableView:didSelectRowAtIndexPath:] + 567
9 UIKit 0x000c7285 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1194
10 UIKit 0x000c74ed -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 201
11 Foundation 0x00ad15b3 __NSFireDelayedPerform + 380
12 CoreFoundation 0x01c55376 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
13 CoreFoundation 0x01c54e06 __CFRunLoopDoTimer + 534
14 CoreFoundation 0x01c3ca82 __CFRunLoopRun + 1810
15 CoreFoundation 0x01c3bf44 CFRunLoopRunSpecific + 276
16 CoreFoundation 0x01c3be1b CFRunLoopRunInMode + 123
17 GraphicsServices 0x01bf07e3 GSEventRunModal + 88
18 GraphicsServices 0x01bf0668 GSEventRun + 104
19 UIKit 0x00017ffc UIApplicationMain + 1211
20 CustomTableCellTest 0x000021ed main + 141
21 CustomTableCellTest 0x00002115 start + 53
)


(堆栈跟踪类型B)
该事件仅在表视图第一次出现时发生,因此仅发生一次。我相信它是在表最初加载其视图时发生的。

(
0 CustomTableCellTest 0x00002fe0 -[MasterViewController observeValueForKeyPath:ofObject:change:context:] + 320
1 Foundation 0x00b0d417 NSKeyValueNotifyObserver + 357
2 Foundation 0x00b26b24 NSKeyValueDidChange + 456
3 Foundation 0x00adbd60 -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 131
4 Foundation 0x00b48b80 _NSSetSizeValueAndNotify + 185
5 UIKit 0x000b57ef -[UITableView(_UITableViewPrivate) _updateContentSize] + 782
6 UIKit 0x000cbc8e -[UITableView _rectChangedWithNewSize:oldSize:] + 261
7 UIKit 0x000cc231 -[UITableView setFrame:] + 279
8 UIKit 0x000f5014 +[UIViewControllerWrapperView wrapperViewForView:frame:] + 448
9 UIKit 0x00110e18 -[UINavigationController _startTransition:fromViewController:toViewController:] + 239
10 UIKit 0x0011189b -[UINavigationController _startDeferredTransitionIfNeeded:] + 386
11 UIKit 0x00111e93 -[UINavigationController pushViewController:transition:forceImmediate:] + 1030
12 UIKit 0x00111a88 -[UINavigationController pushViewController:animated:] + 62
13 CustomTableCellTest 0x00004e21 -[MetaMasterViewController tableView:didSelectRowAtIndexPath:] + 705
14 UIKit 0x000c7285 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1194
15 UIKit 0x000c74ed -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 201
16 Foundation 0x00ad15b3 __NSFireDelayedPerform + 380
17 CoreFoundation 0x01c55376 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
18 CoreFoundation 0x01c54e06 __CFRunLoopDoTimer + 534
19 CoreFoundation 0x01c3ca82 __CFRunLoopRun + 1810
20 CoreFoundation 0x01c3bf44 CFRunLoopRunSpecific + 276
21 CoreFoundation 0x01c3be1b CFRunLoopRunInMode + 123
22 GraphicsServices 0x01bf07e3 GSEventRunModal + 88
23 GraphicsServices 0x01bf0668 GSEventRun + 104
24 UIKit 0x00017ffc UIApplicationMain + 1211
25 CustomTableCellTest 0x000021ed main + 141
26 CustomTableCellTest 0x00002115 start + 53
)


因此,当事情正常运行时,类型A和B都会出现。但是,当事情不能正常工作时,我也会得到下一个堆栈跟踪。

(堆栈跟踪类型C)
这是将内容大小恢复为旧大小时发生的堆栈跟踪。

(
0 CustomTableCellTest 0x00003080 -[MasterViewController observeValueForKeyPath:ofObject:change:context:] + 320
1 Foundation 0x00b0d417 NSKeyValueNotifyObserver + 357
2 Foundation 0x00b26b24 NSKeyValueDidChange + 456
3 Foundation 0x00adbd60 -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 131
4 Foundation 0x00b48b80 _NSSetSizeValueAndNotify + 185
5 UIKit 0x0007c213 -[UIScrollView _resizeWithOldSuperviewSize:] + 161
6 UIKit 0x0005cf2a -[UIView(Geometry) resizeWithOldSuperviewSize:] + 72
7 UIKit 0x0005bb28 __46-[UIView(Geometry) resizeSubviewsWithOldSize:]_block_invoke_0 + 80
8 CoreFoundation 0x01cb85a7 __NSArrayChunkIterate + 359
9 CoreFoundation 0x01c9003f __NSArrayEnumerate + 1023
10 CoreFoundation 0x01c8fa16 -[NSArray enumerateObjectsWithOptions:usingBlock:] + 102
11 UIKit 0x0005babf -[UIView(Geometry) resizeSubviewsWithOldSize:] + 149
12 UIKit 0x00557dcc -[UIView(AdditionalLayoutSupport) _is_layout] + 143
13 UIKit 0x000607ae -[UIView(Hierarchy) layoutSubviews] + 80
14 UIKit 0x000682dd -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 279
15 libobjc.A.dylib 0x010e76b0 -[NSObject performSelector:withObject:] + 70
16 QuartzCore 0x02292fc0 -[CALayer layoutSublayers] + 240
17 QuartzCore 0x0228733c _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 468
18 QuartzCore 0x02287150 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
19 QuartzCore 0x022050bc _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 324
20 QuartzCore 0x02206227 _ZN2CA11Transaction6commitEv + 395
21 QuartzCore 0x022068e2 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 96
22 CoreFoundation 0x01c5eafe __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
23 CoreFoundation 0x01c5ea3d __CFRunLoopDoObservers + 381
24 CoreFoundation 0x01c3c7c2 __CFRunLoopRun + 1106
25 CoreFoundation 0x01c3bf44 CFRunLoopRunSpecific + 276
26 CoreFoundation 0x01c3be1b CFRunLoopRunInMode + 123
27 GraphicsServices 0x01bf07e3 GSEventRunModal + 88
28 GraphicsServices 0x01bf0668 GSEventRun + 104
29 UIKit 0x00017ffc UIApplicationMain + 1211
30 CustomTableCellTest 0x000021ed main + 141
31 CustomTableCellTest 0x00002115 start + 53
)


如您所见,Type A或B中没有出现某些QuartzCore东西。而且,在Type C中的几行中,实际上在幕后调用了一些方法,这些方法的名称中包含“ OldSize”名称。因此,显然它确实是将其还原为“旧尺寸”。但是,我仍然不明白为什么首先要调用这些QuartzCore和“ OldSize”方法。

我尝试查找“ resizeSubviewsWithOldSize”,但是我只能在它上找到的 documentation是NSView(用于OS X应用程序,而不是使用UIView的iOS应用程序)。文档中的此说明并没有真正帮助我理解为什么在这种情况下调用它。

但是,尽管我不知道为什么要调用它,但我对何时调用它有一个很好的了解。通过更多的测试,我意识到,只要出现表视图,有时甚至消失,它都会恢复为旧的内容大小。

例如,当我使模式视图覆盖屏幕然后消失时,内容大小实际上已更改了3倍。日志消息看起来像这样。

*Modal view entering*
second table: viewWillDisappear
OBSERVED CHANGE: contentSize.height = 484 (row count = 11)
(Stack Trace Type C)
second table: viewDidDisappear

*Modal view exiting*
second table: viewWillAppear
OBSERVED CHANGE: contentSize.height = 484 (row count = 11)
(Stack Trace Type C)
second table: viewDidAppear
OBSERVED CHANGE: contentSize.height = 484 (row count = 11)
(Stack Trace Type C)


因此,更改与重新加载表视图无关:它与显示和消失有关。我猜测视图每次出现时都会尝试对其子视图进行布局,因此自定义单元格的布局似乎存在问题。

最初,我认为问题在于我的自定义单元的设置方式:在我的代码中还是在xib中。这是因为当我不使用自定义单元格时问题就消失了。但是有一次我在自定义单元xib中关闭了自动布局,问题也消失了。因此,显然问题与自动布局有关。

为了方便调试,我在xib中设置单元格的方法非常简单:单元格上唯一的东西是带有灰色背景色的UILabel。所有自动布局约束都是Interface Builder的默认约束。您可以在下面看到我的自定义单元xib。



我还尝试删除UILabel,以便该单元格上没有任何内容,并且我保留了自动布局打开的功能。当我这样做时,问题也消失了。所以我想在单元上设置UILabel的方式有问题。这很奇怪,因为它是由Interface Builder设置的那些“紫色”默认约束设置的,我无法删除。

在查找有关表格视图单元格中自动布局的信息之后,我遇到了 this question。显然,Interface Builder没有将其约束正确设置到单元格的内容视图。我不确定这是否与我的问题有关,但是我还是尝试了一下。我使用了艾德里安(Adrian)发布的解决方案,它看起来像这样。

//  MasterCell.m
// My custom UITableViewCell subclass

- (void)awakeFromNib {
[super awakeFromNib];

for (NSLayoutConstraint *cellConstraint in self.constraints) {

[self removeConstraint:cellConstraint];

id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;

NSLayoutConstraint *contentViewConstraint =
[NSLayoutConstraint constraintWithItem:firstItem
attribute:cellConstraint.firstAttribute
relatedBy:cellConstraint.relation
toItem:seccondItem
attribute:cellConstraint.secondAttribute
multiplier:cellConstraint.multiplier
constant:cellConstraint.constant];

[self.contentView addConstraint:contentViewConstraint];
}
}


但是,尽管它似乎可以完成预期的工作(即,将约束更改为与内容视图相关,而不是与单元格本身相关),但这仍然无法解决我的问题:内容大小仍在还原为旧尺寸。

因此,总而言之,为了澄清这个相当长的问题,这基本上就是我要问的问题。

为什么当我在自定义表格单元xib中启用自动布局时,表格出现在屏幕上时表格视图的内容大小被错误地重新调整为“旧大小”?

最佳答案

您正在为“主”表重新使用表视图控制器的相同实例,但是您正在按似乎错误的顺序向其发送消息。

您像这样推动控制器:

if (!self.masterViewController) {
self.masterViewController = [[MasterViewController alloc] init];
}

self.masterViewController.objects = self.masterObjects[indexPath.row];

[self.masterViewController.tableView reloadData];
[self.navigationController pushViewController:self.masterViewController animated:YES];


如果您只是在reloadData和推行之间交换,那么问题就消失了。

我不完全确定这里发生了什么,但是我认为问题是,一旦弹出主控制器,就没有任何参照系(双关语意!)来重新加载时计算适当的内容大小,因此它可能不会为此新高度缓存内容大小。

然后,当推入视图时,滚动视图突然又具有上下文,它知道它已经重新计算,因此使用缓存的内容大小。无论如何,这就是我从堆栈跟踪中得到的印象(与您一样)。您选择的第一个表始终很好,因为没有可用的旧大小。如果使用情节提要进行此操作,则每次都会实例化一个新的Master控制器,而您永远不会注意到问题。

至于为什么自动布局会产生任何不同,那么,我只是在这里猜测和挥手,但是在自动布局下,与在显式布局下相比,在视图控制器生命周期中发生的一切都晚得多。如果您一直使用自动布局,则在没有超级视图(因此对其大小没有限制)的视图中,自动布局无法计算出应设置的大小。这可能是一个错误,但是解决方法很简单,而且正如我所说,通常在将新的视图控制器推入导航堆栈时,您会初始化新的视图控制器,而当它们弹出时它们就会消失,因此您永远不会看到这个。

关于cocoa-touch - 为什么表格 View 单元格中的自动布局会导致表格的内容大小错误地更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15583887/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com