UITableView Row Persistance - why does "didSelectRowAtIndexPath" crash - xcode

I have a UITableView that stores the selected row in User Defaults. The tableView is part of a menu structure that may be reloaded during the lifetime of the application, hence I want the persistence between loads. In viewDidLoad, this UserDefault is checked for existence, and I call
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:0];
[self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:(UITableViewScrollPositionMiddle)];
This works fine, as expected. However, it doesn't actually select the row, it just highlight's it. If I subsequently call
[self tableView:self.tableView didSelectRowAtIndexPath:path];
I get a crash - "unrecognised selector sent to instance". Why?

[self tableView:self.tableView didDeselectRowAtIndexPath:path] will call YOUR implementation of the deselection method (which is defined in UITableViewDelegate).
You get a crash since you didn't implement it in your delegate.
You should call:
[self.tableView deselectRowAtIndexPath:path animated:NO];

The UITableView works via two delegate protocols, UITableViewDelegate and UITableViewDataSource. The class that defines your UITableView should implement these protocols and you should set the delegate and datasource of the UITableView to "self". You should not call the protocol methods directly which is most likely the reason for your crash.
In order to select a particular row based on your data model (User Default), you will need to set the UITableViewCell selected property to "YES" in the tableView:cellForRowAtIndexPath: for the row that is being rendered.
I recommend going through this tutorial to help you understand UITableView's better.
http://www.iosdevnotes.com/2011/10/uitableview-tutorial/

Related

self.tableView reloadData not working in viewWillAppear

I have an Xcode app that creates a tableview and then populates this with data from an sqlite database, this works perfectly and allows me to push data to a new view and delete etc,
This tableview is the first view loaded on a tab bar controller. The problem arises when i move to another view on the same controller, i.e. create an athlete, in this view i can add a new athlete to the database (which works fine) but when i go back to the tableview on the tab bar controller, the table is the same, i have to log out and log back in to see the change in the table,
I have tried [self.tableView reloadData] in the viewWillAppear function but this does nothing. I know that the function is getting called as i have placed an NSLog in their just for peace of mind.
I have found what i think may be the solution on here but i cant get it to work!
viewWillAppear, viewDidAppear not being called, not firing
i will post my code below and a screen shot of the storyboard as im convinced it is my poor way of setting up controllers that has caused this error. Any help would be much appreciated as i am now at the meltdown stage :D
-(void)viewWillAppear:(BOOL)animated {
//[super viewWillAppear:animated];
//[self.tabBarController viewWillAppear:animated];
[self.tableView reloadData];
NSLog(#"i just ran viewwillAppear");
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
athleteObject *athObj3 = [athletes objectAtIndex:indexPath.row];
athleteIdDelete = athObj3.athId;
[self openDatabase];
[self deleteAthleteFromDataBase];
[athletes removeObjectAtIndex:indexPath.row];
[tableView reloadData];
}
reloadData works in my commitEditing function (this updates the tableview when i delete an entry via a swipe)
any help would be much appreciated, il post the screen shot below.
(cant figure out how to load a screenshot if its even possible)
It looks like athletes (a NSMutableArray I assume) is not being updated in viewWillAppear. Have you tried running the selector that populates the array from the database before calling reloadData?
Hope that helps!
I'm assuming you're setting the number of rows/sections in your code based on how many objects are returned from a database.
Set a breakpoint at this method, and get the count each time the compiler breaks. You are probably not getting any objects from the database, and therefore the compiler never runs the rest of the table view dataSource methods.

View-based NSTableView view controllers

I'm not sure if I am doing things right but this is my problem:
I have a view-based NSTableView using bindings to an arraycontroller.
I need to do some custom drawing on each row, depending the represented object as well as capture click in certain areas so for this I would need to have a controller for each row and set outlets for the sub-views in my custom cell view, but I don't understand how I can achieve this.
If I just add an object to the nib and make the connections to it, then I cannot tell which of the views is being drawn (or has been clicked).
You have to implement the delegate methods :
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
It's used by the table view to get a view for a give cell (column, row).
Then by using "makeViewWithIdentifier:owner:", you can get the a reusable cell with a given identifier and a given owner (view controller).
The simplest way is to design your cells in Interface Builder, and set a different identifier for each one. Then the method "makeViewWithIdentifier:owner" will automatically create a view for you for the given identifier.
I just found someone asked a similar question and the answer to it also satisfies my needs, so for anyone ending up here, this is what I did:
I set my NSTableCellView controller as the delegate of the NSTableView.
In my NSTableCellView subclass I implement the needed methods (drawRect:, mouseUp: and so forth) and call the respective methods in the controller.
To access the controller I get the NSTableView and then its delegate like this:
NSTableView *tableView = (NSTableView*)myView.superview.superview.superview;
MyControllerClass *controller = (MyControllerClass*)tableView.delegate;
[controller view:myView drawRect:dirtyRect]
On the controller, to tell which view is sending an event, I use their identifiers.

Cocoa: setters in an NSViewController view

I'm using an NSViewController class with a single view in it to display a progress indicator bar and some text fields. I'm trying to use progressIndicator setMaxValue:and theTextField setStringValue: but neither of these are doing anything.
I've done this before and I've checked and rechecked, it's fairly straightforward, the fact that it's not working makes me think that it has to do with the fact that the class is NSViewController. Which is why I tried
Timers *aTimer = [[Timers alloc] init];
[aTimer.timerNameLabel setStringValue:#"name"];
[aTimer.progressIndicator setMaxValue:x];
in the app delegate which is an NSObject class, but that didn't work either.
I've tried looking around the NSViewController documentation but I can't find anything that says it can't set those values so I don't know what's happening. What am I doing wrong?
You probably want to use -initWithNibName:bundle: instead of a regular init to initialize your custom nib.
EDIT: It seemed the problem was due to the view not being queried before getting other objects. By calling [myController view] you actually load the nib, which isn't done automatically when you initialize the view controller. So before you can use any element of the view, you need to call [myController view]

Linking a UITableView to NSMutableArray

This isn't a "please help me debug my code" message. I actually have it working, but want a deeper understanding of how it works!
I have a view-based xCode application. On my main view, I have a UITableView and I've set the delegate and dataSource to File's Owner.
In my .h file for the Main ViewController, in the #interface section, I declare an NSMutableArray which will contain a list of custom objects.
In the #interface declaration, I include the protocols UITableViewDelegate and UITableViewDataSource.
Later, after a few button taps, my code parses an XML file from a server and inserts objects into the NSMutableArray. I added code to the cellForRowAtIndexPath function, which gets called as it should, and when I call the reloadData method for my UITableView, the table gets populated with the results parsed from my XML!
Pretty cool, but I'd like to understand how this happens; a better understanding will be useful to me in the future.
I never explicitly tie my NSMutableArray to that UITableView. Why does cellForRowAtIndexPath get invoked when an object is added to the array? How is it connected? Is it because the NSMutableArray is declared in the #interface block which has the protocols that I mentioned above? If I had two UITableViews on my .XIB how would the code know which NSMutableArray should be mapped to which UITableView? (Or is that something I should strive to avoid?) Even if I only have on UITableView on the XIB, if I have two NSMutableArrays, will I have to be concerned with them somehow tangling up my UITableView?
Thank you!
NSMutableArray --- in your case it is the data to be displayed in table view. When display has to happen Table view ask for the data using data source.
How they are connected -- Data source ask for the data there is not other explicit connection.
How to handle 2 table view -- Ideal approach is have different data source object for each table view, each object will have its own NSMutableArray of data.
1 table view and 2 NSMutableArray -- It is left to the data source implementation, depending on your need you can display data of any NSMutableArray objects.
Why does cellForRowAtIndexPath get invoked when an object is added to the array? -- you might be calling view reloadData
Whenever you call [tableView reloadData]; your cellForRowAtIndexPath method and numberOfRowsInSection are called.
Its because of the UITableViewDataSource protocol. There's no connection between NSMutableArray and UITableView.
When a new object is added to the array, after that [tableView reloadData]; will be called. Hence, now the cellForRowAtIndexPath method will be called for the array which will be having a count increased by 1.
Hope this wud help. :)

cocoa + context sensitive menu on NSTableView with multiple rows selected

i am having a problem displaying context sensitive menu on control click on a tableview when multiple rows are selected.
Its working fine when a single row is selected and then control clicked on it.
The way i am implementing this is shown below:
-(void)doSingleClick
{
NSLog(#"single clicked");
if([[NSApp currentEvent] modifierFlags] & NSControlKeyMask)
{
NSLog(#"control clicked.......");
[NSMenu popUpContextMenu:[self showContextMenu] withEvent:[NSApp currentEvent] forView:tableView];
return;
}
}
and showContextMenu function returns a NSMenu object.
I am dong it this way as my table view for some strange reason does not recognize mouseDown or mouseUp or menuForEvent events.
the problem with the above code segment is, when multiple rows are selected and control clicked, it does not recognize the control click and does not go into that loop and hence not displaying the context menu.
Please suggest me a mechanism to achieve this.
Thanks
I don't recommend the approach that is given in the answers above. Instead, look at the "DragNDropOutlineView" example in Leopard and higher. That, and the release notes, give a proper way to implement contextual menus for a single row, or multiple rows. This includes having AppKit automatically do the proper highlighting.
corbin dunn
(NSTableView Software Engineer)
i hve tableviewcontroller class which is a subclass of NSTableView.
That's very bad naming and suggests that you are not architecting your application properly. Views aren't controllers. Keep them separate.
but this class in which i implemented menuForEvent method but its not getting called for some reason.
Did you make your table view an instance of this class in Interface Builder? If not, your instance is still an NSTableView, and the subclass you wrote is what Ian Hickson might call “a work of fiction”.
Corbin's answer is the best one here.
link text
I don't believe the action method is called when multiple rows are selected.
What would probably be a lot easier would be to override the menuForEvent: method in NSTableView. You'd have to create a subclass of NSTableView to do this, but it would be a cleaner solution.
You could also create an informal protocol (a category on NSObject) and have the NSTableView delegate return the appropriate menu.
#interface NSObject (NSCustomTableViewDelegate)
- (NSMenu *)tableView:(NSTableView *)tableView menuForEvent:(NSEvent *)event;
#end
#implementation NSObject (NSCustomTableViewDelegate)
- (NSMenu *)tableView:(NSTableView *)tableView menuForEvent:(NSEvent *)event {
return nil;
}
#end
And in your NSTableView subclass:
- (NSMenu *)menuForEvent:(NSEvent *)event {
return [[self delegate] tableView:self menuForEvent:event];
}

Resources