NSTableView and NSMutable Array don't link - objective-c

I've made a project which is a kind of Todo List but it doesn't work. My tableView has a blue halo on it but nothing appear.
Here is the code of TPRendu.h:
#interface TPRendu : NSObject
{
IBOutlet NSButton *boutonAjouter;
IBOutlet NSTableView *tableauEtudiant;
NSMutableArray *sourceTable;
}
-(IBAction)ajouterEtudiant:(id)sender;
#end
And the code of TPRendu.m:
#import "TPRendu.h"
#implementation TPRendu
-(id)init
{
[super init];
NSLog(#"init");
//init du tableau
sourceTable = [[NSMutableArray alloc] init];
return self;
}
-(IBAction)ajouterEtudiant:(id)sender
{
[sourceTable addObject:#"test"];
[tableauEtudiant reloadData];
NSLog(#"Nombre éléments ajoutés: %d",[sourceTable count]);
}
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
return [sourceTable count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tc row:(NSInteger)rowIndex
{
return [sourceTable objectAtIndex:rowIndex];
}
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tc row:(NSInteger)rowIndex
{
[sourceTable replaceObjectAtIndex:rowIndex withObject:anObject];
}
#end
As you can see, it's very simple. I haven't made any binding in IB except the Referencing outlet for tableauEtudiant and for the NSButton.
The thing is: I've made a similar program a month ago that work flawlessly, I've made this on the same template and it doesn't work. It seems that the NSMutableArray isn't linked with the NSTableView (the NSLog in ajouterEtudiant works well for example).
Any thought?
Thanks.

First guess: you didn't connect the table's dataSource outlet to an instance of the controller that contains the code you posted.

Related

NStableview setDatasource EXC_BAD_ACCESS

Im trying to just create a simple menu with an NSTableView using an NSarray. When i set the data source to the class i created i get EXC_BAD_ACCESS error. Wierd thing is, it worked in macruby?
implementation file:
#implementation TableArray
- (id) init
{
self = [super init];
if(self) {
arr = [NSArray arrayWithObjects:#"hey", #"what", #"there", nil];
}
return self;
}
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
{
return [arr count];
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
return [arr objectAtIndex:rowIndex];
}
#end
Header:
#interface TableArray: NSObject <NSTableViewDataSource> {
NSArray *arr;
}
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView;
- (id) tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex;
#end
And in the app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
TableArray *arr = [[TableArray alloc] init];
[tv setDataSource:arr];
[tv reloadData];
}
And the delegate header:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTableView *tv;
}
#property (assign) IBOutlet NSWindow *window;
#end
I'm betting you have ARC enabled (possibly GC). NSTableView maintains a weak reference to its data source and you aren't maintaining a strong reference to same, so ARC is releasing your data source before you are done with.
Note that it is exceptionally rare to have a data source float about like this. It is almost assuredly a part of the control layer of your app since the data source is the conduit between the table and the underlying data store.
It likely works under MacRuby because the code is slightly different or because of implementation details.
It would be useful to know where you are initializing *tv. I'm assuming you've placed it in some NIB file that gets loaded at app startup.
Then, you should put IBOutlet NSTableView *tv; in a ViewController, ideally one that subclasses UITableViewController. a tableView reference/outlet belongs there.
Also, it would be easier to use the viewController itself as dataSource, and make the connection in Interface Builder.

Datasource not returned by sub class of parent class implementing UITableView, throwing exception

I want to show some media of different categories (e.g. mostViewed, starred) in a UITableView. I created APPParentViewController which implements UITableViewDataSource and UITableViewDelegate protocol. In cellForRowAtIndexPath method in APPParentViewController, I return the appropriate cell which is filled with data coming from an array.
The array is actually instantiated in the init method in a sub class of APPParentViewController, which I exemplarily called APPChildViewController. There is one APPChildViewController for each media category. They just differ in the way the array is instantiated, the content of the array so to say.
I instantiate all APPChildViewController classes in another UIViewController ([[APPChildViewController alloc] init]) and then initially select one APPChildViewController to view (all happens in viewDidLoad method of that UIViewController). Working so far.
But when I want to show another APPChildViewController simply by removing the old view and adding the requested view (when the user requested it by pressing a button), I am getting the following exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:' ***
This is the code I am using (I removed everything which is not important in my opinion, so I hope it's still comprehensible):
APPParentViewController.h
#import <UIKit/UIKit.h>
#interface APPParentViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) NSArray *media;
#property (strong, nonatomic) UITableView *tableView;
#end
APPParentViewController.m
#import "APPParentViewController.h"
#import "APPCell.h"
#interface APPParentViewController ()
#end
#implementation APPParentViewController
#synthesize media;
#synthesize tableView;
-(void)viewDidLoad
{
self.tableView = [[UITableView alloc] init];
[self.view addSubview:self.tableView];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.media count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellTableIdentifier = #"CellTableIdentifier";
static BOOL nibsRegistered = NO;
if (!nibsRegistered) {
UINib *nib = [UINib nibWithNibName:#"APPCell" bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:CellTableIdentifier];
nibsRegistered = YES;
}
APPCell *cell = [self.tableView dequeueReusableCellWithIdentifier: CellTableIdentifier];
NSUInteger row = [indexPath row];
NSDictionary *rowData = [self.media objectAtIndex:row];
cell.title = [rowData objectForKey:#"Title"];
return cell;
}
#end
APPChildViewController.h
#import "APPParentViewController.h"
#interface APPChildViewController : APPParentViewController
#end
APPChildViewController.m
#import "APPChildViewController.h"
#import "APPCell.h"
#interface APPChildViewController ()
#end
#implementation APPChildViewController
- (id)init
{
self = [super init];
if (self) {
self.media = fill array...
}
return self;
}
It actually works when I copy the cellForRowAtIndexPath method implementation to all sub classes, but this is obviously not the way inheritance is intended to work...
When you make the switch, some cells will have a reference to the old child's media, and if that object is gone uh oh:
NSDictionary *rowData = [self.media objectAtIndex:row];
As soon so make the switch of dataSource, are you sending:
[self.tableView reloadData];
I'm assuming (hoping!) that you have just the one nib owned by the parent...

Master Detail Storyboard: Table View not showing cells (Objective-C)

I'm having troubles with the master viewController not showing any cells. This is the situation:
The app uses storyboard.
When the app launches, it goes to the navigationController
A button is pressed and connected to the table ViewController and it's set to "push" to it.
I've added the object and made a cell/detailView or whatever.
For some reason, the cell won't show up!!!
Here's the files:
MasterViewController.h:
#import <UIKit/UIKit.h>
#import "CraftingDetail.h"
#import "Crafting.h"
#class CraftingList;
#interface CraftingMaster : UITableViewController
#property (strong, nonatomic) CraftingDetail *detailViewController;
#property (strong, nonatomic) CraftingList *CL;
#end
MasterViewController.m:
#import "CraftingMaster.h"
#import "CraftingList.h"
#interface CraftingMaster ()
#end
#implementation CraftingMaster
#synthesize detailViewController = _detailViewController;
#synthesize CL;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
self.CL = [[CraftingList alloc] init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return self.CL.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.CL.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
cell.textLabel.text = [self.CL craftingAtIndex:indexPath.row].Title;
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#end
DetailViewController.h:
#import <UIKit/UIKit.h>
#interface CraftingDetail : UIViewController
#property (strong, nonatomic) IBOutlet UIImageView *Image;
#property (strong, nonatomic) IBOutlet UITextView *Description;
#end
This is an old question, but having an unexpectedly empty table view is a common issue when starting to develop with table views so hopefully this answer will be of use to someone.
Here are some things to check when your table view is devoid of cells and you expected otherwise:
Are your data source object(s) (self.CL in this case) valid? (ie. Are they != nil and point to the correct object?)
Does numberOfSectionsInTableView: return an integer value greater than zero?
Does tableView:numberOfRowsInSection: return an integer value greater than zero?
Here are a couple of problems in MasterViewController.m above that need attention:
InitWithStyle: will not be executed when the view controller is instantiated in a storyboard. Instead, initWithCoder: should be used. I suspect this was the source of JomanJi's pain as this resulted in self.CL not being instantiated. (As an aside, the data source object/property: CL should be instantiated by assigning the value to the _CL ivar directly, not to the property. See "Initializing a property, dot notation" to learn why).
Due to returning (what is likely) the same value for numberOfSectionsInTableView: as tableView:numberOfRowsInSection: (ie. "return self.CL.count;"), the table view will display the same number of sections as there are cells in each section with each sections' cells containing the same data as the other sections. I doubt this effect was what the developer intended. (This is of course unless the count accessor method in CraftingList does something really strange).
Without seeing the code for CraftingList it is impossible to determine exactly what the problem is. However, given the age of the question, I suspect JomanJi has since figured it out on his/her own.

Initialising an NSTableView

I'm quite new to Cocoa and I am trying to setup a table view backed by an array. I've setup the app delegate as the datasource for the tableview, and implemented NSTableViewDataSource protocol.
When I run the app, I get the following log output:
2012-06-23 18:25:17.312 HelloWorldDesktop[315:903] to do list is nil
2012-06-23 18:25:17.314 HelloWorldDesktop[315:903] Number of rows is 0
2012-06-23 18:25:17.427 HelloWorldDesktop[315:903] App did finish
launching
I thought that when I called reloadData on the tableView it would call numberOfRowsInTableView:(NSTableView *)tableView again to refresh the view, but that doesn't seem to be happening. What have I missed?
My .h and .m listings are below.
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource>
#property (assign) IBOutlet NSWindow *window;
#property (assign) IBOutlet NSTableView * toDoListTableView;
#property (assign) NSArray * toDoList;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize toDoList;
#synthesize toDoListTableView;
- (void)dealloc
{
[self.toDoList dealloc];
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"App did finish launching");
// Insert code here to initialize your application
// toDoList = [[NSMutableArray alloc] init];
toDoList = [[NSMutableArray alloc] initWithObjects:#"item 1", #"item 2", nil];
[self.toDoListTableView reloadData];
// NSLog(#"table view %#", self.toDoListTableView);
}
//check toDoList initialised before we try and return the size
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView {
NSInteger count = 0;
if(self.toDoList){
count = [toDoList count];
} else{
NSLog(#"to do list is nil");
}
NSLog(#"Number of rows is %ld", count);
return count;
}
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSLog(#"in objectValueForTable");
id returnVal = nil;
NSString * colId = [tableColumn identifier];
NSString * item = [self.toDoList objectAtIndex:row];
if([colId isEqualToString:#"toDoCol"]){
returnVal = item;
}
return returnVal;
}
#end
The first thing that I'd check is that you're NSTableView IBOutlet is still set in applicationDidFinishLaunching.
NSLog(#"self.toDoListTableView: %#", self.toDoListTableView)
You should see output like:
<NSTableView: 0x178941a60>
if the outlet is set properly.
If you see 'nil' rather than an object, double check to ensure that your NSTableView is connected to your outlet in the XIB editing mode of Xcode. Here's a documentation link for assistance connecting outlets.
I fixed it - I'd set the appDelegate as the datasource and the delegate for the tableView but ctrl-dragging from the tableView to the appDelegate, but I hadn't ctrl-dragged the other way to actually link up the outlet I'd declared with the table view. It's working now. Thanks for your help though Jeff.

NSTableView binding problem

I have only just started with XCode (v3.2.2) and Interface Builder and have run into a problem.
Here is what I have done:
I have made a class to be the datasource of a NSTableView:
#interface TimeObjectsDS : NSControl {
IBOutlet NSTableView * idTableView;
NSMutableArray * timeObjects;
}
#property (assign) NSMutableArray * timeObjects;
#property (assign) NSTableView * idTableView;
- (id) init;
- (void) dealloc;
- (void) addTimeObject: (TimeObj *)timeObject;
// NSTableViewDataSource Protocol functions
- (int)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn row:
(int)row;
#implementation TimeObjectsDS
#synthesize timeObjects;
#synthesize idTableView;
-(id) init {
if (self = [super init]) {
self.timeObjects = [[NSMutableArray alloc] init];
TimeObj *timeObject = [[TimeObj alloc] init];
[timeObject setProjectId:11];
[timeObject setDescription:#"Heja"];
[timeObject setRegDate:#"20100331"];
[timeObject setTimeSum:20.0];
[timeObjects addObject:timeObject];
[timeObject release];
[idTableView reloadData];
}
return self;
}
- (void) dealloc {
[idTableView release];
[timeObjects release];
[super dealloc];
}
// Functions
- (void) addTimeObject: (TimeObj *)timeObject {
[self.timeObjects addObject:timeObject];
[idTableView reloadData];
}
// NSTableViewDataSource Protocol functions
- (int) numberOfRowsInTableView:(NSTableView *)tableView {
return [self.timeObjects count];
}
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row {
return [[timeObjects objectAtIndex:row] description];
}
#end
I have then bound my NSTableView in the View to this datasource like so:
alt text http://www.og-entertainment.com/tmp/ib_datasource_bindings_big.png
I have also bound the View NSTableView to the Controller idTableView variable in Interface Builder seen above
In the init function I add a element to the mutable array. This is displayed correctly in the NSTableView when I run the application. However when I add another element to the array (of same type as in init) and try to call [idTableView reloadData] on the View nothing happens.
In fact the Controller idTableView is null. When printing the variable with NSLog(#"idTableView: %#", idTableView) I get "idTableView: (null)"
Im runing out of ideas how to fix this. Any ideas to what I could do to fix the binding?
If your tableview outlet in your controller is null, then you haven't connected it in Interface Builder. Your screenshot above shows a connection to TimeObjectsDS, but that doesn't mean a lot - is that the instance that you are calling reloadData from? It is possible that you have more than one instance of this class, for example.
That's just one possibility. Without more code, it's not feasible to list many more.
Incidentally, in MVC it's considered a bad thing to connect a model object directly to a view. You may just be using the terminology incorrectly.