I have the following Objective C code that keeps error complaining I am redefining it when I not!! I am trying to implement a NSTableViewDelegate thing but xcode keep complaining
#import "AppDelegate.h"
#import <AppKit/AppKit.h>
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// add Hello World to sidebar
[self.Sidebar.tableView insertValue:0 atIndex:0 inPropertyWithKey:#"Hello World!"];
[self.Sidebar.tableView setDelegate: SidebarDelegate()]
// reload data
[self.Sidebar.tableView reloadData];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
return YES;
}
#end
// Sidebar Delage
#implementation SidebarDelegate : NSObject <NSTableViewDelegate, NSTableViewDataSource>;
// setting data
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
anObject = #"Apples";
}
// height
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
return 20;
}
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
{
NSLog(#"%ld", (long)rowIndex);
return ((long)rowIndex % 2) == 0;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return 4;
}
#end
replace
[self.Sidebar.tableView setDelegate: SidebarDelegate()];
with
SidebarDelegate *sidebarDelegate = [[SidebarDelegate alloc] init];
[self.Sidebar.tableView setDelegate:sidebarDelegate];
This line:
[self.Sidebar.tableView setDelegate: SidebarDelegate()];
implicitly creates a function named SidebarDelegate() of the form
int SidebarDelegate(void)
Thus, when you try to define a class of the same name, you overwrite the symbol in an unacceptable way. You need to initialize an instance of SidebarDelegate and pass that as the TableView's delegate not, I will presume, just pass the name of the class.
Related
I have the IntroScene, and I wanna add a node, but it doesn't seem to work. Here are two different ways I tried doing it, and BOTH failed.
First way, failed:
in hearts2.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Hearts2 : CCNode {
}
#end
in hearts2.m
#import "Hearts2.h"
#implementation Hearts2
#end
in IntroLayer.m
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
heart2 *heart;
[self addChild:heart z:2];
// done
return self;
}
I didn't expect that to work (actually I was desperate and tried it that way as the second way just to see if it would work). The actual first attempt I tried to do was this, and it also Failed:
in hearts1.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Hearts1 : CCNode
+ (Hearts1 *)node;
- (id)init;
-(void)selfAnimate;
#end
in hearts1.m
#import "Hearts1.h"
#implementation Hearts1 {
}
+ (Hearts1 *)node
{
return [[self alloc] init];
}
- (id)init
{
self = [super init];
if (!self) return(nil);
return self;
}
- (void)dealloc
{
}
- (void)onEnter
{
[super onEnter];
}
- (void)onExit
{
// always call super onExit last
[super onExit];
}
- (void)selfAnimate
{
}
#end
in IntroLayer.m
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
heart1 *heart;
[self addChild:heart z:2];
// done
return self;
}
Please, I would do anything if someone could help me figure this out thanks everyone very much. I always get the SigABRT so I have no idea what is going wrong. I'm sure I'm just stupid and don't know how to code and missing something simple.
heart2 *heart;
You named your class Hearts2 so use the exact same name, including uppercase.
Secondly you created a variable but this will be nil. If you aren't using ARC (which you should) this will create an uninitialized object.
This will create an instance of Hearts2, assign it to the local var heart and add it as a child:
Hearts2 *heart = [Hearts2 node];
[self addChild:heart z:2];
I have a View-Based NSTableView which should be initially empty. I also have "add" and "remove" buttons for adding and deleting rows from same NSTableView.
My delegate methods and method for adding new row look like this:
#import "PreferencesApiKeysViewController.h"
#import "UserKeys.h"
#interface PreferencesApiKeysViewController ()
#property (weak) IBOutlet NSTableView *keysTableView;
#end
#implementation PreferencesApiKeysViewController
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get a new ViewCell
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if([tableColumn.identifier isEqualToString:#"userKeysColumn"]) {
UserKeys *allKeys = [self.allKeys objectAtIndex:row];
cellView.textField.stringValue = allKeys.userKeyName;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"Initial rows: %li", (unsigned long)[self.allKeys count]);
return [self.allKeys count];
}
- (IBAction)addKey:(id)sender {
UserKeys *newKey = [[UserKeys alloc] initWithKeyName:#""
apiID:#""
apiCode:#"" ];
[self.allKeys addObject:newKey];
NSLog(#"Total rows: %li", (unsigned long)self.allKeys.count);
NSInteger newRowIndex = self.allKeys.count;
if (newRowIndex == 0) {
NSLog(#"No rows.");
newRowIndex = 0;
} else {
NSLog(#"Has rows.");
newRowIndex = self.allKeys.count-1;
}
NSLog(#"New Index: %ld", (long)newRowIndex);
[self.keysTableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:newRowIndex] withAnimation:NSTableViewAnimationSlideDown];
[self.keysTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:newRowIndex] byExtendingSelection:NO];
[self.keysTableView scrollRowToVisible:newRowIndex];
}
#end
and in my AppDelegate.m, I'm calling my view like this:
#import "AppDelegate.h"
#import "UserKeys.h"
#include "PreferencesApiKeysViewController.h"
#interface AppDelegate()
#property (nonatomic,strong) IBOutlet PreferencesApiKeysViewController *prefsViewController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.prefsViewController = [[PreferencesApiKeysViewController alloc] initWithNibName:#"PreferencesApiKeysViewController" bundle:nil];
// Create few rows as dummy data
/*
UserKeys *key1 = [[UserKeys alloc] initWithKeyName:#"key one"
apiID:#"123"
apiCode:#"xxx" ];
UserKeys *key2 = [[UserKeys alloc] initWithKeyName:#"key two"
apiID:#"456"
apiCode:#"yyy" ];
NSMutableArray *tempKeys = [NSMutableArray arrayWithObjects:key1, key2, nil];
self.prefsViewController.allKeys = tempKeys;
*/
// done.
[self.window.contentView addSubview:self.prefsViewController.view];
self.prefsViewController.view.frame = ((NSView*)self.window.contentView).bounds;
}
#end
Now, the thing here is that if I uncomment those few lines for adding dummy data in AppDelegate and launch my app, everything works fine. I can add/remove rows without any problem, I can even delete all of them and add a new one after that.
But, if I comment those lines again, my app starts with an empty table (which is what I need), and when I want to add new row I get an error:
*** Assertion failure in -[NSTextFieldCell _objectValue:forString:errorDescription:], /SourceCache/AppKit/AppKit-1187.34/AppKit.subproj/NSCell.m
Looking further the thread, I see only one line referencing to my app:
0x0000000100003116 -[PreferencesApiKeysViewController tableView:viewForTableColumn:row:] + 406
I'm guessing that my TableView is not properly instantiated if allKeys array doesn't contain any single object really, but I'm not sure how to fix that? How to create an empty NSTableView and have the ability to add the first row by myself when "add" button is clicked (without adding any dummy data to allKeys)?
Add this to the implementation of PreferencesApiKeysViewController:
#implementation PreferencesApiKeysViewController
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle {
if ((self = [super initWithNibName:nibName bundle:bundle])) {
self.allKeys = [NSMutableArray array];
}
return self;
}
// add the following only if not using ARC
- (void)dealloc {
[_allKeys release];
[super dealloc];
}
#end
What's happening when you allow that commented-out code to be compiled in is that you assign an NSMutableArray instance to the allKeys instance variable. As a result, adding and removing items works as you would expect.
Generally, in a case like this, the class that manages the NSMutableArray should override the init method to make sure the array is properly initialized to an empty array. (By default, non-IBOutlet instance variables are initialized to nil, which won't allow you to properly add or remove items from it).
I'm implementing a subclass of NSActionCell (inside an NSTableView), and noticing something unusual. If I set a property (isEditing) when a user clicks a cell, the value of that property is lost, because NSCell is released shortly thereafter. I assumed that this was happening because I wasn't handling copying correctly, so I added copyWithZone. Now I am seeing copyWithZone called - but it's called on an unexpected instance - and the property on that instance is NO - the default value. Each time copyWithZone is called, it's called on this same instance.
Can anyone shed a light on this behavior? I'm attaching the subclass in question, and the output I'm getting. Exactly what do I need to do for cell's properties to be retained when the user clicks on different cells?
#interface MyCell : NSActionCell <NSCoding, NSCopying>
{
}
#property (nonatomic, assign) BOOL isEditing;
#end
#implementation MyCell
- (id)init
{
if ((self = [super init]))
{
[self initializeCell];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self initializeCell];
self.isEditing = [[aDecoder decodeObjectForKey:#"isEditing"] boolValue];
NSLog(#"initWithCoder %ld %i", (NSInteger)self, self.isEditing);
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[super encodeWithCoder: aCoder];
NSLog(#"encode %i", self.isEditing);
[aCoder encodeObject:[NSNumber numberWithBool:self.isEditing] forKey:#"isEditing"];
}
- (void)dealloc
{
NSLog(#"dealloc %ld %i", (NSInteger)self, self.isEditing);
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
MyCell *copy;
if ((copy = [[MyCell allocWithZone:zone] init]))
{
copy.isEditing = self.isEditing;
}
NSLog(#"copy %ld %i new: %ld", (NSInteger)self, self.isEditing, (NSInteger)copy);
return copy;
}
- (void)initializeCell
{
self.isEditing = NO;
}
- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
{
return YES;
}
- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag
{
if (flag)
{
self.isEditing = YES;
NSLog(#"stopTracking %ld %i", (NSInteger)self, self.isEditing);
}
}
#end
Output (produced when a user clicks the cell):
2012-11-21 08:17:59.544 SomeApp[2778:303] copy 4310435936 0 new: 4310152512
2012-11-21 08:18:00.136 SomeApp[2778:303] stopTracking 4310152512 1
2012-11-21 08:18:00.136 SomeApp[2778:303] dealloc 4310152512 1
and another click (on a different cell):
2012-11-21 08:19:24.994 SomeApp[2778:303] copy 4310435936 0 new: 4310372672
2012-11-21 08:19:25.114 SomeApp[2778:303] stopTracking 4310372672 1
2012-11-21 08:19:25.114 SomeApp[2778:303] dealloc 4310372672 1
It sounds like you want to persist the properties – is that right?
You'll probably have an easier time if you adjust your design by storing the cell properties in your model objects instead of the NSCell, and having the cell or table view delegate fetch the value from the model.
What particular behavior are you trying to achieve using this property?
Simple question: where do the tableView and section arguments get passed from? The actual code in the method return [self.listData count]; doesn't even mention them.
Here's my interface code:
#interface Simple_TableViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource>
{
NSArray *listData;
}
#property (nonatomic, retain) NSArray *listData;
#end
And this is all the implementation code:
#import "Simple_TableViewController.h"
#implementation Simple_TableViewController
#synthesize listData;
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:#"Sleepy", #"Sneezy",
#"Bashful", #"Happy", #"Doc", #"Grumpy", #"Dopey", #"Thorin",
#"Dorin", #"Nori", #"Ori", #"Balin", #"Dwalin", #"Fili", #"Kili",
#"Oin", #"Gloin", #"Bifur", #"Bofur", #"Bombur", nil];
self.listData = array;
[array release];
[super viewDidLoad];
}
- (void)viewDidUnload {
self.listData = nil;
}
- (void)dealloc {
[listData release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.listData count];
}
I just want to know how does the method (NSInteger)tableView: (UITableView *)numberOfRowsInSection: receive those arguments? Of course this happens everywhere; I just want to understand it.
The Simple_TableViewController class is likely meant to manage a single table with a single section. Given that, the tableView and section parameters aren't important because they can only be one thing: a pointer to the table and 0, respectively.
Your view controller class is adding support for these callback methods through UITableViewDelegate and UITableViewDataSource. You are adding this support in your .h file through <UITableViewDelegate, UITableViewDataSource>. These classes are built in to the Cocoa Touch framework and you are just using them. When the table is (re)loaded, this callback methods are called if you have defined them (some are required, others are optional).
I'm having trouble getting an NStableview to populate. I've been working through examples and have been having some success using a test array to feed it. My only problem is that the file often crashes on running (sometimes it doesn't, it's temperamental) and I think I've whittled it down to it sometimes returning nothing for array count. I'm going to guess this is because the array is sometimes not fully loaded by the time it's called by the tableview controller. Any ideas how to solve this? Thanks in advance.
tableController.h:
#import <Cocoa/Cocoa.h>
NSArray *testArray;
#interface tableController : NSObject {
IBOutlet NSTableView *jobsTable;
}
#end
tableController.m:
#import "tableController.h"
#implementation tableController
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
testArray = [NSArray arrayWithObjects:#"foo",#"bar",#"baz",nil];
}
- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
if ([testArray count] > 0) {
return [testArray count];
} else {
return 0;
}
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(int)row
{
if ([testArray count] > 0) {
return [testArray objectAtIndex:row];
} else {
return 0;
}
}
#end
I am pretty sure testArray could be an instance variable, and there is no need for a global variable.
If you do this you could use a custom getter for testArray which lazy loads the array.
like this:
- (NSArray *)testArray {
if (!testArray) {
testArray = [[NSArray alloc] initWithObjects:#"foo",#"bar",#"baz",nil];
}
return testArray;
}
and all your tableView datasource methods would use self.testArray instead of testArray
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.testArray count];
}
Try using init instead of applicationDidFinishLaunching for initialization. According to the documentation of NSApplicationDelegate, applicationDidFinishLaunching is called "after the application has been launched and initialized", but the TableView may well try to fetch its entries from the two delegate methods before that method is called.
If you absolutely have to use applicationDidFinishLaunching, you will have to check if testArray != nil before you access it in your delegate methods.