NSTableView: only numberOfRowsInTableView: datasource is called - cocoa

I met a problem about the NSTableView, and it is like:
one datasource of tableview is called:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [self.dataArray count];
}
But this one is never called:
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
// the code
}
I have checked the the array really had data, and also checked the
[NSTableView reloadData] was called in the main thread.
SO I'm totally lost about this weird problem.
Would any one give some tips?
thanks!

I moved the initial code from awakeFromNib to the init, and it called correctly. But I still don't know the real reason. I guess it matters with the life-cycle of the view because the class which has the tableview is a subclass of NSObject and not of NSViewController.

Related

UITableView custom cell not displaying data

Here is the story. I have a simple app in which I use 2 tabs, both of them with UITableView. The first tab/view is called "Favorites" and the second one is called "My Profile." In addition I have a custom UITable cell named "CustomViewCell.xib" with an identifier of the same name. FavoritesViewController is a subclass of UITableViewController and that one is running perfectly. But for the ProfileViewController I am using normal ViewController because I don't want the whole view to be UitableView. To make that possible, I the following to ProfileViewController.h:
#interface ProfileViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) IBOutlet UITableView *tableView;
then in the viewDidLoad of ProfleViewController.m file I have:
[self.tableView registerNib:[UINib nibWithNibName:#"CustomViewCell" bundle:nil]
forCellReuseIdentifier:#"CustomViewCell"];
The following methods are implemented just as in the other tab that's working:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.myArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Here I am customizing the cell and returning it.
}
When i run the app, the cells are the right height, but are empty.
To debug I put a break point right before cellForRowAtIndexPath, and the app runs without an error which it shouldn't. So, the program is not even getting to this method. So, I think that's the reason the cells are empty. Do you guys have any idea what might be causing it to skip this particular method? can you also explain it in simpler terms because I'm newbie, you know?
In you ViewDidLoad method, add this code:
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
Then check again if the breakpoint works
Have you implemented a UITableViewDataSource? A UITableViewController is the delegate and datasource for the Table View it manages so you can define all those methods in the View Controller itself.
For a UITableView that is a subview of a UIViewController, you need to define the Table View's delegate and datasource.
See here: http://www.aboveground.com/tutorials/adding-a-uitableview-to-a-custom-uiview

view-based NSTableView's views from XIB?

Is it possible to have a separate XIB file for the NSTableCellView of a view-based NSTableView? Maybe with the help of a NSViewController?
Yes, it seems to be possible.
From Apple's documentation:
In order to function, a programmatically implemented view-based table must implement the following:
...
The - (NSView *)tableView:viewForTableColumn:row: method that is defined by the NSTableViewDelegate Protocol. This method both provides the table with the view to display as the cell for the specific column and row, as well as populates that cell with the appropriate data.
This way you can have an object of the class NSView (or any subclass) and give it back, after you properly filled it with data. Where you get this object from, is not of interest. As far as I know, it would be possible to do the following, for example:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Assume you have a XIB called View.xib
[NSBundle loadNibNamed:#"View" owner:self];
// And you have an IBOutlet to your NSTableView (that's view based) called tView
[tView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 20;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
// Assume your class has an IBOutlet called contentOfTableView,
// your class is File's Owner of the View.xib and you connected the outlet.
return contentOfTableView;
}
Hope it works. I just threw it together having a rough idea in mind. Good luck!

Xcode 4.2 iOS 5 : Multiple segues from a UITableView

I'm starting now with Xcode on 4.2 for iOS5 and there are a few changes and I'm now crossing a problem that I can't figure out a way to solve it.
I'm doing an example with a UITablwView that is populated programmatically with 2 Sections, 1st section with only 1 Row, and 2nd Section with 3 Rows.
My aim is to select a row from the table, and based on that row, the user will be redirected to different Views.
For example:
selecting section 0 row 0, app pushes to view 1 - name setting //
selecting section 1 row 0, app pushes to view 3 - address setting
The old fashion way, this is quite simple, just needed to init a UIViewController with initWithNibName and then push the view.
Now with the storyBoard everything changes, or at least I think it changes because I can't see how to get the same result since I can't set multiple segue's from the tableView to different UIViewControllers...and to do the old fashion way I can't see where I can get the NIB names from the views on the storyBoard to init an UIViewController to push.
Does any one knows how to get to this result??
Define two "generic" segues (identified as "segue1" and "segue2", for example) in the storyboard from your source view controller, one to each destination view controller. These segues won't be associated with any action.
Then, conditionally perform the segues in your UITableViewDelegate:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Conditionally perform segues, here is an example:
if (indexPath.row == 0)
{
[self performSegueWithIdentifier:#"segue1" sender:self];
}
else
{
[self performSegueWithIdentifier:#"segue2" sender:self];
}
}
I have the same problem as you do. The problem is that you can't link your tableViewCell to multiple view controllers. However you can link your source view itself to multiple view controllers.
Control-drag the master view controller (instead of table view cell) from the scene viewer to whatever view controller you want to link. You can do this as much as you want. Notice that the segue shown in source view controller scene should be something like "Push Segue from Root View Controller ..." instead of "Push Segue from NavCell to ...".
Identify each segue link a unique name like "toDetailView1"
Finally, custom the selection in your source view controllers:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2 == 1) {
[self performSegueWithIdentifier:#"toDetailView1" sender:self];
} else {
[self performSegueWithIdentifier:#"toDetailView2" sender:self];
}
}
Like #陳仁乾 and #Marco explained was completely correct. To make everything a little bit easier I would recommend you to use a single NSArray which will be initialized when viewDidLoad. Just name the segues the same as your UIViewControllers, this way you can Display a correct description of what UIViewControllers you can choose from and you can also perform the segues from this NSArray:
(Actually I'm not sure if it can cause any problems calling the segue the same as the UIViewController you want to call. Please let me know if this is BadPractise)
viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
_arraySessions = [[NSArray alloc] initWithObjects:
#"MyViewControllerName", nil];
}
cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"overviewCell"
forIndexPath:indexPath];
[cell.textLabel setText:_arraySessions[indexPath.row]];
return cell;
}
didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self performSegueWithIdentifier:_arraySessions[indexPath.row]
sender:self];
}

NSOutlineView outlineViewSelectionDidChange

my NSOutlineView outlineViewSelectionDidChange method will not be called.
I set set the NSOutlineViews delegate to the class where the other methods such as
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
exist. But outlineViewSelectionDidChange will not be called on selecting an item.
Does anybody has an idea?
This notification is a bit odd, in that it is not automatically forwarded to delegates. Try adding an explicit registration to your initialization code, like this example:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController;
{
[super windowControllerDidLoadNib:aController];
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:#selector(outlineViewSelectionDidChange:)
name:#"NSOutlineViewSelectionDidChangeNotification"
object:outlineView];
}
Okay,
meanwhile i figured out that the "NSOutlineViewSelectionDidChangeNotification" will be thrown only within the notification object. So i had to subclass my NSOutlineView to catch the notification and pass it to the object where i need it.
Your own view needs to conform to the NSOutlineViewDelegate protocol like so..
#interface MyOutlineViewController : NSView <NSOutlineViewDataSource,NSOutlineViewDelegate> {
IBOutlet NSOutlineView *myoutlineview;
}
#end
you will have this methods in your implementation
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item;
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item;
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
where you setup your outlineview.
When loading this view -(void)viewDidLoad gets called and your predefined nib/xib file or your manual call will set your datasource to fill it depending on your logic.
Now in your -(void)viewDidLoad your myoutlineview needs to set its own delegate with
[myoutlineview setDelegate:self];
so your own View may know where to call its notification methods triggerd from selections and so on. So you can place your notification logic inside the same View class conforming to this protocol.
-(void)outlineViewSelectionDidChange:(NSNotification *)notification {
NSLog(#"selection did change");
}

View based NSTableView EXC_BAD_ACCESS on Lion with ARC

This is weird. I've got a super simple project to learn NSTableView, and it's set up in my nib, set as a View-based tableView. I've also set the dataSource and delegate to my controller obejct.
When I do this, however, and run, I get an EXC_BAD_ACCESS, with the trace starting in my main function and the rest of the stack is internal to Cocoa (so not my code).
There's really nothing fancy going on, other than this project is using ARC (it's a new project, so this was the default).
I also tried using the Analyzer to make sure I wasn't improperly doing memory managment anywhere and there were no issues with it.
I don't get the crash if I don't set the dataSource/delegate, but obviously this is not a very good way to build my app!
Any ideas?
Edit
The delegate and dataSource are both set up in IB. The code is as follows (view-based). It's important to note, I'm getting crashes whether or not this code is present, and it's the same crash in either case:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 5;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTextField *cell = [tableView makeViewWithIdentifier:#"MyView" owner:self];
if (nil == cell) {
cell = [[NSTextField alloc] initWithFrame:CGRectZero];
cell.identifier = #"MyView";
}
[cell setStringValue:[NSString stringWithFormat:#"Row %d", row + 1]];
return cell;
}
It's simple!
I had been (somewhat intentionally) trying to leak a variable (because I was too lazy to make an instance variable...writing quick code here), but of course ARC took care of that leak for me, causing the whole thing to blow up.
So, I just needed to make a strong property so the object I was trying to have stick around (which object was acting as my tableView's delegate and dataSource) would not be prematurely released.

Resources