I'm building a document based Mac application. I have two classes myDocument and Person. The difficulty I'm having is when I push the button to add a new Person in the table view and display it it doesn't show in the table view. I've placed log statements inside the delegate methods. Since my log statements are not being displayed in the console I know they are not being called. Here are the implementations of the delegate methods
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [employees count];
}
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
{
// What is the identifier for the column?
NSString *identifier = [aTableColumn identifier];
NSLog(#"the identifier's name is : %s",identifier);
// What person?
Person *person = [employees objectAtIndex:rowIndex];
// What is the value of the attribute named identifier?
return [person valueForKey:identifier];
}
- (void)tableView:(NSTableView *)aTableView
setObjectValue:(id)anObject
forTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
{
NSString *identifier = [aTableColumn identifier];
Person *person = [employees objectAtIndex:rowIndex];
NSLog(#"inside the setObjectMethod: %#",person);
// Set the value for the attribute named identifier
[person setValue:anObject forKey:identifier];
[tableView reloadData];
}
Here is a pic of my .xib
Here are my actions methods
#pragma mark Action Methods
-(IBAction)createEmployee:(id)sender
{
Person *newEmployee = [[Person alloc] init];
[employees addObject:newEmployee];
[newEmployee release];
[tableView reloadData];
NSLog(#"the new employees name is : %#",[newEmployee personName]);
}
-(IBAction)deleteSelectedEmployees:(id)sender
{
NSIndexSet *rows = [tableView selectedRowIndexes];
if([rows count] == 0){
NSBeep();
return;
}
[employees removeObjectAtIndexs:rows];
[tableView reloadData];
}
You forgot to bind the document's tableView outlet to the actual table view. Thus your reloadData messages are sent to nil.
Related
I've an ARC enabled project and within IB I've created a window that holds the source list component which I believe is just a configured NSOutlineView. I'm using the magical delegate method:
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
for which I cannot find any documentation for at all. Once this method is implemented the root node in my outline view will appear, upon which my entire model gets deallocated. Then when I try and expand the root node the app immediately crashes as model no longer exists.
If I don't use this method, my model remains, the source list works but none of the cells appear (understandably). I'm really not doing any thing fancy here at all.
I've never run into this sort of issue with ARC before, but it's late so there is a chance I've done something dumb and just can't see it. Here's the full code:
#implementation RLListController
- (void)awakeFromNib
{
RLPerson *stan = [[RLPerson alloc] initWithName:#"Stan"];
RLPerson *eric = [[RLPerson alloc] initWithName:#"Eric"];
RLPerson *ken = [[RLPerson alloc] initWithName:#"Ken"];
RLPerson *andrew = [[RLPerson alloc] initWithName:#"Andrew"];
RLPerson *daniel = [[RLPerson alloc] initWithName:#"Daniel"];
RLPerson *aksel = [[RLPerson alloc] initWithName:#"Aksel"];
[stan addChild:eric];
[stan addChild:ken];
[stan addChild:andrew];
[ken addChild:daniel];
[daniel addChild:aksel];
self.people = [#[stan] mutableCopy];
}
#pragma mark - Source List dataSource
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children count] : [self.people count];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children count] > 0 : YES;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children objectAtIndex:index] : [self.people objectAtIndex:index];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
RLPerson *person = item;
return person.name;
}
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
RLPerson *person = item;
NSTableCellView *cell = [outlineView makeViewWithIdentifier:#"DataCell" owner:self];
cell.objectValue = person;
[cell.textField setStringValue:person.name];
return cell;
}
#end
#implementation RLPerson
- (id)initWithName:(NSString *)name
{
self = [super init];
if(self)
{
_name = [name copy];
_children = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
- (void)addChild:(RLPerson *)child
{
[_children addObject:child];
}
- (void)dealloc
{
NSLog(#"dealloc");
}
#end
I've just figured out a similar crash in my code. I'll describe what the cause was for me... I'm pretty sure the same applies here, but I haven't tested your code.
Be aware that awakeFromNib can be called multiple times if you have multiple NIBs. I believe this is the case if you have NSTableCellView objects embedded within the NSOutlineView within your XIB file, which are extracted when you call makeViewWithIdentifier:owner: within outlineView:viewForTableColumn:item:.
Because you are creating your model objects (stan etc) within awakeFromNib, they are being recreated during these multiple calls. With each call, ARC is cleaning up the previous model objects, but NSOutlineView is still referencing them, hence the later crash when NSOutlineView tries to ask them for more information.
The fix is to move the model object creation out of awakeFromNib, perhaps into an init method instead.
Update:
Some other small points... it also took me a while to find the documentation for the magic outlineView:viewForTableColumn:item: method. For some reason, it is part of the NSOutlineViewDelegate protocol, not NSOutlineViewDataSource. I believe that if you implement this method, you don't need an implementation of outlineView:objectValueForTableColumn:byItem:.
Image of Set up
I am trying to update different attributes in one entity from different view controllers.
Say I have the entity person open, one view controller will take the persons name email etc, the other view controller has the next time you will meet that person and activities you do with that person open.
Both should save to their respective attributes for the same entity. As it stands I can only get the first viewcontrollers info saving to new cells then the second one only saves to one cell no matter how many cells are saved with the first view controller
#interface PeopleDetailViewControllerUIViewController ()
#end
#implementation PeopleDetailViewControllerUIViewController
#synthesize people;
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
if (self.people) {
[self.nameTextField setText:[self.people valueForKey:#"name"]];
[self.emailTextField setText:[self.people valueForKey:#"email"]];
[self.homeTextField setText:[self.people valueForKey:#"home"]];
[self.cellTextField setText:[self.people valueForKey:#"cell"]];
[self.addressTextField setText:[self.people valueForKey:#"address"]];
[self.notesTextField setText:[self.people valueForKey:#"notes"]];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
return cell;
}
/*
}
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
if (self.people) {
[self.people setValue:self.nameTextField.text forKey:#"name"];
[self.people setValue:self.emailTextField.text forKey:#"email"];
[self.people setValue:self.homeTextField.text forKey:#"home"];
[self.people setValue:self.cellTextField.text forKey:#"cell"];
[self.people setValue:self.addressTextField.text forKey:#"address"];
[self.people setValue:self.notesTextField.text forKey:#"notes"];
}
else{
NSManagedObject *newPeople = [NSEntityDescription insertNewObjectForEntityForName:#"People" inManagedObjectContext:context];
[newPerson setValue:self.nameTextField.text forKey:#"name"];
[newPerson setValue:self.emailTextField.text forKey:#"email"];
[newPerson setValue:self.homeTextField.text forKey:#"home"];
[newPerson setValue:self.cellTextField.text forKey:#"cell"];
[newPerson setValue:self.addressTextField.text forKey:#"address"];
[newPerson setValue:self.notesTextField.text forKey:#"notes"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Here's a piece of code that I'm using to populate view-based NSTableView with data:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
MyCustomCellView *view = (MyCustomCellView *)[tableView makeViewWithIdentifier:#"MyCustomCellView" owner:self];
if (!view) {
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"MyCustomCellView" bundle:[NSBundle mainBundle]];
NSArray *array = nil;
if ([cellNib instantiateNibWithOwner:self topLevelObjects:&array]) {
DLog(#"%#", array);
view = [array objectAtIndex:0];
[view setIdentifier:#"MyCustomCellView"];
}
[cellNib release];
}
MyObject *object = [_objects objectAtIndex:row];
[[view titleTextField] setStringValue:object.title];
return view;
}
The DLog statement prints arrays as following for two consecutive delegate calls:
(
"<MyCustomCellView: 0x7fb2abe81f70>",
"<NSApplication: 0x7fb2ab80cbf0>"
)
(
"<NSApplication: 0x7fb2ab80cbf0>",
"<MyCustomCellView: 0x7fb2abb2c760>"
)
This is output only for two rows out of few hundred so I randomly either get my view displayed correctly or I get unrecognized selector error while calling setIdentifier: for view object when view being objectAtIndex:0 is actually an instance of NSApplication top level object from loaded nib.
Is this a bug in nib loading mechanism or am I doing something wrong with this code?
This thread is a little old, but for what it's worth:
It's not clear whether this is a bug, as the documentation is not specific as to the ordering of the array that's passed back in the topLevelObjects: parameter. However, this snippet has worked for me.
NSArray *arrayOfViews;
BOOL wasLoaded = [[NSBundle mainBundle] loadNibNamed:xibName owner:self topLevelObjects:&arrayOfViews];
NSUInteger viewIndex = [arrayOfViews indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [obj isKindOfClass:[MyCustomView class]];
}];
self = [arrayOfViews objectAtIndex:viewIndex];
I am a newbie. I have written some code for UITable but I am unable to update table values after adding the values to the array. I'm using a tableview subclass. The code is as follows. Check the last function. Now how can I update my table values?
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [_phones count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
HLPFoundPhones *p = (HLPFoundPhones *)[_phones objectAtIndex:indexPath.row];
NSString *subtitle = [NSString stringWithFormat:#"Found (%1.2f,%1.2f) on %#" ,[p loc].x, [p loc].y , [p foundDate]];
cell.textLabel.text = [p name];
cell.detailTextLabel.text = subtitle;
return cell;
}
- (void)insertNewObject
{
HLPPhonesAdd *view = [[HLPPhonesAdd alloc]initWithNibName:#"HLPPhonesAdd" bundle:nil];
[self.navigationController pushViewController:view animated:YES];
[view release];
}
- (void)updateTable:(CGPoint)loc name:(NSString *)_name{
HLPFoundPhones *a = [[HLPFoundPhones alloc]initWithLoc:loc name:_name];
[_phones addObject:a];
[phones reloadData];
}
call
[yourTableView reloadData];
in your updateTable Function..
- (void)updateTable:(CGPoint)loc name:(NSString *)_name{
HLPFoundPhones *a = [[HLPFoundPhones alloc]initWithLoc:loc name:_name];
[_phones addObject:a];
//give your tableView object instead of yourTableView
[yourTableView reloadData];
}
check value of _phone count and use in function
- (void)updateTable:(CGPoint)loc name:(NSString *)_name
{
HLPFoundPhones *a = [[HLPFoundPhones alloc]initWithLoc:loc name:_name];
[_phones addObject:a];
[table_name reloaddata];
}
Confirm the array _phones is allocated.
Then there will be problems if the [tableview reloadData] is called inside a thread.
So confirm the reloadData is called from the main thread.
First of all, your cell identifier looks to be same for all cells, which may confuse iOS which cell needs to be dequeued. So, keep your cell identifiers unique, something like below :
NSString *cellIdentifier = [NSString stringWithFormat:#"Cell-%d", indexPath.row];
then, after table view is first displayed, update your datasource and call:
[tableView reloadData];
I'm building a to-do-list application and I want to be able to edit the entries in the table and replace them with new entries. I'm close to being able to do what I want but not quit. Here is my code so far:
/*
IBOutlet NSTextField *textField;
IBOutlet NSTabView *tableView;
IBOutlet NSButton *button;
NSMutableArray *myArray;
*/
#import "AppController.h"
#implementation AppController
-(IBAction)addNewItem:(id)sender
{
[myArray addObject:[textField stringValue]];
[tableView reloadData];
}
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [myArray count];
}
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
{
return [myArray objectAtIndex:rowIndex];
}
- (id)init
{
[super init];
myArray = [[NSMutableArray alloc] init];
return self;
}
-(IBAction)removeItem:(id)sender
{
NSLog(#"This is the index of the selected row: %d",[tableView selectedRow]);
NSLog(#"the clicked row is %d",[tableView clickedRow]);
[myArray replaceObjectAtIndex:[tableView selectedRow] withObject:[textField stringValue]];
[myArray addObject:[textField stringValue]];
//[tableView reloadData];
}
#end
It's not clear what problem you're having, so here's a better way to implement editing instead:
Why not just have your data source respond to tableView:setObjectValue:forTableColumn:row: messages messages? Then the user can edit the values right in the table view by double-clicking them; no need for a separate text field.
There's also a delegate method you can implement if you want to allow only editing some columns and not others.
Peter's answer is correct, but just in case someone would be looking for complete method for editing row:
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
YourClassWhichHoldsRowRecord *abc = [yourMutableArray objectAtIndex:row];
[abc setValue:object forKey: [tableColumn identifier]];
}