NSTableView not populating any data - xcode

I am trying to set up a very basic NSTableView with one column via the example code in Apple's documentation. I am setting it up programatically as Cocoa bindings are still a little like a dark art to me at the moment, however when I Build & Run I get no data in my app. Is there something missing from my code? (I've also hooked up my datasource and delegate via Interface Builder, so it can't be that either.)
Interface file
#import <Cocoa/Cocoa.h>
#interface RLTAppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSScrollView *tableView;
#property (copy) NSArray *nameArray;
#end
Implementation file
#import "RLTAppDelegate.h"
#implementation RLTAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_nameArray = [[NSArray alloc] initWithObjects:#"Ryan", #"Steven", #"Scott", nil];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return _nameArray.count;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTextField *result = [tableView makeViewWithIdentifier:#"withoutIcon" owner:self];
result.stringValue = [self.nameArray objectAtIndex:row];
return result;
}
#end

I think you missed
#property (weak) IBOutlet NStableView *tableview;

Connect your Outlet of tableview from xib/storyboard and dont forget to set delagate and datasource.
#property (weak) IBOutlet NSTableView *tableView;
In Interface Builder, in the Attributes inspector pane (Cmd-Opt-4), set the table view type to View Based. It is Cell Based by default.
You have to select the table view in either in the left-hand object view or by clicking on it in the editor. Notice that the first time you click on it in the editor, it will select the scroll view, so you will have to click on it a second time to get it to select the table view object.
I believe you need Xcode 4.3 or newer for the OS X 10.7 SDK, and hence view-based tables views, to be available.

I tried the same thing and it turned out that the NSTextField result was not being instantiated. I ended up using
NSTextField *result = [[NSTextField alloc]init];
result.string = [_nameArray objectAtIndex:row];
and this works just fine.

Related

Why won't the data display in my NSTableView(view based)?

I followed the advice here on how to setup a MainWindowController: NSWindowController for my project's single window. I used a Cocoa class to create the .h/.m files, and I checked the option Also create .xib for User Interface. As a result, Xcode automatically hooked up a window, which I renamed MainWindow.xib, to my MainWidowController.
Next, I deleted the window in the default MainMenu.xib file (in Interface Builder I selected the window icon, then I hit the delete key). After that, I was able to Build my project successfully, and my controller's window in MainWindow.xib displayed correctly with a few buttons on it.
Then I tried adding an NSTableView to my MainWindowController's window. In Xcode, I dragged the requisite delegate and datasource outlets for the NSTableView onto File's Owner, which is my MainWindowController, and I implemented the methods in MainWindowController.m that I thought would make the NSTableView display my data:
- tableView:viewForTableColumn:row:
- numberOfRowsInTableView:
Now, when I Build my project, I don't get any errors, but the data doesn't appear in the NSTableView.
My code is below. Any tips are welcome!
//
// AppDelegate.h
// TableViews1
//
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#end
...
//
// AppDelegate.m
// TableViews1
//
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) MainWindowController* mainWindowCtrl;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setMainWindowCtrl:[[MainWindowController alloc] init] ];
[[self mainWindowCtrl] showWindow:nil];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
...
//
// MainWindowController.h
// TableViews1
//
#import <Cocoa/Cocoa.h>
#interface MainWindowController : NSWindowController
#end
...
//
// MainWindowController.m
// TableViews1
//
#import "MainWindowController.h"
#import "Employee.h"
#interface MainWindowController () <NSTableViewDataSource, NSTableViewDelegate>
#property (strong) NSMutableArray* employees;
#property (weak) IBOutlet NSTableView* tableView;
#end
#implementation MainWindowController
- (NSView*)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
Employee* empl = [[self employees] objectAtIndex:row];
NSString* columnIdentifier = [tableColumn identifier];
//The column identifiers are "firstName" and "lastName", which match my property names.
//You set a column's identifier by repeatedly clicking on the TableView until only
//one of the columns is highlighted, then select the Identity Inspector and change the column's 'Identifier' field.
NSString* emplInfo = [empl valueForKey:columnIdentifier]; //Taking advantage of Key-Value coding
NSTableCellView *cellView =
[tableView makeViewWithIdentifier:columnIdentifier
owner:self];
NSLog(#"The Table view is asking for employee: %#", [empl firstName]);
[[cellView textField] setStringValue:emplInfo];
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [[self employees] count];
}
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
Employee* e1 = [[Employee alloc] initWithFirstName:#"Joe" lastName:#"Blow"];
Employee* e2 = [[Employee alloc] initWithFirstName:#"Jane" lastName:#"Doe"];
[self setEmployees:[NSMutableArray arrayWithObjects:e1, e2, nil]];
//Test to see if the employees array was populated correctly:
Employee* e = [[self employees] objectAtIndex:0];
NSLog(#"Here is the first employee: %#", [e firstName]);
//I see the output: "Here is the first employee: Joe"
}
- (id)init {
return [super initWithWindowNibName:#"MainWindow"];
}
- (id)initWithWindowNibName:(NSString *)windowNibName {
NSLog(#"Clients cannot call -[%# initWithWindowNibName] directly!",
[self class]
);
[self doesNotRecognizeSelector:_cmd];
return nil;
}
#end
...
//
// Employees.h
// TableViews1
#import <Foundation/Foundation.h>
#interface Employee : NSObject
#property NSString* firstName;
#property NSString* lastName;
- initWithFirstName:(NSString*)first lastName:(NSString*)last;
#end
...
//
// Employees.m
// TableViews1
//
#import "Employee.h"
#implementation Employee
- (id)initWithFirstName:(NSString *)first lastName:(NSString *)last {
if (self = [super init]) {
_firstName = first; //I read that you shouldn't use the accessors in init methods.
_lastName = last;
}
return self;
}
#end
File's Owner(=MainWindowController) connections:
NSTableView connections:
Response to comments:
Here is why calling [self tableView] reloadData] at the end of -windowDidLoad, as suggested in the comments, didn't work:
My _tableView instance variable--created by my #property declaration in MainWindowController.m--doesn't point to anything; therefore calling:
[[self tableView] reloadData]
I think is equivalent to calling:
[nil reloadData]
which doesn't do anything.
I never assigned anything to the _tableView instance variable in the -init method, nor did I assign it a value by dragging an outlet somewhere in Interface Builder. To fix that problem, I selected MainWindow.xib (the controller's window) in the Project Navigator(left pane), and then in the middle pane(Interface Builder), I selected the cube representing the File's Owner(selecting the Identity Inspector in the right pane reveals that the File's Owner is the MainWindowController). Then in the right pane, I selected the Connections Inspector, and it revealed an outlet called tableView, which is the IBOutlet variable I declared in MainWindowController.m.
Next, I dragged from the tableView outlet onto the TableView in the middle pane:
Doing that assigns the NSTableView object to the _tableView instance variable that was created by my #property declaration in MyWindowControler.m:
#property (weak) IBOutlet NSTableView* tableView;
As an experiment, I disconnected the outlet, then commented out the #property declaration for tableview, and the tableView outlet no longer appeared in the Connections Inspector. Also, if I change the declaration from:
#property (weak) IBOutlet NSTableView* tableView;
to:
#property (weak) NSTableView* tableView;
...then the tableView outlet doesn't appear in the Connections Inspector. That experiment answered a couple of questions I had about whether I should declare a property as an IBOutlet or not: if you need to assign one of the objects in Interface Builder to one of your variables, then declare the variable as an IBOutlet.
Thereafter, calling [self tableView] reloadData] at the end of -windowDidLoad succeeds in populating the TableView. However, I have not seen any tutorials that call reloadData, and even Apple's guide does not do that.
So, I am still puzzled about whether calling -reloadData is a hack or it's the correct way to do things.
Without it, your table view sits there blissfully clueless about your
expectation that it should even bother asking its datasource for data.
I assumed that an NSTableView automatically queries its datasource when it is ready to display itself, and that my code needed to be able to provide the data at that time.
I don't see you sending -reloadData to your table view anywhere. Tacking it onto the end of -windowDidLoad would be a good place. Without it, your table view sits there blissfully clueless about your expectation that it should even bother asking its datasource for data.
For all it knows, the data is simply not ready / available, so why would it try? More importantly, when should it try? It'd be rather rude of it to try whenever it pleases, considering the UI may not have finished loading / connecting to outlets, or its datasource may be in a vulnerable state (like teardown during/after dealloc) and sending datasource requests may result in a crash, etc.
Two things:
1st, set some breakpoints on when you set your employees array in windowDidLoad vs. when the table first attempts to populate itself and your numberOfRowsInTableView implementation gets called. If the latter happens before the former, then you'll need to add a reloadData after you create your array.
2nd, I personally always use NSCell instead of NSViews for my tables, so I always implement objectValueForTableColumn in my table's datasource. So I'm not sure if there's something different you need to do when you use NSView objects and implement viewForTableColumn. Is there a reason you're not using NSCell?

xCode Tableview set bounds

Trying to set the bounds of my table view to be smaller than my screen. I am setting it in my viewDidLoad method but nothing happens when I run. Not sure why it's not working. Am I calling it in the right place??
//OptionsViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.frame = CGRectMake(1,1,29,80);
}
// OptionsViewController.h
#import <UIKit/UIKit.h>
#interface OptionsViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
NSIndexPath *selectedRowIndex;
}
#property (weak, nonatomic) NSIndexPath *selectedRowIndex;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
Set the frame in viewDidAppear method . It works. You might want to look at the following question and answer for a good explanation :
Unable to set frame correctly before viewDidAppear
TL;DR - The UI elements are not drawn to screen yet during viewDidLoad .

passing data back to view controller not working (iOS)

I see there are lots of posts on this topic but none seem to solve my problem.
I have to a view controller with has just a textfield, a navigation bar button (called save) and a number/punctuation keyboard.
The other view controller has a static table view with 2 rows.
Workflow: When a user taps on the 1st row in the table, the second view controller appears (this already works).
The user then enters a number and when they tap save, the number typed should be used to set the detail label of the 1st row in the table view.
I have set up my protocols and delegates but something is wrong as the 2nd view controller does not disappear and also detail label never gets updated to reflect this typed number.
I am very stumped. Been through lots of code samples and tried so many things but still no solution. Any help is greatly appreciated.
Below is my code for both classes.
1st View (The table view)
.h file (The hourlyRateDetialLabel is the detail label from the table view):
#import UIKit/UIKit.h
#import "priceCalculatorHrRateSettingsViewController.h"
#interface priceCalculatorSettingsViewController : UITableViewController<SettingsViewControllerDelegate>
#property (strong, nonatomic) IBOutlet UILabel *hourlyRateDetialLabel;
#end
.m file
#import "priceCalculatorSettingsViewController.h"
#import "priceCalculatorHrRateSettingsViewController.h"
#interface priceCalculatorSettingsViewController ()
#end
#implementation priceCalculatorSettingsViewController
#synthesize hourlyRateDetialLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
priceCalculatorHrRateSettingsViewController *vc = [[priceCalculatorHrRateSettingsViewController alloc] init];
vc.settingsViewDelegate = self;
}
- (void) HourlyRateDidSave:(priceCalculatorHrRateSettingsViewController *)controller didSetHourlyRate:(NSString *)rateValue{
self.hourlyRateDetialLabel.text = rateValue;
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
The 2nd Class (The View with the textfield and save button)
.h file
#import UIKit/UIKit.h
#class priceCalculatorHrRateSettingsViewController;
#protocol SettingsViewControllerDelegate <NSObject>
- (void)HourlyRateDidSave:
(priceCalculatorHrRateSettingsViewController *)controller didSetHourlyRate:(NSString *)rateValue;
#end
#interface priceCalculatorHrRateSettingsViewController : UIViewController<UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet UITextField *setHourlyRate;
#property (weak, nonatomic) id<SettingsViewControllerDelegate> settingsViewDelegate;
- (IBAction)saveHourlyRateValue:(id)sender;
#end
.m file
#import "priceCalculatorHrRateSettingsViewController.h"
#interface priceCalculatorHrRateSettingsViewController ()
#end
#implementation priceCalculatorHrRateSettingsViewController{
}
#synthesize setHourlyRate = _setHourlyRate;
#synthesize settingsViewDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//Automatically show the keybaord
[_setHourlyRate becomeFirstResponder];
}
- (void)viewDidUnload
{
[self setSetHourlyRate:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)saveHourlyRateValue:(id)sender {
[self.settingsViewDelegate HourlyRateDidSave:self didSetHourlyRate:_setHourlyRate.text];
}
#end
The provided code includes:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
priceCalculatorHrRateSettingsViewController *vc = [[priceCalculatorHrRateSettingsViewController alloc] init];
vc.settingsViewDelegate = self;
}
This creates a controller when a cell is selected but never presents it and yet the unexpected behavior is described as:
...2nd view controller does not disappear...
These seems to be at odds with each other. Without an accurate description of 1. the expected behavior 2. the observed behavior and 3. the actual implementation producing the observed behavior, it is almost impossible for anyone to solve this problem. Please do not ask us all to guess how to help.

NSTableView not Populating

I've been trying to get this NSTableView to populate for the last 7 hours. I am trying to get a list of all the currently running application and put them into an NSTableView. Eventually I would like to parse the resultes and organize the PID in one column and the Application Bundle in the other. I am getting an EXC_BAD_ACCESS error on " return [listOfWindows objectAtIndex:row];" I am currently using Xcode 4.3.2 and running OS X Lion 10.7.4. Thanks in advance everyone!
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSMenu *statusMenu;
IBOutlet NSButton *button;
IBOutlet NSWindow *menuWindow;
IBOutlet NSTableView *proTable;
NSArray *listOfWindows;
IBOutlet NSArrayController *arrayController;
AppDelegate *mainMenu;
NSWorkspace *workSpace;
NSStatusItem *statusItem;
}
#property (assign) IBOutlet NSWindow *window;
-(IBAction)loadConfig:(id)sender;
#end
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
- (void) awakeFromNib
{
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadMenu:)
name:#"WhiteBox"
object:nil];
[self addStatusItem];
//[proTable setDataSource:self];
listOfWindows = [[NSWorkspace sharedWorkspace] runningApplications];
NSLog(#"index %#", listOfWindows);
int y = 0;
y = [listOfWindows count];
NSLog(#"y = %d", y);
[proTable setAllowsMultipleSelection:YES];
}
-(void)applicationWillTerminate
{
NSLog(#"Will Terminate");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
-(void)applicationDidResignActive:(NSNotification *)notification
{
NSLog(#"Resign Active");
}
-(void) addStatusItem
{
//Create a variable length status item from the system statusBar
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem retain];
//Set a Title for it
[statusItem setTitle:#"Status Item"];
//Set an Image and an alternate image
//[statusItem setImage:[NSImage imageNamed:#"lnc"]];
//[statusItem setAlternateImage: [NSImage imageNamed:#"status"]];
//Add a Tool Tip
[statusItem setToolTip:#"Status Item Tooltip"];
//Choose to highlight the item when clicked
[statusItem setHighlightMode:YES];
//To Trigger a method on click use the following two lines of code
[statusItem setMenu:statusMenu];
//[statusItem setAction:#selector(loadMenu:)];
}
-(IBAction)loadConfig:(id)sender
{
if(! [menuWindow isVisible] )
{
[menuWindow makeKeyAndOrderFront:sender];
} else {
[menuWindow performClose:sender];
}
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [listOfWindows count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
return [listOfWindows objectAtIndex:row];
}
#end
What object is the table view's data source? I don't see any object in the source you posted as implementing the NSTableViewDataSource protocol.
Further, have you tried putting breakpoints in the various data source methods to see if the debugger stops in them? If not, it's usually a good sign that your data source isn't connected to your table view.
I got: -[NSRunningApplication copyWithZone:]: unrecognized selector error when I ran your code. This could be fixed by changing your return line in tableView:objectValueForTableColumn:row: to
return [[listOfWindows objectAtIndex:row]localizedName];
NSRunningApplication doesn't conform to NSCopying, so I don't know if you can put instances of that class in a table view. However, you can get its properties like localizedName, processIdentifier, and bundleIdentifier.
I've run into this problem before with classes that don't implement NSCopying, I'd be happy to know if anyone knows a way to use these classes in table views or outline views.

Creating a UITabBarController using a NIB outside of AppDelegate?

Still new to iOS programming, and despite copious amounts of research, I have run in to another roadblock.
What I want to implement:
I want a UITabBarController that gets loaded when I navigate from the main UI. I would also like to use a NIB to define its properties.
All of the examples I can find put the UITabBarController in the AppDelegate, but I would not like to load it unless it gets used. I also dont know if all of the UIGestureRecognizers would remain active if I just did it modally (I cant get a working implementation).
What I have so far
First, I load an initial loading view from AppDelegate
AppDelegate.h
#class InitialViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *viewController;
#end
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[InitialViewController alloc] initWithNibName:#"InitialViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
From this view, as I am just making a skeleton of the UI, I have two buttons, one goes to what would be the main interface, and the other to the UITabBarController.
InitialViewController.h
#interface InitialViewController : UIViewController
- (IBAction)toMain:(id)sender;
- (IBAction)toTabs:(id)sender;
#property (strong, nonatomic) UIViewController *mviewController;
#property (strong, nonatomic) UIViewController *tviewController;
#end
InitialViewController.m
- (IBAction)toMain:(id)sender {
self.mviewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[[[UIApplication sharedApplication] delegate] window].rootViewController = self.mviewController;
}
- (IBAction)toTabs:(id)sender {
self.tviewController = [[tabViewController alloc] initWithNibName:#"tabViewController" bundle:nil];
[[[UIApplication sharedApplication] delegate] window].rootViewController = self.tviewController;
}
On loading MainViewController, it behaves exactly like I want. But when I load the tab view, I get one long tab at the bottom and a black background. I can add in things in viewdidload, like changing the background color, but no actual tabs or views linked to the tabs in the XIB.
I suspect there is something I am missing in two areas: in the tab .h, and some linking associated with that in interface builder. Or setting a new rootViewController isnt enough.
tabBarController.h
#import <UIKit/UIKit.h>
#interface iPodViewController : UITabBarController <UITabBarControllerDelegate>
#end
If someone can point me in the right direction and/or show me an implementation that works, I would be most grateful.
-- as a note, when I go in to the tabbar.xib, and use the assistant editor, it opens InitialViewController.h --
Unlike other view controllers (e.g. UITableViewController) you should not subclass the UITabViewController. Therefore, unlike you other view controllers, you don't subclass and then make your subclass the owner of the nib, pointing at the view in the nib, with a customised view.
Instead, for whichever class that you want to own your UITabBarController, add a plain, vanilla UITabBarController as an outlet property on this class. (e.g. your app delegate).
Then create a nib file and drag a UITabBarController object into the nib. Set the owner of the nib to be the class that you want to own your tab bar controller (e.g. your app delegate) and connect the outlet you created as a property to the tab bar controller in the nib.
#interface myTabOwningClass
#property (strong, nonatomic) IBOutlet UITabBarController myTabBarControllerOutlet;
Now at the point you want to create and display your tab bar controller, use the following method:
[[NSBundle mainBundle] loadNibNamed:#"MyTabControllerNib" owner:myTabOwningClass options:nil];
This will initialise the property (i.e. myTabBarControllerOutlet in our example) on the owning class and load the tab bar controller from the nib, including all sub view controllers for each tab etc. that you have defined in the nib.

Resources