このエントリーをはてなブックマークに追加

XcodeでCocoa, Swiftを勉強 セルの高さをセル内のテキストの高さに調整する

セル内のテキストが入りきらない。折り返ししてでも全て表示したいって時、ありますよね。
で、テキストを折り返し(wrap)設定にすると、テキストが入るText Fieldはちゃんと折り返ししてる感じなんだけど、セルの高さはそのままでは変わらないもんだから表示されないんですよねー。

たとえばこれ、「あいうえおかきくけこ」って入れたのに「あいうえおかきくけ」までしか表示されないという。


で、色々考えてみた。

1. そもそもセル毎の高さってどうやって変えるのよ?
 tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat
 っていうのがあるようです。これの戻り値がそれぞれのセルの高さになります。
 他の場所、例えばviewForTableColumnの中でtableView.makeViewWithIdentifierを使ってTableCellViewを取得して、それの高さを設定したとしても、セル毎の高さではなく、残念ながら全てのセルの高さに適用されるんですよね。
 だから、どうにかしてText Fieldの高さを取得して、このheightOfRowの戻り値に設定してやれば出来そうなんですよね。

2. どうにかって、どうやってText Fieldの高さを知るのよ?
 はじめは、
 tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
 でCellViewを取得して、これのtextField.bounds.heightを使えばいいんじゃないかと思ったんですけどダメでした。この高さ、テキスト折り返した後も何にも変わってない。
 正解は
 textField.attributedStringValue.boundingRectWithSize(CGSizeMake(
            CellView.frame.width, CGFloat.max), options: .UsesLineFragmentOrigin, context: nil)
 とやって領域情報(NSRect)を取得して、これのheightを使えばよいみたいです。


実際にやったこと
Table View CellのLayoutを「Wraps」に、Line Breakを「Character Wrap」にした。

Autoresizingはこんな感じになっている。おそらくデフォルト。

で、コードは以下のとおり
import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate {

    @IBOutlet weak var window: NSWindow!
    @IBOutlet weak var tableView: NSTableView!
    @IBOutlet weak var txtCell1: NSTextField!
    @IBOutlet weak var txtCell2: NSTextField!
    @IBOutlet weak var btnPost: NSButton!

    //TableViewで扱う値が入った配列
    var tableArray:[NSDictionary] = []
    
    //行数(配列の数)を返す
    func numberOfRowsInTableView(tableView: NSTableView) -> Int
    {
        return self.tableArray.count
    }

    //配列更新時に呼び出される カラムのidentifierを元に、そのカラムのTableCellView内のTextFieldの内容を変更
    func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
    {
        let cellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
        cellView.textField?.stringValue = tableArray[row][tableColumn!.identifier] as! String
        cellView.textField?.frame.size.width = cellView.frame.width //TextFieldをセルの横幅にあわせる
        return cellView
    }
    
    //tableViewセルの高さ指定
    func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
        
        let tableColumn1 = tableView.makeViewWithIdentifier("tableColumn1", owner: self) as! NSTableCellView
        //viewForTableColumnで文字列を入れてもここでは反映されてなくて高さが取得できないので、ここでも入れる
        tableColumn1.textField!.stringValue = tableArray[row]["tableColumn1"] as! String
        let rect1 = tableColumn1.textField!.attributedStringValue.boundingRectWithSize(CGSizeMake(
            tableColumn1.frame.width, CGFloat.max), options: .UsesLineFragmentOrigin, context: nil)

        let tableColumn2 = tableView.makeViewWithIdentifier("tableColumn2", owner: self) as! NSTableCellView
        //viewForTableColumnで文字列を入れてもここでは反映されてなくて高さが取得できないので、ここでも入れる
        tableColumn2.textField!.stringValue = tableArray[row]["tableColumn2"] as! String
        let rect2 = tableColumn2.textField!.attributedStringValue.boundingRectWithSize(CGSizeMake(
            tableColumn2.frame.width, CGFloat.max), options: .UsesLineFragmentOrigin, context: nil)

        if(rect1.size.height > rect2.size.height) {
            return rect1.size.height
        } else {
            return rect2.size.height
        }
    }

    
    @IBAction func btnPost(sender: NSButton) {
        self.tableArray.append(["tableColumn1": self.txtCell1.stringValue, "tableColumn2": self.txtCell2.stringValue])
        self.tableView.reloadData()
    }
    
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // Insert code here to initialize your application
    }

    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }

}

そんなこんなで折り返されたテキストに合わせてセルの高さが変更されました。

めでたしめでたし