16
votes

I am fairly new to OS X Cocoa programming but have decided to give it a go with the new Swift language.

I have an NSTableView with 1500 rows (will be more) and 7 columns. There is one checkbox column and the rest are text fields, one with a date formate and one a currency formatter. I first set this up as cell based. Scrolling was buttery smooth (I even did a test adding 1 million rows, still smooth). This was under mavericks.

I then upgraded to Yosemite, scrolling performance significantly degraded. Enabling Core animation layer checkbox on the table view improved this but was still worse than in mavericks.

During my reading trying to improve scroll performance in Yosemite I came across "View-Based" NSTableViews. From the documentation it said cell based table views should generally not be used and are only supported for legacy projects.

I therefore converted my table to a View based table view. Sample simple concept, nothing complicated. The scrolling performance is absolutely terrible. If you scroll very slow it is smooth enough but as soon as you start to scroll faster its like it hasn't buffered enough and it starts stuttering and jerking. Also when the NSTableview is populated, focusing and defocusing the window takes a second or more (I tried it in mavericks again and this was not present, scrolling was also a little better, but still nowhere near cell based).

Are view based NSTableviews always bad for scrolling performance? If so why do apple recommend using them over cell based NSTableviews.

Also some applications like safari and Reeder2 have buttery smooth scrolling even in Yosemite. How do they achieve this?

Am I missing something or is the performance of OS X just going to hell with each new thing? I.e

Mavericks > Yosemite

Cell-Based > View-Based

Old > New

Any help is much appreciated. Thanks!

5
Did you solve this problem?Fabian Zeindl
Nope. Unfortunately not. I have stopped development and will try again at next version of OS X before calling it quits.Gavin
Did you report a bug on radar? Not that Apple would care about, but just out of curiosity. I feel really, really pissed by Apple. As long as I just use TextFields in my large table it's smooth, but once I add just a Colorbox/TextField view it starts eating all CPU while scrolling. Yosemite sucks >:-(qwerty_so
@Gavin did you find a solution? I hit the same issue even in macOS 10.11 and 10.12. View-based table views are extremely slow if there are 1000+ rows.Ethan

5 Answers

4
votes

According to Apple, OSX never enable QuartzCore by default (as iOS, instead, do). So, you need to:

  • link against QuartzCore.framework your project under Build Settings pane.
  • enable CoreAnimation Layer (under View Effects Inspector on IB) for your main window (if possible, otherwise be sure to enable it on the container view that give you poor performances).

Quoting Apple docs:

In iOS apps, Core Animation is always enabled and every view is backed by a layer. In OS X, apps must explicitly enable Core Animation support by doing the following:

Link against the QuartzCore framework. (iOS apps must link against this framework only if they use Core Animation interfaces explicitly.) Enable layer support for one or more of your NSView objects by doing one of the following:

In your nib files, use the View Effects inspector to enable layer support for your views. The inspector displays checkboxes for the selected view and its subviews. It is recommended that you enable layer support in the content view of your window whenever possible. For views you create programmatically, call the view’s setWantsLayer: method and pass a value of YES to indicate that the view should use layers. Enabling layer support in one of the preceding ways creates a layer-backed view. With a layer-backed view, the system takes responsibility for creating the underlying layer object and for keeping that layer updated. In OS X, it is also possible to create a layer-hosting view, whereby your app actually creates and manages the underlying layer object. (You cannot create layer-hosting views in iOS.) For more information on how to create a layer-hosting view, see “Layer Hosting Lets You Change the Layer Object in OS X.”

More on: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/SettingUpLayerObjects/SettingUpLayerObjects.html

3
votes

I recently found myself in a very similar situation, having a cell based NSOutlineView that was blazing fast pre-Yosemite and almost crawled to a halt after upgrading to Yosemite.

In my experience, enabling layer-backing on the table view itself is not enough, you also have to enable it for the NSScrollView that contains your table view and it's NSClipView.

After making those changes my table view was up to speed again but experiencing some strange visual artifacts. Those might or might not be relevant in your situation since you're not using an source list NSOutlineView that uses the new vibrancy/transparency stuff in Yosemite.

In any case, those "effects" went away as soon as I converted to a view based table.

0
votes

Recently they have introduced the concept called "Reuse queues", where in only the rows which are currently visible are actually there in the table. The rows which go out of the clip, while scrolling are replaced by the newly introduced rows. This was introduced for performance enhancements. But considering million x 7 views, I suppose there might be a lot of CPU cycles wasted in reuse queue.
Also cells are light weight as compared to views.


If possible you can consider, restructuring the single tableview to multiple tableviews. Practically human being needs to view the data with atleast some simple predicate in their mind. For example, in an organization you might want to view

  1. All manager details
  2. All employees having salary greater than x
  3. All female employees

instead of viewing all employee details at a time.
Thus initializing a table view, only with the required information would enhance both, the performance and UX.

0
votes

I've got a similar problem. I'm also new to Swift and Apple development in general. My project was working well until I started adding a 1000 or more rows.

After some playing around, I've narrowed the issue down to the way that my dictionary based data source is being accessed. This might point you in the right direction.

 func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {...

 return myDictionary.values.array[row] //-- this makes access very slow, jerky with pauses

return myDictionary[someKey] //-- this makes access a lot faster, smooth and slick

Looks like my data source needs some re-work. It still hasn't been tested with large quantities of data, but at the outset seems to make a significant difference.

0
votes

You need to simply turn off the "Draws Background" on the text cell (or the group cell)

enter image description here