{"id":3642,"date":"2014-04-28T08:14:20","date_gmt":"2014-04-28T16:14:20","guid":{"rendered":"http:\/\/www.corbinstreehouse.com\/blog\/2014\/04\/nstableview-tips-view-controller-per-row\/"},"modified":"2018-11-03T13:36:24","modified_gmt":"2018-11-03T20:36:24","slug":"nstableview-tips-view-controller-per-row","status":"publish","type":"post","link":"https:\/\/www.corbinstreehouse.com\/blog\/2014\/04\/nstableview-tips-view-controller-per-row\/","title":{"rendered":"NSTableView Tips: View Controller per row"},"content":{"rendered":"<p id=\"top\" \/>\n<p>Sometimes you may find your application has rather complex rows, and you want an NSViewController to manage each row in a &#8220;View Based&#8221; NSTableView. This is fairly easy to do, and I did it in my Cyr Wheel pattern editing application. My &#8220;Cell View&#8221; that I want for each row was designed in Xcode like:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.corbinstreehouse.com\/blog\/wp-content\/uploads\/2014\/04\/Screen-Shot-2014-04-26-at-8.52.47-PM.png\" width=\"871\" height=\"129\" alt=\"Screen Shot 2014-04-26 at 8.52.47 PM.png\" \/><\/p>\n<p>A simple NSViewController subclass:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow:auto;width:auto;;\">\n<pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #000080; font-weight: bold\">@interface<\/span> CDPatternItemViewController : NSViewController\r\n   <span style=\"color: #008800; font-style: italic\">\/\/ the thing we are editing<\/span>\r\n   <span style=\"color: #000080; font-weight: bold\">@property<\/span>(weak) CDPatternItem *patternItem;\r\n<span style=\"color: #000080; font-weight: bold\">@end<\/span>\r\n<\/pre>\n<\/div>\n<p>\nThe controller code is what is interesting. I use an NSWindowController subclass, and keep an array of the view controllers:<\/p>\n<p>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow:auto;width:auto;;\">\n<pre style=\"margin: 0; line-height: 125%\">\r\n<span style=\"color: #000080; font-weight: bold\">@interface<\/span> CDPatternEditorWindowController () {\r\n<span style=\"color: #000080; font-weight: bold\">@private<\/span>\r\n  NSMutableArray *_patternViewControllers;\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<br \/>\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<\/pre>\n<pre style=\"margin: 0px;\">\r\n<font face=\"Helvetica\"><span style=\"white-space: normal;\">I decided to lazily load the view controllers. On initialization\/load of my window I fill the array with NULL:<\/span><\/font>\r\n<\/pre>\n<pre style=\"margin: 0px;\">\r\n<font face=\"Helvetica\"><br \/><\/font>\r\n<\/pre>\n<\/div>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow:auto;width:auto;;\">\n<pre style=\"margin: 0; line-height: 125%\">\r\n<span style=\"color: #000080; font-weight: bold\">-<\/span> (<span style=\"color: #000080; font-weight: bold\">void<\/span>)_resetPatternViewControllers {\r\n  _patternViewControllers = NSMutableArray.new;\r\n<span style=\"color: #000080; font-weight: bold\">  for<\/span> (NSInteger i = <span style=\"color: #0000FF\">0<\/span>; i &lt; self._patternSequence.children.count; i++) {\r\n    [_patternViewControllers addObject:[NSNull null]]; <span style=\"color: #008800; font-style: italic\">\/\/ placeholder<\/span>\r\n  }\r\n  [_tableView reloadData];\r\n}\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<br \/>\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<\/pre>\n<pre style=\"line-height: normal; margin-top: 0px; margin-bottom: 0px;\">\r\n<\/pre>\n<pre style=\"margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><span style=\"white-space: normal;\">_patternSequence is simply a wrapper around my model object, stored in my Core Data backed NSDocument; it has an array of things I want to show in the table<\/span><\/font>\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<\/pre>\n<\/div>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow:auto;width:auto;;\">\n<pre style=\"margin: 0; line-height: 125%\">\r\n<span style=\"color: #000080; font-weight: bold\">-<\/span> (CDPatternSequence *)_patternSequence {\r\n<span style=\"color: #000080; font-weight: bold\">   return<\/span> self.document.patternSequence;\r\n}\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<br \/>\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<\/pre>\n<pre style=\"line-height: normal; margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><span style=\"white-space: normal;\">Then, the view based tableview method that requests the view simply creates and caches it:<\/span><\/font>\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<\/pre>\n<pre style=\"line-height: normal; margin-top: 0px; margin-bottom: 0px;\">\r\n<\/pre>\n<\/div>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow:auto;width:auto;;\">\n<pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #000080; font-weight: bold\">-<\/span> (CDPatternItemViewController *)_patternViewControllerAtIndex:(NSInteger)index {\r\n    <span style=\"color: #000080; font-weight: bold\">id<\/span> currentObject = [_patternViewControllers objectAtIndex:index];\r\n    CDPatternItemViewController *result;\r\n    <span style=\"color: #000080; font-weight: bold\">if<\/span> (currentObject == [NSNull null]) {\r\n        CDPatternItem *item = [self._patternSequence.children objectAtIndex:index];\r\n        result = [CDPatternItemViewController new];\r\n        result.patternItem = item;\r\n        [_patternViewControllers replaceObjectAtIndex:index withObject:result];\r\n    } <span style=\"color: #000080; font-weight: bold\">else<\/span> {\r\n        result = (CDPatternItemViewController *)currentObject;\r\n    }\r\n    <span style=\"color: #000080; font-weight: bold\">return<\/span> result;\r\n}\r\n\r\n<span style=\"color: #000080; font-weight: bold\">-<\/span> (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {\r\n    CDPatternItemViewController *vc = [self _patternViewControllerAtIndex:row];\r\n    <span style=\"color: #008800; font-style: italic\">\/\/ don&#39;t set the identifier so it isn&#39;t reused...<\/span>\r\n    <span style=\"color: #000080; font-weight: bold\">return<\/span> vc.view;\r\n}\r\n<\/pre>\n<\/div>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<br \/>\r\n<\/pre>\n<pre style=\"margin: 0; line-height: 125%\">\r\n<br \/>\r\n<\/pre>\n<pre style=\"margin: 0px;\">\r\n<\/pre>\n<pre style=\"margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><span style=\"white-space: normal;\">This should be fairly straightforward, and easy to read. But if you have questions let me know and I will clarify.<\/span><\/font>\r\n<\/pre>\n<pre style=\"margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><br \/><\/font>\r\n<\/pre>\n<pre style=\"margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><span style=\"white-space: normal;\">I <\/span><b style=\"white-space: normal;\">don't<\/b><span style=\"white-space: normal;\"> cleanup the view controllers when the items are removed from the table. I could use the NSTableView delegate method tableView:didRemoveRowView:forRow: to drop my NSViewController cache at this time if I so wanted. However, my table is small..and I don't bother with this.<\/span><\/font>\r\n<\/pre>\n<pre style=\"margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><br \/><\/font>\r\n<\/pre>\n<pre style=\"margin-top: 0px; margin-bottom: 0px;\">\r\n<font face=\"Helvetica\"><br \/><\/font>\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes you may find your application has rather complex rows, and you want an NSViewController to manage each row in a &#8220;View Based&#8221; NSTableView. This is fairly easy to do, and I did it in&#8230; <a class=\"read-more\" href=\"https:\/\/www.corbinstreehouse.com\/blog\/2014\/04\/nstableview-tips-view-controller-per-row\/\">[read more]<\/a><\/p>\n","protected":false},"author":1,"featured_media":3641,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[6,86],"tags":[63,72],"class_list":["post-3642","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cocoa","category-coding","tag-cocoa","tag-programming"],"jetpack_featured_media_url":"https:\/\/www.corbinstreehouse.com\/blog\/wp-content\/uploads\/2014\/04\/Screen-Shot-2014-04-26-at-8.52.47-PM.png","jetpack_shortlink":"https:\/\/wp.me\/p8zn47-WK","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts\/3642","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/comments?post=3642"}],"version-history":[{"count":3,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts\/3642\/revisions"}],"predecessor-version":[{"id":5169,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts\/3642\/revisions\/5169"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/media\/3641"}],"wp:attachment":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/media?parent=3642"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/categories?post=3642"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/tags?post=3642"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}