adding KVO to UITableViewCell - objective-c

I have a custom UITableViewCell which is displaying various attributes of a Person object (backed by Core Data) ... some labels, images etc. I currently force the whole tableview to reload whenever any property changes, and that's obviously not efficient. I know with KVO, I should be able to add a listener to a label in the cell that can listen for changes in the Person's properties. But I'm not sure how to implement it and can't find any examples.
Here's what I typically do in my UITableView's cellForRowAtIndexPath:
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
static NSString *simple = #"CustomCellId";
CustomCell *cell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:simple];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for (id findCell in nib )
{
if ( [findCell isKindOfClass: [CustomCell class]])
{
cell = findCell;
}
}
}
Person *managedObject = [self.someArray objectAtIndex: indexPath.row];
cell.namelabel.text = managedObject.displayName;
return cell;
}
The cell is hooked up in IB. I would want to detect when displayName changes, and update just the name label.
Thanks

The above answer is great for static cells. Using KVO for UITableViewCells still works with cell reuse. Add the observers you need when the cell is about to appear, and remove them when the cell is no longer displayed. The only trick is that Apple seems to be inconsistent about sending didEndDisplayingCell:, so observers need to be removed in two places on iOS 6.1
#implementation MyTableViewCell
#property MyTableViewController * __weak parentTVC;
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
((MyTableViewCell *)cell).parentTVC = self;
// Don't add observers, or the app may crash later when cells are recycled
}
- (void)tableView:(UITableView *)tableView
willDisplayCell:(HKTimelineCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Add observers
}
- (void)tableView:(UITableView *)tableView
didEndDisplayingCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self removeMyKVOObservers];
}
- (void)viewWillDisappear:(BOOL)animated
{
for (MyTableViewCell *cell in self.visibleCells) {
// note! didEndDisplayingCell: isn't sent when the entire controller is going away!
[self removeMyKVOObservers];
}
}
The following can occur if observers aren't cleaned up. The observer might try to notify whatever object is at that memory location, which may not even exist.
<NSKeyValueObservationInfo 0x1d6e4860> (
<NSKeyValueObservance 0x1d4ea9f0: Observer: 0x1d6c9540, Key path: someKeyPath, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1c5c7e60>
<NSKeyValueObservance 0x1d1bff10: Observer: 0x1d6c9540, Key path: someOtherKeyPath, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1c588290>)

For background, you probably want to read the Key-Value Observing and Key-Value Coding Guides, if you haven't already. Then review the NSKeyValueObserving category methods.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html
In a nutshell, you need to carefully manage adding and removing the observing object to the observed objects list of observers (pardon the seeming redundancy of that statement). You don't want to have an object going away with observers still registered, or you get complaints and possible other issues.
That said, you use -addObserver:keyPath:options:context to add an object as an observer. Context should be a statically declared string. The options argument controls what data you get back in your observation method (see below). The keyPath is the path of property names from the observed object to the observed property (this may traverse multiple objects, and will be updated when intermediate objects change, not just when the leaf property changes).
In your case, you could observe the label, and use the text keyPath, or the cell, and use the nameLabel.text key path. If the table view class were designed differently, you might observe the entire array of cells, but there is no such property on UITableView. The problem with observing the cell is that the table view might delete it at any time (if your design uses multiple cells that serve the same purpose in a variable-length list). If you know your cells are static, you can probably observe them without worry.
Once you have an observer registered, that observer must implement
-observeValueForKeyPath:ofObject:change:context:, confirm that the context matches (just compare the pointer value to your static string's address; otherwise, invoke super's implementation), then look into the change dictionary for the data you want (or just ask the object for it directly) and use it to update your model as you see fit.
There are many examples of KVO in sample code, including on Apple's developer site, and as part of the bindings samples on Malcolm Crawford (mmalc)'s site, but most of it is for Mac OS X, not iOS.

This works:
In configureCell:
[managedObject addObserver: cell forKeyPath: #"displayName" options:NSKeyValueObservingOptionNew context: #"Context"];
In CustomCell:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
Person *label = (Person *) object;
self.namelabel.text = [label valueForKey:#"displayName"];
}

In my case I added an observer to the custom cell label forKeyPath "text" with options (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld).
When observing the value for the keyPath I check to ensure the keyPath is the one I want, just as an extra measure and then I call my method for what ever operation I want to carry out on that label
e.g in my case
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Helpers
CGSize cellSize = self.contentView.frame.size;
CGRect sizerFrame = CGRectZero;
sizerFrame.origin.x = kDefaultUITableViewCellContentLeftInset;
sizerFrame.origin.y = kDefaultUITableViewCellContentTopInset;
// The Profile Image
CGRect imageFrame = CGRectMake(sizerFrame.origin.x, sizerFrame.origin.y, kDefaultProfilePictureSizeBWidth, kDefaultProfilePictureSizeBHeight);
self.userProfilePictureUIImageView = [[UIImageView alloc] initWithFrame:imageFrame];
[self.userProfilePictureUIImageView setImage:[UIImage imageNamed:#"placeholderImage"]];
[ApplicationUtilities formatViewLayer:self.userProfilePictureUIImageView withBorderRadius:4.0];
// adjust the image content mode based on the lenght of it's sides
CGSize avatarSize = self.userProfilePictureUIImageView.image.size;
if (avatarSize.width < avatarSize.height) {
[self.userProfilePictureUIImageView setContentMode:UIViewContentModeScaleAspectFill];
} else {
[self.userProfilePictureUIImageView setContentMode:UIViewContentModeScaleAspectFit];
}
CGFloat readStateSize = 10.0;
CGRect readStateFrame = CGRectMake((imageFrame.origin.x + imageFrame.size.width) - readStateSize, CGRectGetMaxY(imageFrame) + 4, readStateSize, readStateSize);
// Read State
self.readStateUIImageView = [[UIImageView alloc] initWithFrame:readStateFrame];
self.readStateUIImageView.backgroundColor = RGBA2UIColor(0.0, 157.0, 255.0, 1.0);
[ApplicationUtilities formatViewLayer:self.readStateUIImageView withBorderRadius:readStateSize/2];
sizerFrame.origin.x = CGRectGetMaxX(imageFrame) + kDefaultViewContentHorizontalSpacing;
// read just the width of the senders label based on the width of the message label
CGRect messageLabelFrame = sizerFrame;
messageLabelFrame.size.width = cellSize.width - (CGRectGetMinX(messageLabelFrame) + kDefaultViewContentHorizontalSpacing);
messageLabelFrame.size.height = kDefaultInitialUILabelHeight;
// Store the original frame for resizing
initialLabelFrame = messageLabelFrame;
self.messageLabel = [[UILabel alloc]initWithFrame:messageLabelFrame];
[self.messageLabel setBackgroundColor:[UIColor clearColor]];
[self.messageLabel setFont:[UIFont systemFontOfSize:14.0]];
[self.messageLabel setTextColor:[UIColor blackColor]];
[self.messageLabel setNumberOfLines:2];
[self.messageLabel setText:#""];
// Modify Sizer Frame for Message Date Label
sizerFrame = initialLabelFrame;
// Modify the y offset
sizerFrame.origin.y = CGRectGetMaxY(sizerFrame) + kDefaultViewContentVerticalSpacing;
// Message Date
self.messageDateLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.messageDateLabel setBackgroundColor:[UIColor clearColor]];
[self.messageDateLabel setFont:[UIFont systemFontOfSize:12.0]];
[self.messageDateLabel setTextColor:RGBA2UIColor(200.0, 200.0, 200.0, 1.0)];
[self.messageDateLabel setHighlightedTextColor:[UIColor whiteColor]];
[self.messageDateLabel setTextAlignment:NSTextAlignmentRight];
[self.messageDateLabel setNumberOfLines:1];
[self.messageDateLabel setText:#"Message Date"];
[self.messageDateLabel sizeToFit];
[self.contentView addSubview:self.userProfilePictureUIImageView];
[self.contentView addSubview:self.readStateUIImageView];
[self.contentView addSubview:self.messageDateLabel];
[self.contentView addSubview:self.messageLabel];
// Add KVO for all text labels
[self.messageDateLabel addObserver:self forKeyPath:#"text" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL];
[self.messageLabel addObserver:self forKeyPath:#"text" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL];
}
return self;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqual:#"text"]) {
[self resizeCellObjects];
}
}
-(void)resizeCellObjects
{
// Resize and reposition the message label
CGRect messageLabelFrame = initialLabelFrame;
self.messageLabel.frame = messageLabelFrame;
[self.messageLabel setNumberOfLines:2];
[self.messageLabel sizeToFit];
// Resize the messageDate label
CGRect messageDateFrame = initialLabelFrame;
messageDateFrame.origin.y = CGRectGetMaxY(self.messageLabel.frame) + kDefaultViewContentVerticalSpacing;
self.messageDateLabel.frame = messageDateFrame;
[self.messageDateLabel sizeToFit];
}

Related

NSScrollView displaying problems in Xcode 5 after scrolling

NSScrollView displaying NSTableView based on custom view (Image, checkbox and text label). When I scroll - I have lag (bug?) with redrawing rows.
Normal:
Bugged after scroll:
Project (and .xib file) was updated from Xcode 4 to Xcode 5 format. I think this bug appeared after it, but I'm not sure.
Any suggestions how to fix it?
Realisation of NSTableViewDataSource, NSTableViewDelegate protocols:
- (NSInteger) numberOfRowsInTableView: (NSTableView *) aTableView {
return [arrayOfObjects count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
CMListItem *currentObject = [arrayOfObjects objectAtIndex: row];
NSString *currentName = [currentObject name];
BOOL currentState = [currentObject state];
NSImage *currentImage = [currentObject getArtwork];
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"MainCell"]) {
CMTableCellView *cellView = [tableView makeViewWithIdentifier: identifier owner: self];
cellView.textField.stringValue = currentName;
cellView.button.state = currentState;
cellView.imageView.image = currentImage;
return cellView;
}
return nil;
}
NSScrollView not modified.
My own table cell view - subclass of NSTableCellView with few additional outlets.
UPDATE: this thing helped me: set Can Draw Concurrently for all NSTableView at On state, then turn it to Off state.
I think I have seen this problem sometimes when "Copy On Scroll" is enabled. Try disabling this option in IB (in the attribute inspector, scrollview section, behavior).

Determine when UITableViewCell is deallocated

I am using core data in my app along with NSFetchedResultsController to populate a table. My database has 40k+ entries so the table is rather long. Each table cell has a thumbnail image that is loaded from the web using SDWebImage. All works great if I scroll slowly, if I begin to scroll fast within a couple of seconds I get a crash.
NSZombies isn't showing anything useful.
I'm guessing that it has to do with SDWebImage and loading from the web. The way SDWebImage works is by loading the image in the background then setting the downloaded image after it completes downloading (wordy). My thought is that the cells are being deallocated by the UITableView, then SDWebImage tries to set the image on the deallocated cell. So if I can determine when the UITableViewCell is going to be deallocated I can stop the SDWebImage downloading process and hopefully fix the issue.
I've tried to add
- (void)dealloc {
NSLog(#"dealloc");
}
to catch when the cell is going to be deallocated but I never get anything.
EDIT
I have my -(void)dealloc method in a subclass UITableViewCell.
EDIT
Here is where/how I create the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* inventoryCellID = #"InventoryCustomCellID";
InventoryCustomCell* cell = (InventoryCustomCell *)[tableView dequeueReusableCellWithIdentifier:inventoryCellID forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(InventoryCustomCell *)cell atIndexPath:(NSIndexPath *)indexPath {
[cell formatCellWithProduct:[fetchedResultsController objectAtIndexPath:indexPath] enableAdding:NO];
cell.openThumbnailButton.tag = indexPath.row;
[cell.openThumbnailButton addTarget:self action:#selector(presentThumbnailViewWithCell:) forControlEvents:UIControlEventTouchUpInside];
}
In my custom cell this is the configuration method being called:
- (void)formatCellWithProduct:(Product*)product enableAdding:(bool)addingEnabled {
self.titleLabel.text = product.part_number;
self.partNumberLabel.text = [[[product.manufacturer allObjects] objectAtIndex:0] name];
//The SDWebImage UIImageView category method
[self.thumbImageView setImageWithURL:[NSURL URLWithString:product.photo] placeholderImage:[UIImage imageNamed:#"icon.png"]];
}
EDIT
Here is the SDWebImage method that downloads the image and sets it.
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock;
{
[self cancelCurrentImageLoad];
self.image = placeholder;
if (url)
{
__weak UIImageView *wself = self;
id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
__strong UIImageView *sself = wself;
if (!sself) return;
if (image)
{
sself.image = image;
[sself setNeedsLayout];
}
if (completedBlock && finished)
{
completedBlock(image, error, cacheType);
}
}];
objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
Table views don't tend to allocate and deallocate table view cells much. Creating cells is expensive, so they get reused when possible, rather than being discarded when they go off screen.
The UITableViewDelegate method -tableView:didEndDisplayingCell:forRowAtIndexPath: is the better place to update cells to cancel downloads or other no-longer-relevant operations.
It does look like each call to -setImageWithURL:etc:etc: is trying to cancel previous downloads for that image view, though.
You didn't explain where did you put dealloc method..
I consider you can try to add a category to your viewcontroller for debugging just for test if your cell's deallocation is called (not tu subclass UITableviewCell) .
For example:
#implementation UITableViewCell(Dealloc)
- (void)dealloc {
NSLog(#"dealloc");
[super dealloc];
}
#end
Do you use ARC? If no than you forgot
InventoryCustomCell* cell = (InventoryCustomCell *)[[tableView dequeueReusableCellWithIdentifier:inventoryCellID forIndexPath:indexPath] autorelease];

Creating No Empty Selections in NSCollectionView

I have set up an NSCollectionView in a cocoa application. I have subclassed the collection view's NSCollectionViewItem to send me a custom NSNotification when one of its views it selected / deselected. I register to receive a notification within my controller object when this notification is posted. Within this method I tell the view that has just been selected that it is selected and tell it to redraw, which makes it shade itself grey.
The NSCollectionViewItem Subclass:
-(void)setSelected:(BOOL)flag {
[super setSelected:flag];
[[NSNotificationCenter defaultCenter] postNotificationName:#"ASCollectionViewItemSetSelected"
object:nil
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:(ASListView *)self.view, #"view",
[NSNumber numberWithBool:flag], #"flag", nil]];}
The Controller Class (in the -(void)awakeFromNib Method):
//Register for selection changed notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(selectionChanged:)
name:#"ASCollectionViewItemSetSelected"
object:nil];
And the -(void)selectionChanged:(NSNotification *)notification method:
- (void)selectionChanged:(NSNotification *)notification {
// * * Must get the selected item and set its properties accordingly
//Get the flag
NSNumber *flagNumber = [notification.userInfo objectForKey:#"flag"];
BOOL flag = flagNumber.boolValue;
//Get the view
ASListView *listView = [notification.userInfo objectForKey:#"view"];
//Set the view's selected property
[listView setIsSelected:flag];
[listView setNeedsDisplay:YES];
//Log for testing
NSLog(#"SelectionChanged to: %d on view: %#", flag, listView);}
The application that contains this code requires there to be no empty selection within the collection view at any time. This is where i get my problem. I've tried checking when a view's selection is changed and reselecting it if there is no selection, and manually selecting the views using NSCollectionView's
-(void)setSelectionIndexes:(NSIndexSet *)indexes
But there is always a situation which occurs that causes there to be an empty selection in the collection view.
So I was wondering if there is an easier way to prevent an empty selection occurring in an NSCollectionView? I see no checkbox in interface builder.
Thanks in advance!
Ben
Update
I ended up just subclassing my NSCollectionView, and overriding the - (void)mouseDown:(NSEvent *)theEvent method. I only then sent the method [super mouseDown:theEvent]; if the click was in one of the subviews. Code:
- (void)mouseDown:(NSEvent *)theEvent {
NSPoint clickPoint = [self convertPoint:theEvent.locationInWindow fromView:nil];
int i = 0;
for (NSView *view in self.subviews) {
if (NSPointInRect(clickPoint, view.frame)) {
//Click is in rect
i = 1;
}
}
//The click wasnt in any of the rects
if (i != 0) {
[super mouseDown:theEvent];
}}
I ended up just subclassing my NSCollectionView, and overriding the - (void)mouseDown:(NSEvent *)theEvent method. I only then sent the method [super mouseDown:theEvent]; if the click was in one of the subviews. Code:
- (void)mouseDown:(NSEvent *)theEvent {
NSPoint clickPoint = [self convertPoint:theEvent.locationInWindow fromView:nil];
int i = 0;
for (NSView *view in self.subviews) {
if (NSPointInRect(clickPoint, view.frame)) {
//Click is in rect
i = 1;
}
}
//The click wasnt in any of the rects
if (i != 0) {
[super mouseDown:theEvent];
}}
I also wanted to avoid empty selection in my collection view.
The way I did it is also by subclassing, but I overrode -hitTest: instead of -mouseDown: to return nil in case the click wasn't on an item :
-(NSView *)hitTest:(NSPoint)aPoint {
// convert aPoint in self coordinate system
NSPoint localPoint = [self convertPoint:aPoint fromView:[self superview]];
// get the item count
NSUInteger itemCount = [[self content] count];
for(NSUInteger itemIndex = 0; itemIndex < itemCount; itemIndex += 1) {
// test the point in each item frame
NSRect itemFrame = [self frameForItemAtIndex:itemIndex];
if(NSPointInRect(localPoint, itemFrame)) {
return [[self itemAtIndex:itemIndex] view];
}
}
// not on an item
return nil;
}
Although I'm late to this thread, I thought I'd just chime in because I've had the same problem recently. I got around it using the following line of code:
[_collectionView setValue:#NO forKey:#"avoidsEmptySelection"];
There is one caveat: the avoidsEmptySelection property is not part of the official API although I think that it's pretty safe to assume that its the type of property that will stick around for a while.

Equivalent to a "ListBox" in XCode?

You know Visual Studio, that awesome element called "ListBox"? Just a box that would list a bunch of strings.
I am now working with XCode, and I found this class in the interface builder "NSScrollView". It seems to be able to list me a couple strings. It says it got a NSTextView inside, but, how do I access it?
I am not even sure if NSScrollView is the correct solution I need, but if I could simply access the NSTextView inside it, I think it would be enough.
See NSTableView.
As for getting to a text view inside a scroll view, create an Interface Builder outlet (IBOutlet) and connect it to the text view itself, rather than the scroll view.
To get to a text view inside a scroll view; you need to select the controller with your outlet defined; click and hold control and then drag the blue connection line from your controller to the top line of the scroll view; then just wait for a blue line to appear; this will then prompt to let you link your outlet to the text view.
Josh's answer above to use NSTableView is correct. For those not that familiar with it, it can seem like a much bigger task than it actually turns out to be. Hopefully this saves people some time.
Rather than fight with NSTableCellView assumptions, you can create any type of simple view you want and use auto layout (or even return a simple NSTextView. This is what I did to get more control over layout of my text strings:
#interface PreferenceTableViewCell : NSView
#property (nonnull, strong, readonly) NSTextField *tf;
#end
#implementation PreferenceTableViewCell
-(id)init
{
self = [super init];
if(self) {
self.translatesAutoresizingMaskIntoConstraints = NO;
self.autoresizesSubviews = YES;
_tf = [NSTextField labelWithString:#""];
_ tf.translatesAutoresizingMaskIntoConstraints = NO;
_tf.autoresizesSubviews = YES;
[self addSubview:_tf];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(10)-[_tf]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_tf)]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_tf attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
}
return self;
}
#end
Then put this whereever you need the list of strings (or controls, or whatever):
_tv = [NSTableView new];
_tv.translatesAutoresizingMaskIntoConstraints = NO;
_tv.autoresizesSubviews = YES;
_tv.focusRingType = NSFocusRingTypeNone;
_tv.delegate = self;
_tv.dataSource = self;
_tv.rowHeight = 40; // Use this to adjust the height of your cell or do it in cell.
_tv.headerView = nil;
_tv.selectionHighlightStyle = NSTableViewSelectionHighlightStyleRegular;
_tv.allowsColumnReordering = NO;
_tv.allowsColumnResizing = NO;
_tv.allowsEmptySelection = NO;
_tv.allowsTypeSelect = NO;
_tv.gridStyleMask = NSTableViewGridNone;
[panel addSubview:_tv];
// TableView Column
NSTableColumn *col1 = [[NSTableColumn alloc] initWithIdentifier:#"c1"];
col1.resizingMask = NSTableColumnAutoresizingMask;
[_tv addTableColumn:col1];
Then in whatever is set as the delegate and datasource for the NSTableView add these methods:
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
return stringArray.count;
}
-(NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)tc row:(NSInteger)row
{
// This can be ANY NSView based control built as shown above.
PreferenceTableViewCell *cell = [PreferenceTableViewCell new];
cell.tf.stringValue = stringArray[row];
return cell;
}
-(void)tableViewSelectionDidChange:(NSNotification *)notification
{
// Code to do whatever when a list item is selected.
}
That is basically it for a simple list. See the Apple Docs on NSTableView for more details on how to bind the table to a data sources and more complicated problems.

NSView in NSCell

I've read a lot about this but i can't get it to work, i have a custom NSCell with this code
#import "ServiceTableCell.h"
#implementation ServiceTableCell
-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSLog(#"I'm being called");
NSView *newview = [[NSView alloc] initWithFrame:cellFrame];
NSImage *image = [NSImage imageNamed:#"bg.png"];
NSRect imagesize;
NSImageView *IMV = [[NSImageView alloc] initWithFrame:imagesize];
[IMV setImage:image];
[newview addSubview:IMV];
[controlView addSubview:newview];
}
And this my NSTableView data source:
- (long)numberOfRowsInTableView:(NSTableView *)tableView {
return 3;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(long)row
{
return [[ServiceTableCell alloc] initTextCell:#"dd"];
}
As i understand, the drawwithframe... gets called when the cell is initialized but it never gets called, so, what am I missing?
The method tableView:objectValueForTableColumn:row: should return an object value, not a cell.
Note that NSTableView is substantially different from UITableView, which you may be familiar with. For example, the data source doesn't return cells that are filled with the data, but returns the data. And the cell type in an NSTableView is set per column, you can't have a different kinds of cells in one column (well, technically, that's not entirely true, you could have different cells through -[NSTableColumn dataCellForRow:]).
So thanks to #puzzle answer and a little more digging the answer was to set my subclass of NSCell as the main cell in the InterfaceBuilder, then the method was being called, and as he said, in tableView:objectValueForTableColumn:row: i needed to return the data to then draw it.