Recently I am doing a project on iOS, I have created a class, namely YellowTileView, I would like to do something like when I clicked on button, a new tile will be shown
-(IBAction)ShowImage:(id)sender
{
YellowTileView *yt=[[YellowTileView alloc] initWithFrame:CGRectMake(0, 0, 60, 80)];
[self.view addSubview:yt];
}
This work fine for me. But the next step is to take a integer number that randomized by another method.
My question is can I redefine/create the method initWithFrame by myself? If yes, how can I do this and would it be any problems as I have some drawing code in the class YellowTileView?
First of all you should know that you can reimplement all methods of parent. So you can reimplement initialization method of UIView defined like this:
- (id)initWithFrame:(CGRect)frame;
But you can also create your own initialization method with your own parameters list.
In .h file:
- (id)initWithFrame:(CGRect)frame andWithRandomInt:(int)random;
And in .m file:
- (id)initWithFrame:(CGRect)frame andWithRandomInt:(int)random {
self = [super initWithFrame:frame];
if (self) {
_random = random;
}
return self;
}
You absolutely can - it should look something like this :
- (id)initWithFrame:(CGRect)frame andNumber:(int)number {
self = [super initWithFrame:frame];
if (self) {
myNumber = number;
}
return self;
}
As long as you call a super init... method somewhere in your constructor you'll be fine.
Also, it's common practice to name your methods like this:
-(IBAction)showImage:(id)sender
So, methods names start with a lowercase letter. Note that you don't have to - it's just that all the other developers do that.
Related
While referring a sample code i found this snippet can any explain why it is used.
- (id)init
{
self = [super init];
if (self) {
[[self view]setBackgroundColor:[UIColor redColor]];
}
return self;
}
and what is the difference between the following snippet.
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
}
init and viewDidLoad both are completely different.
viewDidLoad called, when the view is loaded into memory, this method called once during the life of the view controller object. It's a great place to do any view initialization.
init method is an initializer method. Cocoa has various types of intializer. To learn more, please check the link,
https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/Initialization/Initialization.html
I made a test app to understand how exactly init methods work. In my simple UIViewController I call the following:
- (id)init {
self = [super init];
self.propertyArray = [NSArray new];
NSLog(#"init called");
return self;
}
The above does not print any values in NSLog. However, when I write :
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
NSLog(#"init called");
self.propertyArray = [NSArray new];
return self;
}
It does print "init called" in console. So my question is: why is the init method called and the other is not? Which one do I have to use, when i want to do my stuff before the view loads (and any other methods called)?
Any explanation will be appreciated, thanks.
To begin with, you mention ViewController in your question. A UIViewController's designated initializer is initWithNibName:bundle:
You would never want to override just init on a UIViewController.
There is a lifecycle for each object:
When initializing in code, you have the designated initializer. Which you can find in the documentation for that class. For NSObject derived classes this would be init:
- (id)init
{
self = [super init];
if (self) {
// perform initialization code here
}
return self;
}
All objects that are deserialized using NSKeyUnrchiving, which is what happens in the case of Storyboard's or NIBs(XIBs), get decoded. This process uses the initWithCoder initializer and happens during the unarchiving process:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// perform initialization code here
}
return self;
}
It is common, because of this lifecycle, to create a shared initializer that gets called from each initializer:
- (void)sharedInit
{
// do init stuff here
}
- (id)init
{
self = [super init];
if (self) {
[self sharedInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self sharedInit];
}
return self;
}
To take it one step further. In the case of Storyboard's and XIBs, if you want to perform initialization or updates AFTER the unarchiving is completed and to guarantee all your outlets and actions are connected, you would use awakeFromNib:
- (void)awakeFromNib
{
// do init or other stuff to be done after class is loaded from Interface Builder
}
When a class is instantiated in your code, you pick which initializer to call, depending on your needs. When a class is instantiated through framework code, you need to consult the documentation to find out what initializer would be called.
The reason that you see the behavior that you describe is that your view controller is in a storyboard. According to Cocoa documentation, when a view controller is instantiated through a storyboard, its initWithCoder: initializer is called. In general, this call is performed when an object gets deserialized.
Note that it is common to check the result of self = [super initWithCoder:aDecoder]; assignment, and skip further initialization when self is set to nil.
When you load view controller from nib file (and storyboard) it uses initWithCoder: so in your example this is why it call this method.
If you create your view controller programatically this method won't work and you should override initWithFrame: initialiser instead and also you should create view controller by calling
[[UIViewController alloc] initWithFrame:...];
The different inits are different constructors. As in any other language, an instance is instantiated by the most appropriate constructor. That's initWithCoder: when restoring from an archive.
As a style point, note that use of self.propertyArray in a constructor is considered bad form. Consider what would happen if a subclass overrode setPropertyArray:. You'd be making a method call to an incompletely instantiated object. Instead you should access the instance variable directly, and perform the idiomatic if(self) check to ensure it is safe to do so.
I have a ViewController (VCViewController) with this code in it:
touchView = [[VCTouchView alloc] initWithFrame:(CGRect){{0, 0}, 320, 480} andTheCameraController:self];
in the UIView (VCTouchView) though I have:
(id)initWithFrame:(CGRect)frame andTheCameraController:(VCViewController*)cameraController
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
Basically I have a ViewController which I want to add an overlay. On this overlay will be a button for the user to press that will take a photo. Quite simple requirement, yet I cannot understand why it can't see the parameter called andTheCameraController?
Any help greatly appreciated.
Mike
Have you added the new method to your VCTouchView.h file?
-(id)initWithFrame:(CGRect)frame andTheCameraController:(VCViewController *)cameraController;
I've been programming in Objective-C for years now, and I always bump into this problem: If you have an object with multiple initializers, but there is code in common to all of them, how do you extract it out?
The best I've been able to come up with is something like this:
#implementation Example
- (void)privateInitWithString:(NSString*)aString
{
self.str = aString;
}
- (id)init
{
self = [super initWithWindowNibName:#"MyNib"]
if(self) {
[self privateInitWithString:#""];
}
return self;
}
- (id)initWithString:(NSString*)aString
{
self = [super initWithWindowNibName:#"MyNib"]
if(self) {
[self privateInitWithString:aString];
}
return self;
}
#end
There is a lot of duplication in the individual initializers which a code smell. However I can't think of a way to get one initializer to "fall through" to another one because there is no guarantee that self has been set before calling [super init]
Is there a best practice for this that I'm missing?
You write one "designated initialiser". That initialiser handles all the different situations. In your case, initWithString seems a good candidate. And init just becomes
- (instancetype)init { return [self initWithString:#""]; }
You can read more about it here:
https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/Initialization/Initialization.html
I'm developing an iOS app with latest SDK.
I have created a class that inherits from UIView and I have to do some initialization every time the class is instantiated.
I have to call a method called setUpVars: but I don't know where to send a message to that method:
- (id)initWithFrame:(CGRect)frame;
- (id)initWithCoder:(NSCoder*)aDecoder;
This class can be used with a custom xib, or added to a Storyboard, so I need to be sure that that method will be called on every case.
- (void)setUpVars
{
_preferenceKey = #"";
_preferenceStatus = NO;
_isDown = NO;
}
Where do I have to add [self setUpVars];?
Essentially you will be wanting to cover both cases
- (id)initWithFrame:(CGRect)frame;
{
self = [super initWithFrame:frame];
if (self) {
[self setUpVars];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder;
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setUpVars];
}
return self;
}
I think that you need to send this message from each method, also do not forget about awakeFromNib method.
You can create BOOL variable, something like isAlreadySetup and set it to YES in setUpVars method.
Docs Says
awakeFromNib
Prepares the receiver for service after it has been loaded from an
Interface Builder archive, or nib file.
- (void)awakeFromNib
{
[self setUpVars];
}
If you use Interface Builder to design your interface, initWithFrame: is not called when your view objects are subsequently loaded from the nib file. Instead initWithCoder gets called. So you can initialize your variables in both methods if you prefer a generic way. Works in both case
I tend to think you should call this method from the -(void)viewDidLoad method of the controller in charge