Why can't I access variables when I subclass a customised UIViewController? - objective-c

I am new to iOS dev and apologies if the answer is obvious...but it isn't to me.
I have an APP with a Navigation controller at its root.
I have many very similar looking areas of the app to be created.
These are each to be UItableviewcontroller which has had a fair bit of customising done to allow buttons and other controls beside the tableview which has been reduced in size to allow for controls beside and below it.
The buttons, text, background etc etc and the data that gets loaded must all be individual to the particular are of the APP.
I created a UItableviewcontroller subclass by simply adding a new file subclass in Xcode.
I created my interface in the xib...created all the methods to drive what I need to in it.
Looks great...all seems fine. If I use it alone...works well.
Problem: I can't figure out how to subclass my custom sub-classed UITVController!
None of its properties are available from inside the new sub-class.
I clearly don't understand how things work here.
I have tried adding a new file > UIViewcontroller sub-class and changing the superclass to my custom superclass...to no avail. No properties accessible.
I have dug and dug and become more confused than anything else.
Is someone kind enough to help me get it right. Frustration is building.
Thanks
Keispe
EDIT:
Whoa found the problem. I have had Xcode open for many many days with several projects open.
It had totally weirded out!
In fact jrturton and eugene...I did know what I was doing (I thought I was going crazy...done this before in my app and suddenly no worky) Xcode had totally lost it's brains!
Anyone seen Xcode do that before??? using 4.1
Bloody hell that wasted a heap of valuable time including yours.
Thanks fellas

When you subclass anything, you can access your parent's class properties by addressing self via dot syntax
#interface BaseClass : NSObject {
#public
NSString *baseclassString;
}
#property (nonatomic, copy) NSString *name;
#end
.h
#interface HigherClass : BaseClass
#end
.m
#implementation HigherClass
- (id)init {
self = [super init];
self.name = #"Hola";
self->baseclassString = #"Hola";
return self;
}
- (void)viewDidLoad {
NSLog(#"name: %#", self.name);
}
#end
This all isn't 100% memory clean but you've gotta get a hang of what is happening here and adjust it properly to your application.

Related

Objective-C: using methods from other files

I have looked at all the other people who had this error pop up, and I know it is a pathing error, and yes I have tried to quit and reboot it, I checked the paths were using up-to-date files, and most of what people suggested with no success. I am trying to get methods from TapViewController to run in JumpController. I simplified the method I want to be called to make it easier to find the issue, but still am having trouble. Here is the relevant code I have so far:
TapViewController.h
-(void)hello;
TapViewController.m
-(void)hello {
NSLog(#"Hello");
}
JumpController.h
#import <UIKit/UIKit.h>
#import 'TapViewController.h'
#property (strong, nonatomic) TapViewController *TapView;
#property (strong, nonatomic) JumpController *JumpControl;
JumpController.m
-(void)viewDidLoad {
self.JumpControl = (TapViewController *)[UIApplication sharedApplication].delegate;
self.TapView = [[TapViewController alloc] init];
[self.JumpControl.TapView hello];
}
I grabbed most of this code from what others have said to do, so I don't really know if some of it is irrelevant or if all will help in the situation. Basically, the app crashes when it loads stating [AppDelegate TapView]: unrecognized selector sent to instance.... Let me know if I am doing anything wrong or if I left out relevant code!
UPDATE: Using what others have said and from my own personal changes, It seems like the problem is not only with self.JumpControl = (TapViewController *)[UIApplication sharedApplication].delegate but also with self.JumpControl as a whole. Because I created the TapViewController *TapView in the .h file there is no reason to use self.JumpControl which caused problems on the order of views that showed up. I will mark almas as correct, but I wanted to clarify what more needed to be done.
Your problem is here: "self.JumpControl = (TapViewController *)[UIApplication sharedApplication].delegate;". You are trying to cast your AppDelegate to TapViewController, thats why it crashes. AppDelegate is not a view controller. Your error message [AppDelegate TapView]: unrecognized selector sent to instance clearly states that AppDelegate doesn't recognize the method "TapView", it is because app delegate is not an instance of "TapViewController".

How to declare a variable from user input?

I am fairly new to programming and am working with Objective-C in Xcode 5.
I'm presently making an OSX application in Xcode that uses Cramer's Rule (this matrix math method to calculate the intersecting point of three lines).
I really need some help with this one concept- I need to be able to take the user's input from multiple text boxes (assign them all a variable), put them through cramer's rule, and feed the answer out through a label.
I've made the storyboard and assigned one of the 12 text boxes (to test it) as an outlet and the label as an outlet and a button as an action, and tried a few different ways to just take the user input and (unaltered) feed it back out through the label so I know what I'm working with before I get into the math, and it's been unsuccessful. Having major syntax problems.
I have attached my code below:
//
// NewClass.h
// Cramer's Rule
#import <Foundation/Foundation.h>
#interface NewClass : NSViewController <NSTextFieldDelegate> {
IBOutlet NSTextField *box_a;
IBOutlet NSTextField *coord;
NSString *string;
}
#property (nonatomic, retain) IBOutlet NSTextField *box_a;
#property (nonatomic, retain) IBOutlet NSTextField *coord;
- (IBAction)calculate:(id)sender;
#end
AND
//
// NewClass.m
// Cramer's Rule
#import "NewClass.h"
#implementation NewClass
#synthesize box_a;
#synthesize coord;
- (IBAction)calculate:(id)sender {
NSTextField * input=box_a;
coord =input;
}
#end
As far as I know, I have the most up to date version of Xcode, and there is no option for creating a storyboard for an OSX project. Storyboards are for iOS projects. And that would explain the reason why you're unable to hook any thing up from the storyboard to your code.
This isn't to say that a storyboard can't be put in an OSX project--it can't. But it can't be selected from the Cocoa section of new files to create--only the Cocoa Touch section, which is iOS stuff--not OSX.
You have to use NSTextFieldDelegate, it have callback methods like in iOS:
- (void)textDidBeginEditing:(NSNotification *)notification;
- (void)textDidEndEditing:(NSNotification *)notification;
- (void)textDidChange:(NSNotification *)notification;
- (BOOL)acceptsFirstResponder;
For example:
- (void)textDidChange:(NSNotification *)notification{
if ([notification object]== box_a)
{
// ...
}else if ([notification object]== box_b)
{
// ...
}
}
Your problem is more fundamental than syntactical, you need to go and study up on what various things are and how they behave, this includes: variables, properties, objects and object references.
To briefly introduce why you're going wrong: Think of an object as a building. What is "in" the building may change over time, but the address of the building (usually!) does not. An address refers you to a building, and that is what an object reference does.
A variable is a box which holds a value of some type, that value can change over time, but the box does not.
When you declare:
NSTextField *input;
You are requesting that a variable be created for you which can hold references to objects - it does not hold an object anymore than address is a building, it just tells you where to find an object.
When you then assign a value to your variable:
NSTextField *input = box_a;
You are requesting the the value in box_a be copied and placed (stored) in input. That value is an object reference, it is not an object. Whatever object was referenced by box_a is not altered in anyway by this statement - what is in the house doesn't change, you just write the house's address down somewhere else.
When you then do:
coord = input;
you are doing the same thing - copying addresses. No objects are altered. The objects you are referring to are of type NSTextField, they have a visual representation on the screen, copying their addresses doesn't alter that visual representation anymore than copying the address of a building changes what is in the building.
When it comes to properties your code suggests a confusion between a property, which is a piece of code which does something, and its backing variable, a variable which that piece of code operates on.
Understanding these concepts is vital. You need to go an study up some more on programming.
HTH

Unable to access App Delegate property

I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:
LTAppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
}
#property Subject *selectedSubject;
#end
LTAppDelegate.m
#synthesize selectedSubject;
The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:
LTTableViewController.h
#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"
#interface LTTableViewController : NSObject{
NSMutableArray *notesArray;
LTAppDelegate *appDelegate;
Subject *s;
}
-(IBAction)currentSubjectDetails:(id)sender;
#end
LTTableViewController.m
#import "LTTableViewController.h"
#implementation LTTableViewController
- (id)init
{
self = [super init];
if (self) {
appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);
s = [appDelegate selectedSubject];
NSLog(#"Test Subject: %#", [s title]);
}
return self;
}
-(IBAction)currentSubjectDetails:(id)sender{
NSLog(#"Selected Subject: %#", [s title]);
}
After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.
Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.
Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.
If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.
Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.
Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).
Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.
-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).
Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.
If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.

Category header imports not working

I have a workspace that has two projects in it. The first project was essentially a test and develop project where I got things working before worrying about tying everything together for real. The second project is bringing all my individually developed view controllers together in a storyboard.
On one of the view controllers I have a bunch of swipe gestures with quite a bit of UIView animation calls nicely formatted for readability and therefore taking a lot of space. I elected to move them out as a category.
The problem is that the compiler is not seeing the instance variable declarations in the main header file.
What has me pulling my hair out is that I did this in the first project and it all worked fine. So I'm carefully comparing the contents of my second project to the first and I see no differences.
Here're some file snippets to help demonstrate how/where I'm defining things, and then snippets of code in the category file that is attempting to access them:
GSBViewController.h
#interface GSBViewController : UIViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *roundPicker;
#property (strong, nonatomic) IBOutlet UIView *roundsSectionView;
GSBViewController.m
#import "GSBViewController+Swipe.h"
#interface GSBGameBuilderViewController ()
{
UIBarButtonItem *rightGatherBarButton;
NSInteger previousRound;
}
#end
#implementation GSBViewController
#synthesize roundPicker;
#synthesize roundsSectionView;
GSBViewController+Swipe.h
#import "GSBViewController.h"
#interface GSBViewController (Swipe)
- (void)establishSwipeGestures;
#end
GSBViewController+Swipe.m
#import "GSBViewController+Swipe.h"
#implementation GSBViewController (Swipe)
- (void)establishSwipeGestures
{
UISwipeGestureRecognizer *swipeLeft =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(roundsSectionLeft:)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[swipeLeft setNumberOfTouchesRequired:1];
[roundsSectionView addGestureRecognizer:swipeLeft];
// bunch-o-code snipped -- for the time being it's actually all commented out
// as a test and because the LLVM compiler was giving up after too many errors
// and I wanted to see if there was more it would like to tell me about this first --
// and very representative -- problem.
}
#end
The complaint from the compiler is "Use of undeclared identifier 'roundsSectionView'"
If I option-click on the use of roundsSectionView in that line of code where I'm adding the gesture recognizer to it the pop-up correctly describes it as declared in GSBViewController.h
So I'm stumped.
Is there something I can do in Xcode (4.3.2 at the time of this posting :-) to let me see what the included files are? Or is there something non-file-based that is needed to tie a category into the class it's augmenting? I don't remember anything like that being necessary before. In fact, the way I generated the files for this category was through Xcode's File -> New File... Objective-C Category template. Then I just copied the contents of the old ...+Swipe.h and ...+Swipe.m files and pasted them into their respective files in the new project.
A synthesized ivar is private. The compiler won't allow you to access it anywhere execept in the #implementation block where it's created. Neither categories nor subclasses can access the ivar directly; they must use the property: [self roundsSectionView].
There's a slight possibilty that earlier Clangs didn't make synthesized ivars private. Either that or you weren't really doing exactly the same thing in the earlier project.
What #Jacques Cousteau says is correct.
Since you just defined a property and no backing ivar, the category won't be able to access it. If you use self.roundsSectionView it will use the getter method generated for the property and hence it will work.
Or you could define a backing variable in your interface
#interface GSBViewController : UIViewController
{
UIBarButtonItem *roundsSectionView;
}
In this case the categories will be able to access the variable. But not any other class.

'TileMap' may not respond to '+mapNamed:'

Here's an odd one. I have a class named TileMap with the following interface:
#interface TileMap : NSObject
{
int *data;
int tilesWide;
int tilesHigh;
NSString *imageName;
}
+ (id)mapNamed:(NSString *)filename;
- (id)initWithFile:(NSString *)filename;
#end
The implementation looks like this:
#implementation TileMap
+ (id)mapNamed:(NSString *)filename
{
return [[self alloc] initWithFile:filename];
}
- (id)initWithFile:(NSString *)filename
{
if (self = [super init])
{
// ...
}
return self;
}
#end
But when I add a call to [TileMap mapNamed:#"map.plist"]; to my application Xcode warns:
'TileMap' may not respond to '+mapNamed:'
The application compiles fine and calls to NSLog within TileMap-initWithFile: are logged. I noticed that Xcode's syntax coloring was off for this class and method so I tried renaming both the class and the method separately. The only combination that eliminated the warning and syntax coloring issues was to rename both the class and the method.
Am I colliding with some undocumented framework class or method? Find in Documentation doesn't reveal anything. Find in Project only reveals the call, interface definition and the implementation. I'm stumped (not that it takes much). Is there a way around this without munging my existing naming conventions?
Did you #import the TileMap.h header? Did you save your TileMap.h header?
Turns out my project directory ended up with two TileMap.h and TileMap.m files—visible from the Finder but not in Xcode. One, a complete interface and implementation, in my root project directory. The other just a bare NSObject subclass in my framework subdirectory. Not sure how that happened. Deleting the latter resolved the problem. Thanks for the help just the same Dave.
Shaun,
besides the problem you asked about, you also have a memory leak in +mapNamed:. The following line returns a non-autoreleased object with a retain count of +1, which basically gives ownership to the caller:
return [[self alloc] initWithFile:filename];
According to the Memory Management Programming Guide for Cocoa, you should return autoreleased objects from convenience methods, such as this:
return [[[self alloc] initWithFile:filename] autorelease];
If you have Snow Leopard and Xcode 3.2, you might want to try running the static analyzer to find mistakes such as this one by pressing Cmd+Shift+A.