iPhone: Error when using -(id)initWithNibName - iphone

I am trying to use the following code, but I always get an error that I can find little information about:
- (id)initWithNibName:#"MyRidesListView" bundle:nil {
if ((self = [super initWithNibName:#"MyRidesListView" bundle:nil])) {
// Custom initialization
}
return self;
}
Error:
expected identifier before 'OBJC_STRING' token
This seems like a simple method to be calling. This is for a UINavigationController.
Ideas?

It looks like you are trying to implement a constructor method in a subclass of UIViewController or UINavigationController.
YOur syntax is a bit off. Without seeing what you are doing in a broader context I don't really know what is going on here, but this might help you out a bit. Its the closesest to your code while being syntactically correct.
- (id)initWithNibName:(NSString *)nibNameOrNull bundle:bundle {
if ((self = [super initWithNibName:nibNameOrNull bundle: bundle])) {
// Custom initialization
}
return self;
}
Then you can do this outside of your class:
[[MyRidesListView alloc] initWithNibNamed:#"MyRidesListView" bundle:nil];

Related

What is Best practice in objective c for creating an object and setting its properties

Considering I have a UIViewController called ErrorViewController that I am instantiating using initWithNibName.
There is a enum on this ErrorViewController describing its "type".
This ErrorViewController has one delegate function that returns to its delegate which will respond according to the type set on the ErrorViewController.
Is it better to pass all the parameters within a new initWithNibName function, and set private properties on the ErrorViewController. Like this:
ErrorViewController *errorVc = [[ErrorViewController alloc]
initWithNibName:#"ErrorViewController" bundle:nil
andErrorType:kErrorTypeA andDelegate:self];
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
andErrorType:(ErrorType)errorType andDelegate:(id<ErrorDelegate>)delegate{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = delegate;
self.errorType = errorType;
}
return self;
}
Or is it better to instantiate the object and afterward set its public properties like this:
ErrorViewController *errorVc = [[ErrorViewController alloc]
initWithNibName:#"ErrorViewController" bundle:nil];
errorVc.delegate = self;
errorVc.type = kErrorTypeA.
And regarding the delegate method, is it best practice to check the type by passing a parameter, or by checking the property of the passed back Controller as follows:
- (void)ErrorPage:(ErrorViewController *)ErrorPage
// check ErrorPage.errorType
}
or this: ?
- (void)ErrorPage:(ErrorViewController *)ErrorPage
andErrorType:(ErrorType)errorType
// check errorType
}
I think it's a question of preference. If the object can't function correctly without error-type and/or delegate, it's probably best to provide your custom initialiser.
As to your second question, I would provide the error type as in your second example. Note that the method name should start with a lowercase character though (-errorPage: instead of -ErrorPage:).
Additionally, if you use it a lot, I would provide a convenience class method to create the object:
+(ErrorViewController*) standardErrorViewControllerWithErrorType: (ErrorType) errorType andDelegate: (id<ErrorDelegate>) delegate {
ErrorViewController *evc = [[ErrorViewController alloc] initWithNibName: #"ErrorViewController" bundle: nil andErrorType: errorType andDelegate: delegate];
return evc;
}
Edit
Also, in your init method, it is encouraged to use -(instancetype) init... instead of -(id) init....

Reducing many similar methods to one parametrized method

I'm studying Objective-C.
A collegue left my company and I "inherited" her code. In a project, in a UIViewController class I found this code:
-(IBAction)goToSect1:(id)sender{
sect1=[[Sect1ViewController alloc]initWithNibName:#"SECT-1" bundle:nil];
[self presentModalViewController:sect1View animated:YES];
}
-(IBAction)goToSect2:(id)sender{
sect2=[[Sect2ViewController alloc]initWithNibName:#"SECT-2" bundle:nil];
[self presentModalViewController:sect2View animated:YES];
}
-(IBAction)goToSect3:(id)sender{
sect3=[[Sect3ViewController alloc]initWithNibName:#"SECT-3" bundle:nil];
[self presentModalViewController:sect3View animated:YES];
}
-(IBAction)goToSect4:(id)sender{
sect4=[[Sect4ViewController alloc]initWithNibName:#"SECT-4" bundle:nil];
[self presentModalViewController:sect4View animated:YES];
}
-(IBAction)goToSect5:(id)sender{
sect5=[[Sect5ViewController alloc]initWithNibName:#"SECT-5" bundle:nil];
[self presentModalViewController:sect5View animated:YES];
}
-(IBAction)goToSect6:(id)sender{
sect6=[[Sect6ViewController alloc]initWithNibName:#"SECT-6" bundle:nil];
[self presentModalViewController:sect6View animated:YES];
}
I consider this a little crazy, since we have six methods doing fundamentally the same thing. Is there a way to have only one parametrized method? How? Should I consider using the sender tag and a switch inside the body of my new method?
Thanks, any help is appreciated.
-(IBAction)goToThisViewControllerByUsingThisSection:(id)sender{
// Here I assume you are using this function with UIButton or other controls.
// If so, then you have to assign a tag for each section button or do as needed by your app.
// If you call this method in didselectrowatindexpath in delegate of UITableView then simply do use:
// indexPath.section instead of sender.tag
NSString *sectionWithViewController=[NSString stringWithFormat:#"Sect%iViewController",sender.tag];
Class classNam=NSClassFromString(sectionWithViewController);
UIViewController *unKnownViewController=[[classNam alloc]initWithNibName:[NSString stringWithFormat:#"SECT-%i",sender.tag] bundle:nil];
[self presentModalViewController:unKnownViewController animated:YES];
}
I wouldn't alter it at all. Those are IBActions, meaning that something in your interface is binded to them, more than likely buttons. So you have 6 buttons on your UI, each lead to a different view controller.
With the current way you have it, you can alter the individual functionality of each button without having to worry about affecting the others. Otherwise you wild be binding all those buttons to the same method, and then inside the method have to do a check for which button it is. At best, you will get rid of 5 lines of code and introduce 6 more. So the change will actually reduce readability and increase your number of lines of code.
In my opinion you should just leave it like that. The other option would be to have a single method like this:
-(IBAction)goToSection:(id)sender{
//Check the sender and then allocate depending on sender...
}
And simply bind the same method to all the buttons...
In my opinion that would look a lot worst.
There is not much you can do as mentioned by others.
Some suggestions:
If you use appropriate naming or change your init this line can be from
[[Sect1ViewController alloc] initWithNibName:#"SECT-1" bundle:nil];
to
[[Sect1ViewController alloc] init];
Either name the xib after the controller like Sect1ViewController or Sect1View or change your init for the class
// Sect1ViewController.m
- (id)init
{
self = [super initWithNibName:#"SECT-1" bundle:nil];
if (self) {
// ...
}
return self
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
return [self init];
}
Generally a viewController has a better idea what xib it should use than any calling code and this keep encapsulation.
May be a bad idea but included so you can see some things you can do
This structure of the method is repeating. I am not 100% you need to hold onto the viewController you are creating as the presenting controller will take a
retain on it.
With this in mind we can DRY this code up slightly like so
-(IBAction)goToSect1:(id)sender{
[self presentClass:[Sect1ViewController class]];
}
-(IBAction)goToSect2:(id)sender{
[self presentClass:[Sect2ViewController class]];
}
-(IBAction)goToSect3:(id)sender{
[self presentClass:[Sect3ViewController class]];
}
-(IBAction)goToSect4:(id)sender{
[self presentClass:[Sect4ViewController class]];
}
-(IBAction)goToSect5:(id)sender{
[self presentClass:[Sect5ViewController class]];
}
-(IBAction)goToSect6:(id)sender{
[self presentClass:[Sect6ViewController class]];
}
- (void)presentClass:(Class)class;
{
UIViewController *viewController = [[class alloc] init];
[self presentModalViewController:viewController animated:YES];
[viewController release]; viewController = nil;
}
}
This does not scale well as for every new button you need to add a new action even though they do the same thing - yuk
try that:
-(IBAction)goToSect:(id)sender{
if (sender /*do your check here*/)
{
sect=[[Sect1ViewController alloc]initWithNibName:#"SECT-1" bundle:nil];
}else if (sender /*another check*/){
sect=[[Sect2ViewController alloc]initWithNibName:#"SECT-2" bundle:nil];
} //other checks
[self presentModalViewController:sect1View animated:YES];
}
you will need all these if-checks because there are different ViewControllers. If they are pretty much the same I'd just leave 1 and pass/change some parameters.
hope it helps

Creating constructors in Objective-C

Why do we always do this when creating constructors in Objective C?
self = [super init];
if ( self ) {
//Initialization code here
}
you can create constructor and destructor in objective-c with
-(id) init
{
self = [super init];
if(self)
{
//do something
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
We reassign to self because [super init] is allowed to return a different object than the one it was called on. We if (self) because [super init] is allowed to return nil.
self is a class based on some superclass (e.g. UIViewController, NSObject - see your interface file to be sure which one). The superclass might need some form of initialization in order for the subclass to work as expected. So by first initializing the superclass we make sure default properties and the like are set. Without initializing the superclass first, we might experience some very unexpected behavior, especially in more complex objects like ViewControllers and the like.
Read this apple document on initialization
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html

Extending UIVIewController of my own I got error accessing self.view

I have created a View controller with some methods I need to use often in different aplications. It works like a charm if I use it directly but when I try to create another UIViewController that extend my class I cannot access self.view anymore. This is the init method of the original class:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil slideFrom:(HalfViewControllerType) from {
self.slideFrom = from;
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
CGRect viewScreen = [self.view bounds];
[self moveTo:startPoint inDuration:0.0];
}
return self;
}
At the point of retrieving [self.view bounds] I got an EXC_BAD_ACCESS.
Even hacking the values manually it then fail to all the other self.view like transform animation and so on.
The call to create the view is this, but it never got after the init method:
SelectViewController *sVC = [[SelectViewController alloc] initWithNibName:#"SelectViewController" bundle:nil slideFrom:top];
[sVC setDelegate:self];
[self.view addSubview:sVC.view];
[sVC slideIn];
[sVC release];
Any help on understanding what I am doing wrong would be really appreciated.
Francesco.
I couldn't understand what was happening, so I re-wrote everything and now works. I can just guess I was releasing something I shouldn't have.

Dynamically load nib for iPhone/iPad within view controller

I have converted an iPhone application using the wizard like thing in XCode into a universal app.
It builds fine but obviously looks a bit rubbish in some areas :)
I need to load nibs according to which device is being used. I dont wish to create my view controllers using initWithNib as I already have code to create the controllers with some data (initWithMyLovelyData) which doesnt do anything to do with nib loading.
I know to find out the device you use UI_USER_INTERFACE_IDIOM() so I tried overriding the initWithNibName within the actual view controllers themselves, assuming they get called internally somehow. But it's not working as I guess I am unsure of the syntax.
I have tried
if(ipad..) self = [super initWithNibName:#"MyIpadNib" bundle:nibBundleOrNil];
And that doesnt work :/
EDIT - I know I have massively edited this, made my question a bit more specific after doing some more research - apologies!
Actually, Apple does all this automatically, just name your NIB files:
MyViewController~iphone.xib // iPhone
MyViewController~ipad.xib // iPad
and load your view controller with the smallest amount of code:
[[MyViewController alloc] initWithNibName:nil bundle:nil]; // Apple will take care of everything
EDIT: #Adam's answer below is the correct answer.
To determine which nib to load, do the following, and scrap your initWithMyLovelyData method and use a property to set the data. You should be able to easily move all your init code into the property setter method.
MyViewController *viewController;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
viewController = [[MyViewController alloc] initWithNibName:#"ipadNIB" bundle:nil];
} else {
viewController = [[MyViewController alloc] initWithNibName:#"iphoneNIB" bundle:nil];
}
viewController.myLovelyData = someData;
I just put these two methods in a class called IPadHelper, and use the addIPadSuffixWhenOnIPad method to conditionally pick between two nibs depending on platform
+ (BOOL)isIPad{
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_2){
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad){
return YES;
}
}
return NO;
}
+ (NSString *)addIPadSuffixWhenOnIPad:(NSString *)resourceName{
if([IPadHelper isIPad]){
return [resourceName stringByAppendingString:#"-iPad"];
}
else {
return resourceName;
}
}
see here http://cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html for more explanation of the first method...
You can have your cake and eat it too. Just add a method that wraps initWithNibName:bundle: and adds your myLovelyData parameter:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil myLovelyData:(id)data
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// Custom initialization using myLovelyData
//
}
return self;
}
I think it will be better to create C file.
FileHelper.h
#import <Foundation/Foundation.h>
BOOL isIPad();
NSString *addIPadSuffixWhenOnIPad(NSString *resourceName);
FileHelper.m
#import "FileHelper.h"
BOOL isIPad() {
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_2) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
return YES;
}
}
return NO;
}
NSString *addIPadSuffixWhenOnIPad(NSString *resourceName) {
if(isIPad()) {
return [resourceName stringByAppendingString:#"-iPad"];
}
else {
return resourceName;
}
}