NSCollectionView or NSTableView automatic height adjustment of cells - macos

In iOS you could use:
self.tableView.rowHeight = UITableViewAutomaticDimension
to achieve automatic height adjustment of cells.
Is there any way to achieve this behaviour in macOS with NSCollectionView or NSTableView?

Talking about NSTableView... As of El Capitan there was no easy way to do this. Not sure about new Sierra SDK.
From my experience, the most reliable way to regulate table row's height is to use NSTableViewDelegate method:
func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
//Here you do some magic measuring actual content of the cell and returning heigh
}
The only positive thing is that some controls have: fittingSize property and can simplify process.
Also, dynamic content change will require you to call noteHeightOfRowsWithIndexesChanged method of table view, to cause that delegate method be called again where your code will recalculate new height.

With macOS 11 and newer, the situation is as follows:
NSTableView can now actually use auto layout to achieve automatic height adjustment of cells. You must properly configure your cell template(s) to use auto layout, and you must instruct the table to use auto layout (can also be done in the storyboard) for row height calculations:
tableView.usesAutomaticRowHeights = true
NSCollectionView is currently (2021) still behind in features compared to the iOS counterpart, and still doesn't support any tableview feature like automatic row heights.

Related

macOS cocoa TableView with variable Row-Height and custom cells

Situation
I want to create a News-Article table within a macOS Cocoa application. (see image below)
Each Cell consists of two labels. One for the header and one for the main-content.
Problem
Now the length of the news-bodies and the width of the whole TableView is variable. I want to have a variable height on each Row/Cell depending on the size of the labels in it.
What we tried
We tried the following:
We have a
TableView
-> TableColumn
-> Custom View
-> LabelHeader
-> LabelBody
The RowSizeStyle Property on TableView is set to "Automatic"
The cells are populated with a custom class. Basically we tried to implement this StackOverflow solution: Link to Solution -> which did not work for us.
(And many more solutions we could not get to run)
Question
Can anyone provide a working tutorial or solution for this problem? Or at least a promising approach?
PS I use XCode 9.4.1 and Swift 4 on macOS 10.13.5
Any help gladly appreciated, Thank you!
In a modern cocoa application you would use NSLayoutConstraint to implement dynamic height of a table view cell. The tricky part would be to get the height of the text that you can set the height of the constrain. This could be done being using an NSTextView instead of NSTextField and asking the layout manager for the current height.
NSRect usedRect = [[textView layoutManager] usedRectForTextContainer:[self textContainer]];
float newHeight = usedRect.size.height;
I used this in an objective c application some years ago so if you have further questions please let me know.

How to update row heights of NSTableView with usesAutomaticRowHeights = true after column resize?

Since macOS 10.13 we can use NSTableView with automatic row heights, thanks to the new property usesAutomaticRowHeights and of course auto layout. This works quite nicely.
But when the user resizes a column, the calculated heights are no longer correct, and gaps appear in the tableview cells.
Is there a proven way to update the row heights after column resize in this scenario?
I already tried methods like updateConstraintsForSubtreeIfNeeded(), updateConstraints(), setNeedsDisplay(), reloadData() and so on, but nothing works.
If you use NSTextField's in your design, make sure the Desired Width setting of each NSTextField with dynamic height, is set to Automatic. This setting is located in the Size Inspector.
Changing this resulted in automatic recalculation of the tableview row heights.
Something that worked for me was to include the tableViewColumnDidResize notification method in the NSTableviewDelegate. There you can call the noteHeightOfRows method of the table view. Here is an implementation:
//Recalculate when the screen moves
func tableViewColumnDidResize(_ notification: Notification) {
let allIndex = IndexSet(integersIn:0..<self.YOURTABLE.numberOfRows)
YOURTABLE.noteHeightOfRows(withIndexesChanged: allIndex)
}
Here are the links to the documentation: tableViewColumnDidResize and noteHeightOfRows

iOS 8 Self Sizing Cells - Allow Zero Height

I am using the self sizing cell feature and it works well until I want to hide a cell completely. I moved away from heightForRowAtIndexPath for this and I setup the following:
override func viewDidLoad() {
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 0
}
However when I have no text for a tableviewcell to render I get the following message:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
I really just need a way to hide / show content dynamically. I am using a static tableview for this, so maybe I am approaching this wrong?
I found the solution.
First, for a static table to use self sizing correctly, you can only have one label per table cell. I was trying to put in a lot of content into a cell and only the first label would size the cell. I could be wrong about the rule of one label per cell, and the problem might be constraints / auto layout related. I've watched the WWDC video on this, and the way I had it setup, should have worked with my existing constraints, as they where set to the contentView of the cell.
Secondly, the UI updates needed to be coupled with begin and end rules, and a reload.
tableView.beginUpdates()
//-- You Table UI changes
tableView.reloadData()
tableView.endUpdates()
You can also replace reloadData with reloadRowsAtIndexPaths to be specific to the rows to update, but my instance required all rows to be updated.
I had a similar problem some time ago. I would use dynamic cells, and remove the hidden cells in
numberOfRowsInSection
and in
cellForRowAtIndexPath
You should try to add a property, like "hidden" in your cell content array, and check when you load/reload your data. That works (of course) still fine with Autolayout.

How to change NSTableCellView's height when using storyboards in Interface Builder

When I create a non-storyboard OSX app and add a Table View from the Object Library to my main window in Interface Builder, I am able to change the height of an NSTableCellView inside that Table View.
Doing so, automatically changes the TableView's row height (even when I run the app).
But when I create a storyboard app and follow the exact same steps (adding Table View, changing cellView's height), the TableView's row height does not change, resulting in the NSTableCellView being cropped when I run the app.
I know you can implement the heightForRowAtIndexPath method, but I'm just wondering why this works when using xib files and stopped working when using storyboards. (I really find it a lot easier to design interfaces graphically instead of writing down arbitrary numbers in a text file.)
Is there something I'm missing here? What is the easiest way of doing this, using storyboards?
You have to set the height in the table view (size inspector -> size style: custom and set row height value).

Can you use Storyboards to set auto-layout constraints on the contentView of a CollectionViewCell, as to take advantage of self-sizing cells in iOS 8?

I'm using Xcode 6 beta 5 at the moment, eager to try out the cool self-sizing collection view cells functionality introduced in iOS 8. It was introduced in the WWDC 2014 session 226: "What's New In Table Views and Collection Views". Links:
https://developer.apple.com/videos/wwdc/2014/
http://asciiwwdc.com/2014/sessions/226
http://blog.indragie.com/
All you gotta do, they said, is that you have to set auto-layout constraints on the contentView of your collection view cell or implement sizeThatFits:. The former sounds easier, so I definitely want to use auto-layout.
When got into Storyboard editor (or IB) though, it appears that you you cannot access the contentView property of a prototype collection view cell. Is this true?
I did set a few constraints between a label (direct subview of the prototype cell) — I pinned its four edges to the cell's bounds itself, hoping that the label's intrinsicSize would provide the needed width for the cell. No avail: I verified that none of these pinning constraints were applied onto the contentView:
- (void)awakeFromNib
NSLog(#"%s... contentView.constrants == %#", sel_getName(_cmd), self.contentView.constraints);
}
... prints out...
awakeFromNib... contentView.constrants == (
)
... no matter what constraints I set in Storyboard.
Am I missing something or must I do this auto-layout in code?

Resources