iphone / ObjC - Method to remove/add views based on arguments - possible? - iphone

I'm new - i'm sorry - but I'm experimenting with multiview iphone apps, and wondered whether the below idea was a) possible and b) sensible.
I want to create a method that can remove and add views based on some parameters - the outgoing view, the incoming view and the incoming class.
- (void)switchViews:(Class)inView:(Class)outView:(Class)inClass{
inClass *tempView = [[inClass alloc]
initWithNibName:#"inView" bundle:nil];
tempView.burgerViewController = self;
self.inView = tempView;
[tempView release];
[outView.view removeFromSuperview];
[self.view insertSubview:tempView.view atIndex:0];
}
This would be called by:
[burgerViewController switchViews:viewMainMenu:viewOptions:ViewMainMenu];
Any help is much appreciated - I have a lot to learn.
Mike.

Your code is wrong, in that (it appears that) you've misunderstood how method names work in Objective-C.
For example, as your method currently stands, it is named:
switchViews:::
That's probably not what you're looking for.
A better name might be:
replaceView:forProperty:withViewOfClass:
Declared, that would look like:
- (void) replaceView:(UIView *)outView forProperty:(NSString *)propertyName withViewOfClass:(Class)inClass;
And you would use it like this:
Class viewOptions = ...;
NSString *viewMainMenu = #"...";
[burgerViewController replaceView:viewMainMenu forProperty:viewMainMenu withViewOfClass:viewOptions];
For more on Objective-C method names and interleaved arguments, check out the Objective-C Programming Language Reference.

Well, your first problem is that you release tempView and then attempt to insert it into the view. Don't release tempView at all, just keep it as-is for insertion into the main view.

Related

Is it generally bad practice to have many "initWith" parameters?

Say for instance I have an implementation of a UIView. The UIView contains a two labels, an image and a frame.
My "init" method ends up looking like:
- (id)initWithFrameAndLabelArrayAndImage:(CGRect)frame:(NSArray *)labelArray:(UIImage *)image;
Is that considered bad practice? Is it better to have a simple "initWithFrame" method and have the other label and picture as #properties?
It's fine. Apple does it frequently. For example, look at NSString:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsstring_Class/Reference/NSString.html
– initWithBytes:length:encoding:
– initWithBytesNoCopy:length:encoding:freeWhenDone:
– initWithCharacters:length:
– initWithCharactersNoCopy:length:freeWhenDone:
– initWithString:
– initWithCString:encoding:
– initWithUTF8String:
– initWithFormat:
– initWithFormat:arguments:
– initWithFormat:locale:
– initWithFormat:locale:arguments:
– initWithData:encoding:
But, following those patterns, yours:
- (id)initWithFrameAndLabelArrayAndImage:(CGRect)frame:(NSArray *)labelArray:(UIImage*)image;
Should probably be:
- (id)initWithFrame:(CGRect)frame labels:(NSArray *)labelArray image:(UIImage *)image;
Now, having said that, I probably wouldn't pass an array of labels. I would pass the data and have the custom view take that data and create/layout the subviews. You're sort of exposing the internal views that compose your custom view in the public methods and you may want to change how you render and compose them in the future.
Another approach would be to use delegates to render the labels the labels would be rendered by calling the delegate for the data it needs - similar to a table view.
Although having multiple paramaters is fine, you really shouldn't have any parameters that are unnamed. In your case, to call your method it would look like this:
[[* alloc] initWithFrameAndLabelArrayAndImage:frame :array :image];
This is generally bad practice. I would rearrange your custom initializer to be more along the following lines:
- (id)initWithFrame:(CGRect)frame labelArray:(NSArray *)labelArray image:(UIImage *)image;
or even
- (id)initWithFrame:(CGRect)frame andLabels:(NSArray *)labels andImage:(UIImage *)image;
I think this is basically a matter of preference, but I personally like to create "convenience methods" whenever I find my parameter list running amok (ie, shorter-named messages that call the longer ones using default values). For instance...
-(id)initWithFrame:(CGRect) frame {
[self initWithFrame:frame andLabel:#"Default text"];
}
-(id)initWithFrame:(CGRect) frame andLabel: (NSString *) str {
...
}
...
-(id)initWithFrame:(CGRect) frame andLabel: (NSString *) str ... andMothersMaidenName:(id) etc { ... }
I do question why you would use "initWithFrameAndLabelArrayAndImage:" as your first parameter, though, rather than just initWithFrame: andLabel: andArray: andImage:. Adding all the parameters to the first parameter name (and then repeating them in the subsequent ones) just seems redundant to me.

iPhone MultiView App Issue [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
iPhone App Dev - Loading View From View Controller
I have a root view controller with a tool bar which has a button. When the root view controller loads I want it to load a subview underneath the toolbar:
//assigns JSON to question objects
-(void) setQuestions {
questionArray = [[NSMutableArray alloc] init];
for (NSDictionary *q in self.questions) {
/* Create our Question object and populate it */
QuestionViewController *question = [[QuestionViewController alloc]init];
[question setQuestionId:[q objectForKey:#"questionId"] withTitle:[q objectForKey:#"question"] number:[q objectForKey:#"questionNumber"] section:[q objectForKey:#"sectionId"]];
/* Add it to our question (mutable) array */
[questionArray addObject:question];
[question release];
}
}
-(void) startQuestionnaire {
currQ = [questionArray objectAtIndex:0];
[self.view insertSubview:currQ.view atIndex:0];
[currQ release];
}
I use the startQuestionnaire to load the viewcontroller from questionArray which contains a load of QuestionViewController objects...When I click on the slider in the view that is loaded the program crashes...Do I have have to hand control over to the subview or something?The program doesnt crash when I emove the code within startQuestionnaire
While it's tough to say without seeing the rest of your code, it looks like your problem is somewhere in the way you're instantiating / referencing objects.
If I were you, I'd approach this slightly differently in the interest of simplifying things.
First off, try to use #property and #synthesize, and get used to referencing ivars with 'self'. Retain counts & memory management issues are often the cause of crashes, and you can clean things up quite a bit by being more formal with your ivars. Here's some reading: http://www.optictheory.com/iphone-dev/2010/02/objective-c-primer-part-3-property-and-synthesize/
Second, I would refrain from creating your QuestionViewController until you're ready to add it to your root ViewController (ie. inside startQuestionnaire). You can pass your JSON data into it with a custom init function. This way, you can ensure that there aren't other QuestionViewController instances lying around that might be causing complications, and taking up memory when they're not being used.
As for the slider issue itself, it's tough to say without seeing the actual error, but hopefully this helps a bit.
Best of luck

Inserting subView - iPhone

- (void)viewDidLoad {
BlueViewController *blueController = [[BlueViewController alloc] initWithNibName#"BlueView" bundle:nil];
self.blueViewController = blueController; //blueViewController set to var above
[self.view insertSubview:blueController.view atIndex:0];
[blueController release];
[super viewDidLoad];
}
not understanding this code very well. How come i am inserting the subview blueController and not self.blueViewController
also what difference does it make if I don't use self. Not even sure why self is used. I interpret it as I am setting the blueViewController property of the current View Controller to the blueController instance but why would I do that. The book I am reading from does not explain such things in detail. It is pretty much monkey do this.
not understanding this code very well. How come i am inserting the subview blueController and not self.blueViewController
since you have executed the assignment:
self.blueViewController = blueController;
those two variables are the same, so
[self.view insertSubview:self.blueController.view atIndex:0];
would be just the same as the code you posted.
also what difference does it make if I don't use self. Not even sure why self is used. I interpret it as I am setting the blueViewController property of the current View Controller to the blueController instance but why would I do that. The book I am reading from does not explain such things in detail. It is pretty much monkey do this.
if you don't assign to self.blueController, then your variable is just a simple variable local to that function. By having a property self.blueController and storing there a value, you can use that value in all of the selectors (functions) of your class.
check the code and you will see that self.blueController is used also in other functions. e.g., at some point you might decide you like making that subview hidden, or you want to remove it, etc. All of this you can do only if you have a pointer to the controller accessible to your class functions.
self is used if you are referring to an object of the class.
While initializing a variable we must use self. This will increment blueViewController retainCount to 1.
self.blueViewController = blueController;
While inserting also you can use both. Results will be same.
[self.view insertSubview:blueController.view atIndex:0];
[self.view insertSubview:self.blueController.view atIndex:0];
blueController is an alloced and initialized object while blueViewController is just a pointer to the BlueViewController class.By writing
self.blueViewController = blueController
you retain the blueController object.If you do not use self you won't be ratining the object and after you release it at line
[blueController release];
your program will crash as soon as you refer to it again.

How do I call - (void) viewDidAppear:(BOOL)animated from another method?

It is possible to call one method from inside another. I've implemented this function
- (void)pickAndDecodeFromSource:(UIImagePickerControllerSourceType) sourceType
I want to call following method inside the above one.
- (void) viewDidAppear:(BOOL)animated
I think I understand what you're asking... the question is.. well not there. Nonetheless:
What I think you're asking: "How do I call viewDidAppear from within another method...?"
- (void)pickAndDecodeFromSource:(UIImagePickerControllerSourceType)sourceType
{
...
[myController viewDidAppear:YES]; //Simply call it on whatever instance of a controller you have
...
}
If the question was actually "How do I override viewDidAppear?" then this is it:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//YOUR STUFF
//GOES HERE
}
You can always call the delegate methods directly:
[self viewDidAppear:YES]
Called from inside your method should work.
I'm not sure exactly what you mean, but from the nature of your question I am guessing you are new to Obj-C so I strongly suggest reading Introduction to The Objective-C Programming Language if you have not already. If you have, great! What you are looking for is most likely under Objects Classes and Messaging - Object Messaging - Message Syntax

Is it better to autorelease or release right after?

There are a lot of cases in which one would alloc an instance, and release it right after it's being assigned to something else, which retains it internally.
For example,
UIView *view = [[UIView alloc] initWithFrame...];
[self addSubView:view];
[view release];
I have heard people suggesting that we go with autorelease rather than release right after.
So the above becomes:
UIView *view = [[[UIView alloc] initWithFrame...] autorelease];
[self addSubView:view];
What's the best practice here? Pros and cons?
In most cases, it wont really matter either way. Since -autorelease simply means that the object will be released at the end of the current iteration of the run loop, the object will get released either way.
The biggest benefit of using -autorelease is that you don't have to worry about the lifetime of the object in the context of your method. So, if you decide later that you want to do something with an object several lines after it was last used, you don't need to worry about moving your call to -release.
The main instance when using -release will make a noticeable difference vs. using -autorelease is if you're creating a lot of temporary objects in your method. For example, consider the following method:
- (void)someMethod {
NSUInteger i = 0;
while (i < 100000) {
id tempObject = [[[SomeClass alloc] init] autorelease];
// Do something with tempObject
i++;
}
}
By the time this method ends, you've got 100,000 objects sitting in the autorelease pool waiting to be released. Depending on the class of tempObject, this may or may not be a major problem on the desktop, but it most certainly would be on the memory-constrained iPhone. Thus, you should really use -release over -autorelease if you're allocating many temporary objects. But, for many/most uses, you wont see any major differences between the two.
I agree with Matt Ball. Let me just add that, if you find yourself using this pattern frequently, it can be handy to write a quick category:
#interface UIView (MyCategories)
- (UIView *)addNewSubviewOfType:(Class)viewType inFrame:(NSRect)frame;
#end
#implementation UIView (MyCategories)
- (UIView *)addNewSubviewOfType:(Class)viewType inFrame:(NSRect)frame
{
UIView * newView = [[viewType alloc] initWithFrame:frame];
[self addSubView:newView];
return [newView autorelease];
}
#end
Which can be used as follows:
UIView * view = [someView addNewSubviewOfType:[UIView class]
inFrame:someFrame];
And it even works with other types, as long as they are derived from UIView:
UIButton * button = [mainView addNewSubviewOfType:[UIButton class]
inFrame:buttonFrame];
I usually go for -release rather than -autorelease whenever possible. This comes from years of experience debugging and enhancing other people's Objective-C code. Code that uses autorelease everywhere makes it harder to debug when an object gets over-released, since the extra release happens far away from the incorrect code.
It's also the case that many folks use autorelease when they just don't understand how cocoa memory management works. Learn the rules, learn the API, and you'll almost never need to autorelease an object.
A last minor point is that if you don't need the autorelease behavior, then using autorelease just needlessly adds extra work for your program to do.