I have some details stored inside an NSDictionary. I'm using a Master-Detail view on iPad and after I added the initWithCoder method, my app crashes on start-up and I don't know how to make it work.
The reason I want to use NSCoder is to store my user's data and be able to show it once he starts the app again. NSUserDefaults isn't feasible because I'm storing UITextFields and UISegmentedControls inside the dictionary for ease of handling data.
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
personInfo = [aDecoder decodeObjectForKey:myString];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:personInfo forKey:myString];
}
Can anyone please tell me how to use it properly?
I fixed it by simply saying
self = [super initWithCoder:aDecoder]
instead of just
self = [super init]
Related
It seems that most init methods in Objective-C now tend to return instancetype instead of id. See [UIView initWithFrame:], [UIViewController initWithNibName:bundle:], [NSArray init] and siblings, etc. But initWithCoder uses id. Why is this? Has it just not been updated yet? Or is there a reason it has to be id?
It is not updated yet. You can still code it with instance type.
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
//...
}
return self;
}
Is there any way to know when a custom object is finished with being initialized from inside the object's file? Or let me rephrase the question, why can't I call any method inside this method?
- (id)initWithCoder:(NSCoder *)coder {
//NSLog(#"initWithCoder inside CustomObject (subclass of UIView)");
self = [super initWithCoder:coder];
if (self) {
//... initialization here
[self visibleEmptyButton]; //why does this method never get called?
}
return self;
}
EDIT:
-(void)viewDidLoad{
NSLog(#"viewDidLoad inside CustomObject(subclass of UIView) is called"); //It never gets called
[self viewDidLoad];
//initialization here...
}
(If the class you are init-ing is a subclass of UIViewController) Changing and setting things in the screen should be done after the view is loaded. Try doing it in this method:
- (void)viewDidLoad {
[super viewDidLoad];
[self visibleEmptyButton];
//Do the additional view altering here
}
If this method doesn't exist yet you can just add it to the .m file (no need to add it to the .h file).
In lieu of you're edit you could simply move the call to the UIViewController:
- (void)viewDidLoad {
[super viewDidLoad];
[TheInstanceOfYourViewClass visibleEmptyButton];
}
Also, to avoid making a whole bunch of small subview related methods public it often makes sense to create one method to handle the initial visual states.
edit : ok, what i've learned is you might choose between initWithNibName or initWithCoder, depending if you use a .xib or not. And "init" is just not the constructor method for UIVIewController.
This might seem to be a fairly simple question, but I'm not sure about the answer : I've read that this method "is only used for programatically creating view controllers", and in the doc : "It is loaded the first time the view controller’s view is accessed"
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
Ok so to understand it a bit more :
What would you write as "custom initialization" into this method?
When should you implement this method, like that in the code, if you can just write it after allocating your viewController (example : MyVC *myvc = [[MyVC alloc] initWithNibName:...bundle...];)
Thanks for your answer
Usually I do this:
// Initialize controller in the code with simple init
MyVC *myVC = [[MyVC alloc] init];
Then do this in my init method:
- (id)init {
self = [super initWithNibName:#"MyVC" bundle:nil];
if (!self) return nil;
// here I initialize instance variables like strings, arrays or dictionaries.
return self;
}
If controller needs some parameters from the initializee, then I write custom initWithFoo:(Foo *)foo method:
- (id)initWithFoo:(Foo *)foo {
self = [super initWithNibName:#"MyVC" bundle:nil];
if (!self) return nil;
_foo = [foo retain];
return self;
}
This allows simplification of initialization as well as extra initializers for your view controller if it can be initialized from different locations with different parameters. Then in initWithFoo: and initWithBar you'd simply call init which calls super and initializes instance variables with default values.
It's an init method, so you initialize everything you need to be initialized when you begin your work in your view controller. Every object ivar will be automatically initialized to nil, but you can initialize a NSMutableArray to work with or an BOOL that you want to be a certain value.
You implement this method every time you have something to initialize, as stated previously. You generally don't initialized things after allocating your view controller, that way you don't need it to do it every time you use your view controller (as you may use it at different place in your application). It's also the best practice.
I usually put in there configuration stuff that I know it wont change.
For example, the ViewController's title:
self.title = #"MyTitle";
Or if this is one of the main ViewController in a TabBar application. That is, it owns one of the tabs, then I configure its TabBarItemlike this:
UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:#"something"
image:[UIImage imageNamed:#"something.png"]
tag:0];
self.tabBarItem = item;
There is all sorts of things you can do there.
does anyone know why when i add...
-(id) initWithCoder: (NSCoder *) decoder {
OutputDataMutableArray = [[decoder decodeObjectForKey:#"RootViewControllerPostArray"] retain];
return self;
}
to my RootViewController it will not push a new view from a table cell using the didSelectRowAtIndexPath method. This is the line it seems to hang on...
[[self navigationController] pushViewController:postController animated:YES];
i get no error messages, it just seems to skip it. Unless i take the initWithCoder out and it works fine.
any insight would be appriciated.
Chris
You’re not calling the superclass’s implementation of -initWithCoder:.
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
OutputDataMutableArray = [[decoder decodeObjectForKey:#"RootViewControllerPostArray"] retain];
}
return self;
}
Check the log and look for exceptions being thrown causing your code to terminate prematurely. Or set a break on NSException raise.
BTW, you need to be calling [super init] in initWithCoder:
I've created an iPad application with a UITableViewController in the UISplitViewController (and everything works :) Since I'd like the table to use UITableViewStyleGrouped, i added:
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self != nil) {
// Initialisation code
}
return self;
}
to the root view controller & included it in the .h, but it's never getting called. So, two questions, can I set UITableViewStyleGrouped for a table in a UISplitViewController? And, if so, how?
init won't be called, initWithStyle: or initWithFrame:style: might be called.
edit: waychick was right. - (id) initWithCoder:(NSCoder *) coder is called.
Same problem. In my case
- (id) initWithCoder:(NSCoder *) coder
is called