2014年3月6日木曜日

UITableViewのCellいっぱいにUITextViewを貼付けた時のスクロール処理

これから数回にわたってiOSアプリで日本語サイトでは見つからなかったFAQ系をメモっていきます。

今回はUITableViewのCellいっぱいにUITextViewを貼付けた時のカーソル処理です。自動でセルの大きさを広げたり、カーソルの動きに合わせてスクロールしてくれます。UITableViewにUITextViewを入れて編集出来るようなアプリはそれなりに見受けられますがまだの方は参考にどうぞ。

構造は上記のようになっています。


- (void)textViewDidChange:(UITextView *)textView {
    if(textView == self.textInfomation) {
        UITableView* tableView = (UITableView*)[self.view viewWithTag:10];
        UITableViewCell* cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]];

        // 現在のセルの高さを取得
        CGRect cellRect = [tableView rectForRowAtIndexPath:[tableView indexPathForCell:cell]];
        
        // 現在のテキストの高さを計算し取得
        CGSize size = [textView.text getTextSizeWithFont:textView.font viewWidth:289.0f padding:0.0f];
        size.height = size.height + HEIGHT_SPACER;  // HEIGHT_SPACERは適当な値でマージンとして使用

        // セルの高さとテキストの高さを比較して違っていればセルサイズを変更し再描画
        if(size.height != cellRect.size.height){
            // セルの高さを記憶しておく(- tableView:heightForRowAtIndexPath:で使用する)
            [self.aryHeights replaceObjectAtIndex:2 withObject:[NSNumber numberWithFloat:size.height]];
            [tableView beginUpdates];
            [tableView endUpdates];
            [self updateInset];
        }
    }
}

- (void)textViewDidChangeSelection:(UITextView *)textView {
    if(textView.selectedRange.location != NSNotFound) {
        
        NSRange range;
        range.location = textView.selectedRange.location;
        range.length = [textView.text length] - range.location;
        
        // 現在のセルの高さを取得
        UITableView* tView = (UITableView*)[self.view viewWithTag:10];
        UITableViewCell* cell = [tView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]];
        CGRect cellRect = [tView rectForRowAtIndexPath:[tView indexPathForCell:cell]];
        
        // 1行分の高さを事前に計算しておく
        CGSize fontsz = [@"あ" getTextSizeWithFont:textView.font viewWidth:100.0f padding:0.0f];
        
        if(range.length != 0) {
            // カーソル位置までの文字を切り出す
            NSString *string = [textView.text stringByReplacingCharactersInRange:range withString:@""];
            
            // 切り出した文字列の高さを計算する
            CGSize size = [string getTextSizeWithFont:textView.font viewWidth:289.0f padding:0.0f];
            
            // 1行多めに上にスクロールの頂上をオフセットしておく
            size.height = size.height - fontsz.height;
            
            // スクロール位置を設定してスクロール開始
            // fontsz.height*2 はスクロールする底辺からの空間量でここでは2行分空けている(実際には1行と半分位)
            CGRect finalRect = CGRectMake(10.f, cellRect.origin.y + size.height, 300.f, fontsz.height*2);
            [tView scrollRectToVisible:finalRect animated:YES];
        }else if(range.location == [textView.text length]){
            // 最後の位置にカーソルがある場合
            // 全文文字列の高さを計算する
            CGSize size = [textView.text getTextSizeWithFont:textView.font viewWidth:289.0f padding:0.0f];
            
            // スクロール位置を設定してスクロール開始
            // fontsz.height*2 はスクロールする底辺からの空間量でここでは2行分空けている(実際には1行と半分位)
            CGRect finalRect = CGRectMake(10.f, cellRect.origin.y + size.height, 300.f, fontsz.height*2);
            [tView scrollRectToVisible:finalRect animated:YES];
        }
    }
}
ソース内にあるメソッドは下記のとおり。
- (CGSize)getTextSizeWithFont:(UIFont *)font viewWidth:(CGFloat)viewWidth padding:(CGFloat)padding {
    
    CGSize size;
    if ([self respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
        NSDictionary *attributeDic = @{NSFontAttributeName:font};
        size = [self boundingRectWithSize:CGSizeMake(viewWidth, CGFLOAT_MAX)
                                  options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine
                               attributes:attributeDic
                                  context:nil].size;
    }
    size.height += padding;
    return size;
}

0 件のコメント: