概要
- 下記のようにウィンドウの大きさに関わらず、テキストが全文表示されるようにしたいです。
- そのためにはテーブルの行の高さを都度変更しないといけません。
- macOS 10.13から簡単に設定できるようになっていたので、その方法とそれを使わない方法の2つを紹介します。


GitHub
参考
-
XcodeでCocoa, Swiftを勉強 セルの高さをセル内のテキストの高さに調整する
- 肝のアイディアはこちらから。
- 上の記事ではAutoLayoutは使っていないので補完します。
はじめは、
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を使えばよいみたいです。
-
tableViewColumnDidResize(_:)
- テーブルサイズが変更されたときに
reloadData
を呼ぶことで、サイズを変更した際に対応します
- テーブルサイズが変更されたときに
-
Macアプリ初心者:NSTableView でカスタムした一覧を作ってみる
- カスタムした
TableCellView
クラスの使用方法
- カスタムした
-
Auto-resizing NSTokenField with constraint-based layout
-
cellSizeForBounds
も使えそう?
-
-
https://lipsum.sugutsukaeru.jp/index.cgi
- ダミーテキスト作成に使用しました
実装例
1. NSTableViewの設定で簡単に
解説
- Auto Layoutを設定してやり
Row Size Style
でAutomatic(Auto Layout)
を設定することで、自動でコンテンツに合わせた行の高さにすることができます。 -
View-based NSTableView with rows that have dynamic heights
- macOS 10.13から実装されたそうで、非常にありがたいです。
This got a lot easier in macOS 10.13 with .usesAutomaticRowHeights
- 横幅の下限・上限は下記より設定できます。
つまづきポイント
- よくわからない点ですが、テキストオブジェクトを
Label
(Libraryボタンから導入)で置き換えると文章が折り返しされません。

- 下記をインポートし、クラスを
init
(これは何でしょうか?)からNSTextField
に変更して使うとうまくいきました。 - 設定値は同じ用にしたつもりだが、何か見落としていたのかもしれませんね。

2. 行の高さを計算する
解説
-
NSTableViewDelegate
にoptional func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat
というのがあり、これで各行の高さを指定することができます。 - 行の高さの計算方法は、泥臭いですが
NSTableView
の幅からNSTextField
の高さを算出し、Constraints
の値を考慮して算出します。 - 肝心の
NSTextField
の高さの求め方ですが、行数は固定ではなく、幅によって折返しがあるのでそこを考慮する必要があります。 - そこで
boundingRect
を使用します。- テキストを表示する範囲を指定して、実際に描画される範囲が取得できます。
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "FirstColumn"), owner: self) as! SecondViewControllerTableCellView
cellView.nameTextField.stringValue = String(format: "%d番目の人", row)
cellView.messageTextField.stringValue = messageList[row] // 計算用
let widthOfMessageTextField = cellView.calculateWidthOfMessageTextField(tableView.frame.width)
let widthOfNameTextField = cellView.calculateWidthOfNameTextField(tableView.frame.width)
// 指定範囲でのNSTextFieldの描画予定範囲を取得し、そこからテーブルに必要な高さを計算する
let messageTextFieldRect = cellView.messageTextField.attributedStringValue.boundingRect(with: CGSize(width: widthOfMessageTextField,
height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
let nameTextFieldRect = cellView.messageTextField.attributedStringValue.boundingRect(with: CGSize(width: widthOfNameTextField,
height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
return cellView.nameTextFieldTopConstraint.constant
+ nameTextFieldRect.height
+ cellView.messageTextFieldTopConstraint.constant
+ messageTextFieldRect.height
+ cellView.messageTextFieldBottomConstraint.constants
}
- また
NSTableCellView
のカスタムクラスを作成し、Constraints
の値をとるために@IBOutlet
を定義しています。
class SecondViewControllerTableCellView: NSTableCellView {
@IBOutlet var myImageViewHeightConstraint: NSLayoutConstraint!
@IBOutlet var myImageViewWidthConstraint: NSLayoutConstraint!
つまづきポイント
-
cellView
の大きさを使えばいいのでは?と思っていたのですが、このメソッド内で取れる大きさはあくまでIB上で表示されている設定値で、実際の大きさではないようでした。-
optional func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
内では実際のサイズが取得できてそうでした。
-
- NSTableCellView のサイズを自動的に調整させてみる
実際のところは、テキストを設定してもすぐには frame にサイズが反映されなくて、View の fittingSize を呼び出すことで、テキストの量に応じたサイズを取得できる様子でした。
- ↑がうまく取れませんでした…。
- また
NSTextField
のsizeToFit()
も折り返しを考慮しない場合の値となるので今回の使用には適していません。