Xcode 4.6.1 を使用しており、iOS 6.1 アプリを持っています。
別のテーブル ビューにドリルダウンするテーブル ビューがあります。2 番目のテーブル ビューは、カスタム セルを使用してデータを表示します。ただし、2 番目のテーブル ビューには問題があります。通常、そのコンテンツ サイズは正しくありません (具体的には、高すぎるか短すぎる高さ)。これにより、下部に余分なスペースが生じるか、下部までスクロールできる十分なスペースがありません。
下の図は、問題を再現するために作成したテスト アプリの 4 つのスクリーンショットを示しています。私の問題に至るまでの手順を順を追って説明します。
アプリが読み込まれ、最初のテーブルが表示されます。2 番目のテーブルにドリルダウンする 3 つの行があります。
1 行目の「List: 0 to 10」を選択します。ナビゲーション コントローラーは、2 番目のテーブルを画面にプッシュします。11 行あり、(正しくは) 11 行分のコンテンツ サイズがあります。
次に戻って次の行、「リスト: A to P」を選択しました。2 番目のテーブルが再び画面に表示され、16 行になりました。ただし、11 行のコンテンツ サイズしかありません。残りの 5 行はまだ下にありますが、コンテンツのサイズが原因で 11 行目まで戻ってきます。
次に、戻って最後の行である「List: a to f」を選択しました。2 番目のテーブルが再び画面に表示され、6 行あります。ただし、コンテンツ サイズは 11 行のままです。テーブル ビューの下部にスペースがありすぎて、下部に何もない 5 つのセルが表示されます。
ここでのパターンは、コンテンツのサイズが最初にロードされたときのままであるということです。したがって、選択した最初の行は正しいコンテンツ サイズになりますが、他のすべての行は、行数に関係なく、そのサイズのままになります。これはまた、最初に 6 行ある "List: a to f" を選択した場合、他のすべての行のコンテンツ サイズは 6 行になることを意味します。.
私のコードでは、コンテンツのサイズを自分で変更することは決してありません。これはすべて自動的に行われます。テーブル ビューのコンテンツ サイズが変更された時期と理由を把握するために、2 番目のテーブルの「contentSize」プロパティにオブザーバーを設定しました。これを行った後、コンテンツのサイズが同じままではないことに気付きましたが、行を選択したときに実際には2 回変更されていました。古いサイズ (テーブル ビューが表示されたとき)。
だから、なぜ元のサイズに戻ったのかを理解しようとしています。バックグラウンドで何が起こっているかを確認するために、コンテンツのサイズが変化するたびにスタック トレースを出力するようにしました。
以下に、これまでの手順のコンソール ログを示します。ただし、スペースを節約して読みやすくするために、長いスタック トレースを「(スタック トレース タイプ 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 になります。これは、高さ 44 の 11 個のセルを表示するのに十分なスペースです。正常に機能していれば、スタック トレース タイプ Cに関連する観察された変化は発生していません。つまり、タイプ C が問題を引き起こしているのです。
次に、スタック トレースが実際にどのように見えるかを示します。最初の 2 つ (A と B) は問題とは関係がないことに注意してください。問題を引き起こしていると思われるタイプ 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
)
ご覧のとおり、タイプ A またはタイプ B には存在しなかったいくつかの QuartzCore が発生しています。また、タイプ C のいくつかの行では、実際には「OldSize」という単語を含むメソッドが舞台裏で呼び出されています。名前。どうやら、実際に「古いサイズ」に戻しているようです。しかし、なぜこれらの QuartzCore と "OldSize" メソッドがそもそも呼び出されているのか、私にはまだわかりません。
「resizeSubviewsWithOldSize」を調べてみましたが、そこにある唯一のドキュメントは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による「紫」のデフォルトの制約が設定されているため、これは奇妙です。
テーブル ビュー セルの自動レイアウトに関する情報を探し回った後、この質問に出会いました。どうやら、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 で自動レイアウトを有効にすると、テーブルが画面に表示されるときに、テーブル ビューのコンテンツ サイズが誤って「古いサイズ」にサイズ変更されるのはなぜですか?