How to Create a Custom Nstablecellview from a Nib

How to create a custom NSTableCellView from a NIB?

When editing a xib, the File's Owner proxy object represents the object which is passed as owner to makeView(withIdentifier:owner:) at runtime. In this case the owner object is the view controller. You can set the class of the File's Owner in the xib to TableViewController and connect actions. You can't set the class of the File's Owner to CustomTableCellView and connect outlets. Instead connect the outlets of the CustomTableCellView view object.

Custom NSTableView with custom NSTableCellView?

This is what I ended up doing :

Of course you have to subclass NSTableCellView and return it like I did below. If you are familiar with table views in iOS you should be familiar with methods like:

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
//this will be called first. It will tell the table how many cells your table view will have to display
return [arrayToDisplay count];

}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {

//this is called after the count of rows is set. It will populate your table according to the data your array to display contains

[tableView setTarget:self];
[tableView setAction:@selector(click)];

NSString *identifier = [tableColumn identifier];

if ([identifier isEqualToString:@"TheCell"]) {

CustomCell *cellView = [tableView makeViewWithIdentifier:identifier owner:self];
cellView.cellText.stringValue = [arrayToDisplay objectAtIndex:row];
return cellView;
}

return nil;
}

And the click method that is triggered when a row is selected would look like this:

-(void)click{

int index = [table selectedRow];

// Do something with your data
//e.g
[[arrayToDisplay objectAtIndex:index] findHiggsBoson];

}

And something that has to be added to the NSTableView:

NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"column"];
column.width = self.frame.size.width;
[tableView addTableColumn:column];

Create a custom cell in a NSTableView

  1. In the Xib, add a NSTableView and make sure the contentType is View Based in the Attributes Inspector pane.
  2. The Table column contains a TableCellview, which contains a TableViewCell by default. Remove the TableViewCell.
  3. Drag the NSTextFields and ImageViews into the TableCellview as needed.
    By default, NSTableCellview supports 1 Imageview and 1 Textfield. If you need two of each, inherit NSTableCellview and create IBOutlet properties for your components and change the class of NSTableCellview in IB to InheritedTableCellview.

    @interface InheritedTableCellview : NSTableCellView

    @property (assign) IBOutlet NSTextField *secondTextField;
    @property (assign) IBOutlet NSImageView *secondImageView;

    @end

    @implementation SRITableCellView

    @end
  4. Name the identifier of TableCellview with unique string.

  5. Connect the Imageview and Textfield outlet components to TableCellview.
  6. Connect the tableview Datasource and delegate to the viewController.

In the view controller, implement the below datasource method for display the number of rows required.

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return self.tableArray.count;
}

Implement the delegate method to set the image and Text for each row,

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
InheritedTableCellview *cellView = [tableView makeViewWithIdentifier:@"MainCell" owner:self];
cellView.backgroundStyle = NSBackgroundStyleDark;
cellView.textField.stringValue = self.tableArray[row][@"textValue1"];
cellView.imageView.image = [NSImage imageNamed:self.tableArray[row][@"image1"]];
cellView.secondTextField.stringValue = self.tableArray[row][@"textValue2"];
cellView.secondImageView.image = [NSImage imageNamed:self.tableArray[row][@"image2"]];

return cellView;
}

NSTableView don`t display NSTableCellView instantiated from xib

Ok my bad.

I should have to use tableView:viewForTableColumn:row: instead of tableView:objectValueForTableColumn:row:, which i chose automatically from data source (lets call it iOS to OSX syndrome).

Still do not understand why tableView:viewForTableColumn:row: is in delegate but not in data source.

Getting position of custom NSTableCellView within NSTableView

Going off of the convo with @KenThomases where the best way would be to have the table view cell itself manage the popover, but that's only half-possible. I'll explain:

Since tables redraw the table cell views, the contactTableCellView can't promise that it exist for the entire lifetime of the NSPopover. In order for the popover to exist with a lifecycle unrelated to the contactTableCellView was to have an NSPopover object exist in my class holding the NSTableView (lets call it addressBookViewController). So addressBookViewController will have a member named contactPopover and as long as the addressBookViewController is still in memory, we can be sure the popover will exist. The problem with this was getting the right bounds for the popover to link to.

If you pass the current contactTableCellView back to the addressBookViewController and just use something like contactPopover.show(relativeTo:contactTableCellView.bounds of:contactTableCellView preferredEdge:.minY) as @KenThomases mentioned, you'll see that the popover gets stuck to the the first/top element in the NSTableView no matter what element I hover over, but I want the popover to point to the element that is hovered over.

I couldn't find any reference material to prove my hypothesis, but from my outcome, it seems that the NSTableView has cells/views that contain the custom ContactTableCellView I created. This is the reason that I was getting "contactTableCellView.frame always has the (x,y) = (0,0)". The frame is always relative to the parent object, which in this case is a view/cell with the same size as the TableCellView. And this outer view had the frame that I wanted/expected contactTableCellView.frame to have. To say this clearly:

contactTableCellView.superView?.frame gave the correct (x,y) in the NSTableView and this frame isn't deleted as contactTableCellView gets redrawn for various reasons. Using my findings, I was able to get a popover appear relative to the correct element like I wanted roughly like this in Swift 3:

class AddressBookViewController {
...
var contactCardPopover: NSPopover?
...

func showContact(sender: AnyObject, contact: ContactVM) {
guard let contactTableCellView = sender as? ContactTableCellView else { return }

// when contactCardVC is non-nil, a popover currently open. This makes sure 2 don't open in the same tableView.
if contactViewController == nil {
contactCardViewController = ContactViewController(contact: contact)

// this here is the important part!!!! Using the contactTableCellView.superview
if let tableCell = participantTableCellView.superview {

contactCardPopover = NSPopover(contentViewController: contactCardViewController, sender: tableCell, bounds: tableCell.frame)
contactCardPopover.delegate = self
}
}
}

}

So the superview of the ContactTableCellView has the correct frame, so my NSPopover works on the custom NSTableCellView like I originally wanted! It just took a few hours to figure out this specific part.



Related Topics



Leave a reply



Submit