Populate NSTableView with a new object by clicking a button - objective-c

Im trying to populate an NSTableView with a custom object by clicking a button. My custom class has just NSString properties: title and volume. I customized the init method to create an object and add it to my array that is hooked up to my table view. Then on the method for my button I had it create another object and add it to the array. When I run the program the object from the init method does display correctly. However, when I click my button I get nothing.
Here is the code for my header file:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource>{
NSMutableArray *_myArray;}
#property (weak) IBOutlet NSTableView *tableView;
- (IBAction)button:(id)sender;
#end
and here is my implementation:
#import "AppDelegate.h"
#import "trade.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
-(id)init{
self = [super init];
if (self){
_myArray = [[NSMutableArray alloc]init];
trade *firstTrade = [[trade alloc]init];
[firstTrade setTitle:#"Trade One"];
[firstTrade setVolume:#"Volume 01"];
[_myArray addObject:firstTrade];}
return self;}
- (IBAction)button:(id)sender {
trade *secondTrade = [[trade alloc]init];
[secondTrade setTitle:#"Trade two"];
[secondTrade setVolume:#"Volume 02"];
[_myArray addObject:secondTrade];
[_tableView reloadData];}
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return _myArray.count;}
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
if ([tableColumn.identifier isEqualToString:#"Title"]){
trade *item = [_myArray objectAtIndex:row];
return item.title;
}else{
trade *item = [_myArray objectAtIndex:row];
return item.volume;}}
#end
Not sure what I'm doing wrong here as what I did in the init method is pretty much the same thing I did in button method. Any help is greatly appreciated. Here is a picture of the program after I run it:
tableView program
As you can see my first object appears in the table view, but not the second.
While looking at this project I duplicated the [_tableView reloadData]; line and placed a breakpoint on the second one so I could see what was going on right after the first one is called. One thing I noticed is that _myArray does now contain 2 objects after the button click.
However, when I look into _tableView it shows that _myArray only contains 1 object. How is it that the object is actually getting added but the table view is only getting 1?
Here is a picture for reference:Debug

Related

Obj-C / Using MapKitView and TableView together with delegation

I am a very beginner of objective c and I wanted to create a scene about using 2 child view controller in a master view controller which includes MapKit and TableView. I ve searched it on internet for 1 day and but I couldn't make any of solutions since I failed in different steps of each suggestions. I know that there are a lot of way to pass data between View Controllers and I thought the best way is using delegation logic in this situation. (let me know if I am wrong please). By the way, I can update or move the cursor onto specific location WHEN I set a dummy button on MapKitViewController, so MapKit part is not where I am failing at. I am pretty sure that the problem is about communication between 2 View Controllers which are active at the same time.
Problem:
Updating MapView by clicking a table row of TableView which includes location coordinate detail. Both children view are set on a MasterViewController by 2 container views.
Tried so far:
Created a method "goToLocation:(Location*) location" in MapViewController and sent parameters to this method from TableViewController. =>(Lat and Long parameters received by goToLocation() but MapKitView doesn't get updated)
Tried to create a delegate logic between MapView and TableView.=>(Probably I couldn't create it properly. Please see below)
What I want :
I want to know what part I am doing wrong. Is there a easy or proper way to achieve that ?
TableViewController.h
#class TableViewController; //define class
#protocol TableViewDelegate <NSObject> //define delegate protocol
//define delegate method to be implemented within another class
- (void) locationSelected: (TableViewController *) sender object:(Location *) location;
#end
#property (nonatomic, weak) id <TableViewDelegate> delegate; //define TableViewVCDelegate as delegate
TableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self notifyNow:[_locations objectAtIndex:indexPath.row]];
}
- (void) notifyNow:(Location *) location {
//this will call the method implemented in your other class
[self.delegate locationSelected:self object:(Location *) location];
NSLog(#"Selected: %#",[location name]);
}
MapViewController.h
//make it a delegate for TableViewVCDelegate
#interface MapViewController : UIViewController <TableViewDelegate>
MapViewController.m
- (void)viewDidLoad {
_tableViewController = [[TableViewController alloc]init];
_tableViewController.delegate = self; //set its delegate to self somewhere
}
!!_ This Method Doesn't Get Triggered _!!
- (void)locationSelected:(TableViewController *)sender object:(Location *)location {
NSLog(#"%# is great!", [location name]);
}
MainViewController.m (Master/Root View Controller)
#import "MainViewController.h"
#import "MapViewController.h"
#import "TableViewController.h"
#interface MainViewController ()
#property (strong, nonatomic) IBOutlet UIView *topCont;
#property (strong, nonatomic) IBOutlet UIView *bottomCont;
#property (strong, nonatomic) IBOutlet UIView *safeArea;
#end
#implementation MainViewController
MapViewController *mapViewVC;
TableViewController *tableViewVC;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES animated:YES];
tableViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TableViewController"];
mapViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TopChildVC"];
[self addChildViewController:tableViewVC];
[tableViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.bottomCont addSubview:tableViewVC.view];
[tableViewVC didMoveToParentViewController:self];
[self addChildViewController:mapViewVC];
[mapViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.topCont addSubview:mapViewVC.view];
[mapViewVC didMoveToParentViewController:self];
}
#end

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.

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.

No action when clicking on button; quote doesn't change

I have put together a simple quote generator, storing the quotes in an array. Following are the quote view controller interface and implementation files:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property(nonatomic, retain)NSArray *myQuotes;
#property(nonatomic, retain)NSMutableArray *movieQuotes;
#property (nonatomic, retain) IBOutlet UITextView *quote_text;
-(IBAction)quote_btn_touch:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myQuotes;
#synthesize movieQuotes;
#synthesize quote_text;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.myQuotes = [NSArray arrayWithObjects:
#"Live and let live",
#"Don't cry over spilt milk",
#"Always look on the bright side of life",
#"Nobody's perfect",
#"Can't see the woods for the trees",
#"Better to have loved and lost than not loved at all",
#"The early bird catches the worm",
#"As slow as a wet week",
nil];
quote_text = nil;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-(IBAction)quote_btn_touch:(id)sender {
// 1 - Get number of rows in array
int array_tot = [self.myQuotes count];
// 2 - Get random index
int index = (arc4random() % array_tot);
// 3 - Get the quote string for the index
NSString *my_quote = [self.myQuotes objectAtIndex:index];
// 4 - Display the quote in the text view
self.quote_text.text = [NSString stringWithFormat:#"Quote:\n\n%#", my_quote];
}
#end
In the xib file, I connected the text view and button to the File's Owner, using quote_text and quote_btn_touch respectively.
Trouble is that when I click on the button, nothing happens. Any idea what I missed?
Thanks in advance!
your setting quote text to nil on viewDidLoad. If you get rid of that your code should work as long as you correctly bound the button to the viewcontroller function
quote_text = nil;

objective C: Using a Delegate to call a function in parent class

I'm creating a 3 layer navigation popup controller and on the 3rd popup controller I have a delegate method to access dismissPopup method that is in the parent class. I can't seem to call it, my NSLog messages in the function in the parent class isn't even showing so I must be either using delegation wrong or I'm calling it incorrectly.
The 3 classes ParentViewController has a toolbar with a button that brings up the table view --> RegionViewController is the First table view controller with items --> ConusViewController is the 2nd table view controller that is pushed onto the navigation stack. I'm trying to call the method dismissPopover that is in the parent method with a delegation after the selection is clicked on so the whole popover goes away.
In the ConusViewController if the delegation had worked I would have seen "Method Accessed" from the function in the parent class. It doesn't show so I must be using delegation wrong.
Sorry for being so wordy on my post, I wanted to be complete on what I'm trying to do here. Thanks.
ParentViewController.h
#import <UIKit/UIKit.h>
#import "ConusViewController.h"
#interface EnscoWXViewController : UIViewController <ConusViewControllerDelegate> {
UIPopoverController *popoverController;
IBOutlet UIWebView *webImageDisplay;
ConusViewController *cViewController;
}
#property (nonatomic, retain) UIPopoverController *popoverController;
#property (nonatomic, retain) UIWebView *webImageDisplay;
#property (nonatomic, retain) ConusViewController *cViewController;
-(IBAction) buttonShowRegion:(id) sender;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "RegionViewController.h"
#implementation ParentViewController
#synthesize cViewController;
-(IBAction) buttonShowRegion:(id) sender {
...
}
-(void)dismissPopover {
[popoverController dismissPopoverAnimated:YES];
printf("Method Accessed\n");
}
- (void)viewDidLoad {
cViewController = [[ConusViewController alloc] init];
cViewController.delegate = self;
[super viewDidLoad];
}
RegionViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: {
ConusViewController *conusViewController = [[ConusViewController alloc] initWithNibName:#"ConusViewController" bundle:nil];
conusViewController.contentSizeForViewInPopover = CGSizeMake(320, 350);
[self.navigationController pushViewController:conusViewController animated:YES];
[conusViewController release];
break;
}
case 1: {
break;
}
}
}
ConusViewController.h
#import <UIKit/UIKit.h>
#protocol ConusViewControllerDelegate <NSObject>
#required
- (void)dismissPopover;
#end
#interface ConusViewController : UITableViewController {
NSMutableArray *conusItems;
id delegate;
}
#property (nonatomic, assign) id <ConusViewControllerDelegate> delegate ;
#end
ConusViewController.m
#import "ConusViewController.h"
#import "ParentWXViewController.h"
#implementation ConusViewController
#synthesize delegate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *itemRequested = [conusItems objectAtIndex:indexPath.row];
NSLog(#"logging: %#", itemRequested);
[delegate dismissPopover];
[itemRequested release];
}
Just before calling [delegate dismissPopover], check if delegate is actually set. It probably isn't.
I see in ParentViewController.m you create an instance of ConusViewController and set its delegate, but never display it. In RegionViewController.m you create another instance of ConusViewController without setting its delegate and that is the one that seems to be being displayed.
Not sure if I missed it, but I never see you set the delegate property in ConusViewController. That needs to be set to an instance of the object that is to be delegated to (the object that has dismissPopover implemented in it).