cocoa-How to use tableViewSelectionDidChange:? - objective-c

I have a NSTableview in my view and I want to do some other thing when the user select a particular row. I tried tableViewSelectionDidChange method but it seemed not working.
-(void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSInteger row = [self.InfoTable selectedRow];
if (row == -1) {
return;
}else{
self.NumberInputTextField.stringValue = studentsInTable[row][0];
self.NameInputTextField.stringValue = studentsInTable[row][1];
self.ClassnumberInputTextField.stringValue = studentsInTable[row][1];
}
}
and I have
#interface ViewController : NSViewController <NSTableViewDelegate,NSTableViewDataSource>
and
self.InfoTable.dataSource = self;
self.InfoTable.dataSource = self;
Also, I've googled but haven't found a useful answer.
Can any one give me a hint on it?

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification is delegate method but you are only setting dataSource to self.
[self.InfoTable setDelegate:self];

If you have a cell-based or view-based NSTableView then nothing as such is required. Just make sure the tableView's delegate is set to the controller class, and you implement them.
Or you can do the above with codes:
self.InfoTable.dataSource = self;
self.InfoTable.delegate = self; //Note you used dataSource twice
And make sure you implement these methods if you are not doing binding to load the tableView.
- (void)tableViewSelectionDidChange:(NSNotification *)notification{
NSLog(#"Your seleceted a row...");
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.anArray count];
}
- (id) tableView:(NSTableView *)TableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(int)row {
return self.anArray[row];
}

Related

Populating NSTableView from NSMutableArray at button pressed

in an OSX app i'm currently developping to get familiar with obj-c, I want to populate a TableView. After some hours spent reading way too much blog posts, I can't understand how to add a row in my TableView.
Here is what I've done following this guide:
I have an NSMutableArray in my ViewController, this ViewController implement both interfaces NSTableViewDataSource and NSTableViewDelegate. And I implement both methodes as indicated in the guide. I also have a button and a tableView. When I click on the button, I fill my array with my own object, that's works great.
But what I want now, is when my array is populated, my tableview is too. I'm aware I need to bind those two in some way, but I have no idea how, can someone give some indication ?
Here is my code for my ViewController:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewEpisodes.delegate = self;
self.tableViewEpisodes.dataSource = self;
}
- (IBAction)btRefresh:(id)sender {
CalendarReader* reader = [[CalendarReader alloc]init];
self.episodes = [Episode getEpisodeFromEKEvents:[reader getLastMonthEventsForCalendarName:#"TV Shows"]];
[self.tableViewEpisodes reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(NSTableView *)tableView
{
return [self.episodes count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColum row:(NSInteger)row {
// Retrieve to get the #"MyView" from the pool or,
// if no version is available in the pool, load the Interface Builder version
NSTableCellView *result = [tableView makeViewWithIdentifier:#"MyView" owner:self];
// Set the stringValue of the cell's text field to the nameArray value at row
result.textField.stringValue = [self.episodes objectAtIndex:row];
// Return the result
return result;
}
First, you are creating a cell view with an identifier which you have not declared, you need to do something like this (assuming you correctly adopted the UITableView protocol in your class):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *Ident = #"Ident";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Ident];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:Ident] autorelease];
}
[cell.textLabel setText: [yourArray objectAtIndex:indexPath.row];
return cell;
}
This is a delegate method for your NSTableView. It is called when the view is loaded so you need to provide a data source at runtime.
Second, I'm assuming you want one section with a number of rows equal to your data array. If this is so, you need to change the delegate method:
- (NSInteger)numberOfSectionsInTableView:(NSTableView *)tableView
to:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Finally, keep in mind these above methods populate the ROWS, not the COLUMNS as you have it now. Once you populate your array, you need to invoke the method:
[yourTableView reloadData]
In order to refresh the table.
Hope this helps.
Thanks to #bryan-wheeler, I notice a message log when testing his code, and I found out, I was not implementing the correct method: here is my code for my ViewController now:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewEpisodes.delegate = self;
self.tableViewEpisodes.dataSource = self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return 1;
}
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex{
return [self.episodes objectAtIndex:rowIndex];
}
For beginner like me, you'll also have to implement the NSCopying protocol for the class stored in your data source array.
Right now, I only have one element in my TableView and it only show its memory address, but I'll update this answer as soon as I found out how to make it works for future beginner in my case.
EDIT: OK, it works !! My mistake was that: in the tableView:objectValueForTableColumn:row: method, I though I needed to return the Object representing the row given in parameter, but I had to return the one for the AND the cell given in parameter, now I found out, it's pretty obvious, but as a French, I didn't understand the method name correctly. Here is my code now:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewEpisodes.delegate = self;
self.tableViewEpisodes.dataSource = self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [self.episodes count];
}
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex{
if([[aTableColumn title] isEqual: #"Serie's name"]){
return [[self.episodes objectAtIndex:rowIndex] seriesName];
}else if([[aTableColumn title] isEqual: #"Season number"]){
return [NSString stringWithFormat:#"%ld", (long)[[self.episodes objectAtIndex:rowIndex] seasonNumber]];
}else if([[aTableColumn title] isEqual: #"Episode number"]){
return [NSString stringWithFormat:#"%ld", (long)[[self.episodes objectAtIndex:rowIndex]episodeNumber]];
}else{
return nil;
}
}
There is some optimisation to do for sure, feel free to propose. But it's doing the job.

NSButtonCell checkbox inside NSTableView is always getting NSOffState. Why?

Hi guys I'm new to Cocoa programming and I am getting always NSOffState whether I'm checking or unchecking an NSButtonCell (Check Box Cell in the UI dragged to a cell in an NSTableView).
I have a #property IBOutlet NSButtonCell *mySelection, connected to the respective UI and the following code.
- (void) tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MediaAComparar *media = [mediasRecesEnStock objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"seleccion"])
{
if ([mySelection state] == NSOnState)
{
[media setValue:object forKey:#"seleccion"];
NSLog(#"on state");
}
if ([mySelection state] == NSOffState)
{
[media setValue:object forKey:#"seleccion"];
NSLog(#"off state");
}
}
}
I never get the NSOnState to execute, the only NSLog message I get is: off state.
Can anyone give me some help?
Thanks!!
If you have one outlet ("mySelection") and multiple rows, which row did you think the outlet connects to? (Answer: none of them. You probably hooked it up to the prototype cell, which is never displayed or used directly.)
But no matter, you don't need to check the state before you set it. Assuming your other code is correct, you should be able to do something like:
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MediaAComparar *medium = [mediasRecesEnStock objectAtIndex:row];
if ([tableColumn.identifier isEqualToString:#"seleccion"])
medium.seleccion = object.booleanValue;
}
Less code is better code.

Basic iOS issues

I am trying to to complete the 'Your Second iOS App' tutorial from the apple developer program. It is a basic tableView app. My problem is that the app is building successfully and without warnings, however I cannot get the detail view to segue from the master view. I have copied and pasted both the segue identifier and the code that apple has provided. The segue is using push and I have deleted it and tried again several times. I am testing the app in the simulator.
How can I tell if a segue is working?
Every time I copy/paste my code from Xcode into the stack overflow question text area, I get warnings at the bottom saying that code must be indented by 4 spaces??? Does this mean I have to go line-by-line indenting code?? I did the control + k and pasted in the highlighted area, however I still got the warning??
When running the simulator and looking at it, I'm trying to use a disclosure indicator by clicking on it, do I have to push something special like control = click or command = click, etc. ?
Here is the code for BirdsMasterViewController.m file:
//
// BirdsMasterViewController.m
// BirdWatching
//
// Created by David Hall on 11/13/12.
// Copyright (c) 2012 David Hall. All rights reserved.
//
#import "BirdsMasterViewController.h"
#import "BirdsDetailViewController.h"
#import "BirdSightingDataController.h"
#import "BirdSighting.h"
/*
#interface BirdsMasterViewController () {
NSMutableArray *_objects;
}
#end
*/
#implementation BirdsMasterViewController
- (void)awakeFromNib
{
[super awakeFromNib];
self.dataController = [[BirdSightingDataController alloc] init];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
/*
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
*/
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
/*- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
*/
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.dataController countOfList];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"BirdSightingCell";
static NSDateFormatter *formatter = nil;
if (formatter == nil)
{
formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
BirdSighting *sightingAtIndex = [self.dataController objectInListAtIndex:indexPath.row];
[[cell textLabel] setText:sightingAtIndex.name];
[[cell detailTextLabel] setText:[formatter stringFromDate:(NSDate *)sightingAtIndex.date]];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return NO;
}
/*- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_objects removeObjectAtIndex:indexPath.row];
[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;
}
*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowSightingDetails"]) {
BirdsDetailViewController *detailViewController = [segue destinationViewController];
detailViewController.sighting = [self.dataController objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
#end
And here is the code for the BirdsDetailViewController.m
//
// BirdSightingDataController.m
// BirdWatching
//
// Created by David Hall on 11/25/12.
// Copyright (c) 2012 David Hall. All rights reserved.
//
#import "BirdSightingDataController.h"
#import "BirdSighting.h"
#interface BirdSightingDataController ()
- (void)initializeDefaultDataList;
#end
#implementation BirdSightingDataController
- (void)initializeDefaultDataList
{
NSMutableArray *sightingList = [[NSMutableArray alloc] init];
self.masterBirdSightingList = sightingList;
BirdSighting *sighting;
NSDate *today = [NSDate date];
sighting = [[BirdSighting alloc] initWithName:#"Pigeon" location:#"Everywhere" date:today];
[self addBirdSightingWithSighting:sighting];
}
- (void)setMasterBirdSightingList:(NSMutableArray *)newList
{
if (_masterBirdSightingList != newList)
{
_masterBirdSightingList = [newList mutableCopy];
}
}
- (id)init
{
if (self = [super init])
{
[self initializeDefaultDataList];
return self;
}
return nil;
}
- (NSUInteger)countOfList
{
return [self.masterBirdSightingList count];
}
- (BirdSighting *)objectInListAtIndex:(NSUInteger)theIndex
{
return [self.masterBirdSightingList objectAtIndex:theIndex];
}
- (void)addBirdSightingWithSighting:(BirdSighting *)sighting
{
[self.masterBirdSightingList addObject:sighting];
}
#end
David Hall
It's working when it works. But without knowing more about what you are doing it's hard to give you an answer.
Highlight the code in the textbox and then click on this button.
It will indent the code correctly for you.
I can't make sense of this question.
Edited to add
(After seeing a copy of the project that the OP mailed me)
Your segue wasn't wired up properly.
The segue is supposed to go from the cell to the next view controller, because the point is to transition on a cell click. Your segue is wired up from the controller to the detail view controller. In your project - right click on your view controller and you'll see the segue is manually connected. But right clicking on the cell doesn't show a segue connection.
Delete the current segue and recreate it, this time by control-dragging from the cell to the next view controller. You can then double check the connection by right clicking on the cell and making sure that the segue is connected.
It should look like:
If the segue is supposed to transition from your table to a detail view, and if you can't get to the detail view from the table, then your segue isn't working.
You can use the code sample button at the top of the SO editor, or you can select the code in your text editor and indent it before you copy it. For example, in Xcode it's easy to select the code, hit Command-], and copy. Then just paste into the SO editor.
Please edit your question so that it makes sense. However, I think you might be asking something along the lines of how to push a view controller from your table. If you're using a segue, and if you've created the segue in your storyboard and it's connected to both the source and destination view controllers, then you can send -performSegueWithIdentifier:sender: to your table's view controller. That is, your table view controller's -tableView:didSelectRowAtIndexPath: method should call -performSegueWithIdentifier:sender: and specify the identifier for the segue leading from the table view controller to the detail view controller. It doesn't matter whether you're using the simulator or running your app on a real device.
See the other answers from Caleb and Abizem regarding 1. and 2. If I understood your question No 3 correctly, then the answer is no. You do not have to press something in particular in the simulator when you want to select a table row or the detail desclosure indicator. Just click on those items that you would tap on your device too. If it does not segue then the chances are that its not the simulator causing the problem :)

Cocoa - multiple view-based NSTableViews

I am just beginning to teach myself cocoa, and I am running into a (probably simple) issue displaying multiple view-based NSTableViews with same delegate and controller (the App Delegate, in my case). I saw this post:
Best way to handle multiple NSTableView(s)
but the method described still gives me errors - specifically
Duplicate declaration of method 'numberOfRowsInTableView:'
Duplicate declaration of method 'tableView:viewForTableColumn:row:'
Obviously, the compiler isn't seeing that the different method declarations are for different table views.
The code for the tableviews in the AppDelegate.m file is
#synthesize tableView1;
#synthesize tableView2;
-(NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView1
{
return 1;
}
-(NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView2
{
return 2;
}
- (NSView *)tableView:(NSTableView *)tableView1 viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSTableCellView *resultForTable1 = [tableView1 makeViewWithIdentifier:tableColumn.identifier owner:self];
resultForTable1.textField.stringValue = #"This should appear in the first tableView";
return resultForTable1;
}
- (NSView *)tableView:(NSTableView *)tableView2 viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSTableCellView *resultForTable2 = [tableView2 makeViewWithIdentifier:tableColumn.identifier owner:self];
resultForTable2.textField.stringValue = #"This should appear in the second tableView";
return resultForTable2;
}
and in my AppDelegate.h file, I have:
#property (weak) IBOutlet NSTableView *tableView1;
#property (weak) IBOutlet NSTableView *tableView2;
What am I doing wrong here?
I think you're misunderstanding the method described in that answer.
You're getting a compiler error because you are trying to implement the same method twice. The following would all be implementations of the same method:
- (void)setBlah:(id)aBlah {
- (void)setBlah:(id)newBlah {
- (void)setBlah:(id)theNewBlah {
The different "names" given to the parameter that follows the (id) parameter type are only local to the implementation block of that method.
You should be able to accomplish what you want to do using code like the following:
#synthesize tableView1;
#synthesize tableView2;
- (NSUInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
if (aTableView == tableView1) return 1;
else if (aTableView == tableView2) return 2;
return 0;
}
- (NSView *)tableView:(NSTableView *)aTableView
viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *tableCellView = [aTableView
makeViewWithIdentifier:tableColumn.identifier owner:self];
if (aTableView == tableView1) {
tableCellView.textField.stringValue =
#"This should appear in the first tableView";
} else if (aTableView == tableView2) {
tableCellView.textField.stringValue =
#"This should appear in the second tableView";
}
return tableCellView;
}
Notice that I made sure to name the parameter aTableView, something different from the instance variables, so that I can successfully compare it to the instance variables in the following lines.
You don't duplicate the methods multiple times -- you're not supplying the argument, "tableView1", tableView2", etc., the table views call these methods and send themselves as the argument. So, if you want to use the same delegate for multiple tables, you put if statements in the delegate methods, to ascertain which table sent the message. Declare an IBOutlet for each table and then do (in pseudo code) if table1 .... else if table2 ...etc.

Changing view for selected in view-based NSTableView

How do I change the view for the selected row when using a view-based NSTableView? Specifically, I'd like to have a simple NSView subclass for unselected rows and a more complex NSView subclass for the selected row which allows editing of more information associated with the row item.
An example is the way Things allows you to expand the item being edited as seen here: http://culturedcode.com/things/
My guess is that you want to use a different NSTableCellView subclass when the row is selected. I think you should be able to do something like this:
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSTableView *table = [notification object];
NSIndexSet *allColumns = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [[table tableColumns] count])];
[table reloadDataForRowIndexes:[table selectedRowIndexes] columnIndexes:allColumns];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if ([[tableView selectedRowIndexes] containsIndex:row])
{
// If the row is selected, return an instance of the class for selected views
SelectedTableCellView *selectedView = ...; // Get from makeViewWithIdentifier:
// Set up selectedView
return selectedView;
}
else
{
NonSelectedTableCellView *nonSelectedView = ...; // Get from makeViewWithIdentifier:
// Set up nonSelectedView
return nonSelectedView;
}
}
It might be nice if you elaborated a little bit more on what you mean by "change the view to a more complex view"
Nonetheless, you could for instance, implement - (void)tableViewSelectionDidChange:(NSNotification *)notification in the delegate of the table view, get the selected NSTableRowView if it is visible, and change it in what way you want, which includes making it more complex, expanding it (see below), etc.
To modify the size of a row, you would need to implement - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row in the same delegate, and call the table view's -noteHeightOfRowsWithIndexesChanged to update the height for particular rows.
I think the app is created by NSOutlineView in outlineview only you can easily expand your selected row...
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
if ([item isKindOfClass:[NSDictionary class]])
{
return YES;
}else
{
return NO;
}
}
I think this way is write..