IOS 8の新しい自己サイズ変更セルを使用しています。視覚的にはうまく機能します-各セルは適切なサイズになります。ただし、最後の行までスクロールしようとすると、Table Viewは適切なサイズを認識していないようです。これはバグですか、それとも修正されていますか?
このプロジェクトを使用して- TableViewCellWithAutoLayoutiOS8 ( this SO answer から参照)、期待どおりに自動サイズ変更セルを取得しました。
ただし、次のようにscrollToRowAtIndexPath関数を呼び出す場合:
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)
I最後の行に到達しません-途中までしか行けません。
次のような低レベルの関数を使用しようとしても:
tableView.setContentOffset(CGPointMake(0, tableView.contentSize.height - tableView.frame.size.height), animated: true)
結果は期待どおりではなく、最後まで到達しません。何度もクリックするか、しばらく待つと、最終的に正しい場所に移動します。 tableView.contentSize.heightが正しく設定されていないようですので、iOSは最後のセルがどこにあるかを「知りません」。
助けていただければ幸いです。
ありがとう
更新:2015年6月24日
Appleは、iOS 9.0 SDKの時点でこれらのバグのほとんどに対処しています。すべての問題は、アニメーションなしでテーブルビューの上下にスクロールしたり、テーブルビューの中央でスクロールしながらreloadData
を呼び出したりするなど、iOS 9ベータ2で修正されました。
まだ修正されていない残りの問題は次のとおりです。
アニメーションによるスクロールに関連するこれらの問題について、新しいバグレポート(rdar:// 21539211)が提出されました。
元の回答
これはApple Table Viewの行の高さの推定に関するバグであり、この機能がiOS 7で最初に導入されてから存在しました。Apple =この問題に関するUIKitのエンジニアおよび開発者のエバンジェリスト-彼らはそれがバグであることを認めていますが、信頼できる回避策はなく(行の高さの推定を無効にすることはありません)、それを修正することに特に関心はないようです。
バグは、reloadData
を呼び出して部分的または完全に下にスクロールしているときにTable Viewセルが消えるなど、他の方法で現れることに注意してください(例:contentOffset.y
は0を大きく上回っています。
明らかに、iOS 8の自己サイズ変更セルでは、行の高さの見積もりが非常に重要であるため、Appleはこのできるだけ早く対処する必要があります。
この問題は2013年10月21日にレーダー#15283329として提出しました。 Apple修正を優先するように、重複したバグレポートを作成してください。
この単純な サンプルプロジェクト を添付して、問題を示すことができます。 Apple自身のサンプルコードに直接基づいています。
これは非常に迷惑なバグでしたが、永続的な解決策を見つけたと思いますが、その理由を完全に説明することはできません。
小さな(気付かない)遅延の後に関数を呼び出します:
let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)
})
これも同様に機能するかどうか教えてください。
これは間違いなくAppleのバグです。私もこの問題を抱えています。 「scrollToRowAtIndexPath」メソッドを2回呼び出すことでこの問題を解決しました。サンプルコードは次のとおりです。
if array.count > 0 {
let indexPath: NSIndexPath = NSIndexPath(forRow: array.count - 1, inSection: 0)
self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
})
}
Appleが私たちを悩ませてきた多くのバグを修正することを決定するまで役立つ一時的な回避策を見つけました。
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text = [self findTextForIndexPath:indexPath];
UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:13];
CGRect estimatedHeight = [text boundingRectWithSize:CGSizeMake(215, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: font}
context:nil];
return TOP_PADDING + CGRectGetHeight(estimatedHeight) + BOTTOM_PADDING;
}
これは完璧ではありませんが、私にとっては仕事でした。今、私は電話することができます:
- (void)scrollToLastestSeenMessageAnimated:(BOOL)animated
{
NSInteger count = [self tableView:self.tableView numberOfRowsInSection:0];
if (count > 0) {
NSInteger lastPos = MAX(0, count-1);
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:lastPos inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}
}
viewDidLayoutSubviews
で、一番下の正しい場所(または非常に近い推定位置)を見つけます。
それがお役に立てば幸いです。
私の場合、プログラムに推定セルの高さを提案しないことで一時的な回避策を見つけました。これを行うには、コード内で次のメソッドをコメントアウトします。
- (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
ただし、セルが互いに大きく異なる場合、これを行うと、ユーザーがスクロールしたときのユーザーエクスペリエンスに影響する可能性があることに注意してください。私の場合、これまでのところ顕著な違いはありません。
それが役に立てば幸い!
私の解決策は、ストーリーボードのサイズを見積もりとして使用することでした。
したがって、これの代わりに:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
私はこのようなことをしました:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
MyMessageType messageType = [self messageTypeForRowAtIndexPath:indexPath];
switch (messageType) {
case MyMessageTypeText:
return 45;
break;
case MyMessageTypeMaybeWithSomeMediaOrSomethingBiggerThanJustText:
return 96;
break;
default:
break;
}
}
私はチャットテーブルビューを書いているので、特にチャットメッセージが非常に長い場合、多くのセル、特にそのテキストタイプがIBのテキストタイプよりも大きくなる可能性があります。これはかなり良いようです...まあ...下までスクロールするとかなり近づきます。スクロールが長くなると少し悪くなるようですが、それは予想されることです
セルの高さが異なるチャットtableViewを作成するときに同じ問題が発生しました。 viewDidAppear()ライフサイクルメソッドで以下のコードを呼び出します。
// First figure out how many sections there are
let lastSectionIndex = self.tableView.numberOfSections - 1
// Then grab the number of rows in the last section
let lastRowIndex = self.tableView.numberOfRowsInSection(lastSectionIndex) - 1
// Now just construct the index path
let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)
// Make the last row visible
self.tableView.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)
それがあなたにとってもうまくいったかどうかを教えてください。
ストーリーボードウィンドウから空白の領域をクリックしてすべてのビューの選択を解除し、テーブルビューが含まれているビューをクリックしてから[Resolve Auto Layout Issue
アイコンを選択してReset to Suggested Constraints
smileyborg's answer はiOS 8.xのバグですが、サポートしているすべてのプラットフォームで修正する必要があります...
IOS9より前のバージョンで回避するには、以下のコードでdispatch_asyncまたはdispatch_afterを使用せずにトリックを実行します。 iOS 8.4シミュレーターでテスト済み。
[〜#〜] update [〜#〜]:スクロールされるUIPageViewControllerによってView Controllerが表示されるようになると、(のみ)layoutIfNeededの呼び出しは機能しません。そのため、代わりにlayoutSubviews(またはsetNeedsLayout + layoutIfNeeded)を使用してください。
// For iOS 8 bug workaround.
// See https://stackoverflow.com/a/33515872/1474113
- (void)scrollToBottomForPreiOS9
{
CGFloat originalY, scrolledY;
do {
// Lay out visible cells immediately for current contentOffset.
// NOTE: layoutIfNeeded does not work when hosting UIPageViewController is dragged.
[self.tableView layoutSubviews];
originalY = self.tableView.contentOffset.y;
[self scrollToBottom]; // Call -scrollToRowAtIndexPath as usual.
scrolledY = self.tableView.contentOffset.y;
} while (scrolledY > originalY);
}
下にスクロールするにはこの簡単なコードを使用
var rows:NSInteger=self.tableName.numberOfRowsInSection(0)
if(rows > 0)
{
let indexPath = NSIndexPath(forRow: rows-1, inSection: 0)
tableName.scrollToRowAtIndexPath(indexPath , atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
}
}
ViewDidAppearが問題を解決した後、tableview reloadDataを呼び出すだけです。
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}