web-dev-qa-db-ja.com

iOS 8自動セルの高さ-最後の行までスクロールできない

IOS 8の新しい自己サイズ変更セルを使用しています。視覚的にはうまく機能します-各セルは適切なサイズになります。ただし、最後の行までスクロールしようとすると、Table Viewは適切なサイズを認識していないようです。これはバグですか、それとも修正されていますか?

問題を再現する方法は次のとおりです。

このプロジェクトを使用して- TableViewCellWithAutoLayoutiOS8this 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は最後のセルがどこにあるかを「知りません」。

助けていただければ幸いです。

ありがとう

57
BenB

更新:2015年6月24日

Appleは、iOS 9.0 SDKの時点でこれらのバグのほとんどに対処しています。すべての問題は、アニメーションなしでテーブルビューの上下にスクロールしたり、テーブルビューの中央でスクロールしながらreloadDataを呼び出したりするなど、iOS 9ベータ2で修正されました。

まだ修正されていない残りの問題は次のとおりです。

  1. 大きな行の推定高さを使用する場合、アニメーションを使用して最後の行までスクロールすると、テーブルビューのセルが消えます。
  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自身のサンプルコードに直接基づいています。

32
smileyborg

これは非常に迷惑なバグでしたが、永続的な解決策を見つけたと思いますが、その理由を完全に説明することはできません。

小さな(気付かない)遅延の後に関数を呼び出します:

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)
})

これも同様に機能するかどうか教えてください。

20
abinop

これは間違いなく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)
        })
    }
13
Bhavesh Tiwari

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で、一番下の正しい場所(または非常に近い推定位置)を見つけます。

それがお役に立てば幸いです。

4
Gui Moura

私の場合、プログラムに推定セルの高さを提案しないことで一時的な回避策を見つけました。これを行うには、コード内で次のメソッドをコメントアウトします。

- (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

ただし、セルが互いに大きく異なる場合、これを行うと、ユーザーがスクロールしたときのユーザーエクスペリエンスに影響する可能性があることに注意してください。私の場合、これまでのところ顕著な違いはありません。

それが役に立てば幸い!

1
Marcus

私の解決策は、ストーリーボードのサイズを見積もりとして使用することでした。

したがって、これの代わりに:

- (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のテキストタイプよりも大きくなる可能性があります。これはかなり良いようです...まあ...下までスクロールするとかなり近づきます。スクロールが長くなると少し悪くなるようですが、それは予想されることです

0

セルの高さが異なるチャット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)

それがあなたにとってもうまくいったかどうかを教えてください。

0
Stefan Olaru

ストーリーボードウィンドウから空白の領域をクリックしてすべてのビューの選択を解除し、テーブルビューが含まれているビューをクリックしてから[Resolve Auto Layout Issueアイコンを選択してReset to Suggested Constraints

enter image description hereenter image description here

0
kendotwill

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);
}
0
ypresto

下にスクロールするにはこの簡単なコードを使用

 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)
        }
            }
0
Bibin Joseph

ViewDidAppearが問題を解決した後、tableview reloadDataを呼び出すだけです。

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}
0
Evan JIANG