1. ホーム
  2. cocoa-touch

[解決済み] iOS 7のUITableViewCellとUITextViewの高さ?

2022-09-01 19:42:55

質問

iOS 7で、UITextViewを含むUITableViewCellの高さを計算するにはどうしたらよいでしょうか。

似たような質問でたくさんの回答がありましたが sizeWithFont: はすべての解決策に関与しており、このメソッドは非推奨です!

を使わなければならないことは分かっています。 - (CGFloat)tableView:heightForRowAtIndexPath: を使用しなければならないことは知っていますが、テキスト全体を表示するために必要なTextViewの高さはどのように計算するのでしょうか?

どのように解決するのですか?

まず最初に、テキストがどのようにレンダリングされるかに関して、UITextView と UILabel の間に大きな違いがあることに注意することが非常に重要です。UITextView はすべての境界線にインセットを持つだけでなく、その内部のテキスト レイアウトもわずかに異なっています。

そのため sizeWithFont: はUITextViewsのために悪い方法です。

代わりに UITextView という関数があります。 sizeThatFits: のすべての内容を表示するのに必要な最小のサイズを返します。 UITextView の全ての内容を表示するのに必要な最小のサイズを返します。

以下はiOS 7とそれ以前のバージョンで同じように動作し、現在のところ、非推奨のメソッドは含まれていません。


簡単な解決方法

- (CGFloat)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width {
    UITextView *calculationView = [[UITextView alloc] init];
    [calculationView setAttributedText:text];
    CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
    return size.height;
}

この関数は NSAttributedString で、希望する幅を CGFloat で、必要な高さを返す


詳細な解決策

私も最近同じようなことをしたので、私が遭遇した接続された問題に対する解決策も共有しようと思いました。誰かの助けになればと思います。

これははるかに深いもので、以下をカバーします。

  • もちろん: 高さを設定する UITableViewCell の内容を完全に表示するために必要なサイズに基づいて、高さを設定することができます。 UITextView
  • テキストの変更に応答する(そして行の高さの変更をアニメーション化する)
  • カーソルを可視領域内に維持し、ファーストレスポンダーを UITextView のサイズを変更する場合 UITableViewCell 編集中

静的なテーブルビューで作業している場合、または、既知の数の UITextView の数が既知である場合、ステップ 2 をより単純化できる可能性があります。

1. まず、heightForRowAtIndexPathを上書きします。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // check here, if it is one of the cells, that needs to be resized
    // to the size of the contained UITextView
    if (  )             
        return [self textViewHeightForRowAtIndexPath:indexPath];
    else
    // return your normal height here:
            return 100.0;           
}

2. 必要な高さを計算する関数を定義する。

を追加する。 NSMutableDictionary (を追加します(この例では textViews ) をインスタンス変数として UITableViewController のサブクラスのインスタンス変数として使用します。

この辞書を使用して、個々の UITextViews のようにします。

(そして、そう indexPaths は辞書の有効なキーです。 )

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Do you cell configuring ...

    [textViews setObject:cell.textView forKey:indexPath];
    [cell.textView setDelegate: self]; // Needed for step 3

    return cell;
}

この関数は、今度は実際の高さを計算します。

- (CGFloat)textViewHeightForRowAtIndexPath: (NSIndexPath*)indexPath {
    UITextView *calculationView = [textViews objectForKey: indexPath];
    CGFloat textViewWidth = calculationView.frame.size.width;
    if (!calculationView.attributedText) {
        // This will be needed on load, when the text view is not inited yet
        
        calculationView = [[UITextView alloc] init];
        calculationView.attributedText = // get the text from your datasource add attributes and insert here
        textViewWidth = 290.0; // Insert the width of your UITextViews or include calculations to set it accordingly
    }
    CGSize size = [calculationView sizeThatFits:CGSizeMake(textViewWidth, FLT_MAX)];
    return size.height;
}

3. 編集中のリサイズを可能にする

次の2つの関数では、デリゲートオブジェクトの UITextViews に設定されていることが重要です。 UITableViewController . もし、デリゲートとして他のものが必要な場合は、そこから関連する呼び出しを行うか、適切なNSNotificationCenterフックを使用することで回避することができます。

- (void)textViewDidChange:(UITextView *)textView {

    [self.tableView beginUpdates]; // This will cause an animated update of
    [self.tableView endUpdates];   // the height of your UITableViewCell

    // If the UITextView is not automatically resized (e.g. through autolayout 
    // constraints), resize it here

    [self scrollToCursorForTextView:textView]; // OPTIONAL: Follow cursor
}

4. カーソルを追いながら編集する

- (void)textViewDidBeginEditing:(UITextView *)textView {
    [self scrollToCursorForTextView:textView];
}

これによって UITableView がUITableViewの可視範囲内にない場合、カーソルの位置までスクロールします。

- (void)scrollToCursorForTextView: (UITextView*)textView {
    
    CGRect cursorRect = [textView caretRectForPosition:textView.selectedTextRange.start];
    
    cursorRect = [self.tableView convertRect:cursorRect fromView:textView];
    
    if (![self rectVisible:cursorRect]) {
        cursorRect.size.height += 8; // To add some space underneath the cursor
        [self.tableView scrollRectToVisible:cursorRect animated:YES];
    }
}

5. インセットの設定による、可視矩形の調整

編集中に UITableView の一部がキーボードで覆われることがあります。テーブルビューのインセットを調整しない場合。 scrollToCursorForTextView: がテーブルビューの下部にある場合、カーソルの位置までスクロールすることができません。

- (void)keyboardWillShow:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, kbSize.height, 0.0);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardWillHide:(NSNotification*)aNotification {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.35];
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, 0.0, 0.0);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
    [UIView commitAnimations];
}

そして最後の部分。

ビューがロードされたら、キーボードの変更に関する通知に登録します。 NSNotificationCenter :

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

この回答が長くなったことで、私に怒らないでください。質問に答えるためにすべてが必要なわけではありませんが、これらの直接関連する問題が役に立つ人が他にいると信じています。


アップデートを行います。

Dave Haupert が指摘したように、私は、(1) と (2) を含めるのを忘れていました。 rectVisible という関数があります。

- (BOOL)rectVisible: (CGRect)rect {
    CGRect visibleRect;
    visibleRect.origin = self.tableView.contentOffset;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size = self.tableView.bounds.size;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
    
    return CGRectContainsRect(visibleRect, rect);
}

また、私が気づいたのは scrollToCursorForTextView: には、私のプロジェクトの TextField の 1 つへの直接参照が含まれていることにも気づきました。もし、あなたが bodyTextView が見つからない場合は、関数の更新されたバージョンを確認してください。