If I have a class that is setup like this to customize a UIView.
#interface myView : UIView
- (id)init {
self = [super init];
if(self){
[self foo];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self foo];
}
return self;
}
-(void) foo{
//Build UIView here
}
How come foo is called twice whether I use
myView *aView = [[myView alloc]init]];
or
myView *aView = [[myView alloc]initWithFram:aFrame];
UIView init calls UIView initWithFrame:. Since you override both, calling your init method results in your initWithFrame: method being called:
In other words: your init calls UIView init. UIView init calls initWithFrame:. Since you override initWithFrame:, your initWithFrame: is called which in turn calls UIView initWithFrame:.
The solution, since your initWithFrame: will always be called, is to remove the call to foo from your init method.
Related
I presumed this would be simple but it is not working.
I am trying to pass an NSArray to my UIView that is getting imported with a NIB.I am importing it as:
DraggableViewBackground *draggableBackground = [[DraggableViewBackground alloc]initWithFrame:frame];
draggableBackground.exampleCardLabels = #[#"Mercedes-Benz", #"BMW", #"Porsche",
#"Opel", #"Volkswagen", #"Audi"];
[self.ripContainer addSubview:draggableBackground];
On my DraggableViewBackground UIView
.h
#property (retain,nonatomic)NSArray* exampleCardLabels;
.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[super layoutSubviews];
NSLog(#"Dish RIP %#", exampleCardLabels);
}
return self;
}
I am currently getting a null value. I am aware this is 101 basics when passing data but I don't understand why this isn't working.
It's just timing.
At this stage the array has not been set:
DraggableViewBackground *draggableBackground = [[DraggableViewBackground alloc]initWithFrame:frame];
and yet you are logging the array in the initWithFrame method. Log it later on in the lifecycle.
Also don't call [super layoutSubviews]; in the initWithFrame method.
Make your init like this:
- (id)initWithFrame:(CGRect)frame andCards:(NSArray *)cards{
self = [super initWithFrame:frame];
if (self){
self.xampleCardLabels = cards;
}
return self;
}
and call it
DraggableViewBackground *draggableBackground = [[DraggableViewBackground alloc]
initWithFrame: frame
andCards: #[#"Mercedes-Benz", #"BMW", #"Porsche",#"Opel", #"Volkswagen", #"Audi"]];
How is this possible?
PlayingCardView *view = [[PlayingCardView alloc] initWithFrame:currentFrame];
if ([view isKindOfClass:[PlayingCardView class]])
NSLog(#"checked!");
The if below doesn't work. However, it says that my view is of UIView class, not the PlayingCardView, which is inherited from the UIView. The problem is that because of it I can't send PlayingCardView's messages to my view.
update:
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
[self setup];
return self;
}
It is possible if you override initWithFrame for PlayingCardView the wrong way. You might be doing something like self = [[UIView alloc] initWithFrame:] instead of self = [super initWithFrame:]. Check any init methods that you have overridden for PlayingCardView.
I can't intercept the init function that's getting called when it's getting created inside of the xib file.
I want to add borderline to it when it gets created so that I won't need to add it manually.
.h:
#import <UIKit/UIKit.h>
#interface UITextView (FITAddBorderline)
- (id) init;
- (id) initWithFrame:(CGRect)frame;
#end
.m:
#import "UITextView+FITAddBorderline.h"
#implementation UITextView (FITAddBorderline)
- (id) init
{
self = [super init];
if (self) {
[self addBorderline];
}
return self;
}
- (id) initWithFrame:(CGRect)frame {
self = [super init];
if (self) {
self.frame = frame;
[self addBorderline];
}
return self;
}
- (void) addBorderline {
//To make the border look very close to a UITextField
[self.layer setBorderColor:[[[UIColor grayColor] colorWithAlphaComponent:0.5] CGColor]];
[self.layer setBorderWidth:2.0];
//The rounded corner part, where you specify your view's corner radius:
self.layer.cornerRadius = 5;
self.clipsToBounds = YES;
}
#end
Views that come from NIBs are initialized with initWithCoder:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self addBorderline];
}
return self;
}
As a side note, I would recommend changing what you are doing and use a subclass instead of a category. You can get yourself into some trouble overriding methods in a category. See more info here.
You just need to implement the awakeFromNib method:
-(void)awakeFromNib
{
[super awakeFromNib];
[self addBorderline];
}
I'm trying to do something very basic. I want to add a subView to my UIView subclass. I assume that I would put this in initWithFrame method as below, but view that are instances of this class do not draw this subview. What am I doing wrong?
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
redView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 20, 20)];
[redView setBackgroundColor:[UIColor redColor]];
[self addSubview:redView];
}
return self;
}
BTW redView is a property defined in the header of the sub class, like:
#property (strong, nonatomic) UIView *redView;
Thanks for reading!
You should place your initializing code inside:
- (id)initWithCoder:(NSCoder *)aDecoder { ... }
or
- (void)awakeFromNib { ... }
These methods are called when a view is loaded from nib. Don't forget to call [super ...] in the above methods.
Let's say I've created a subclass of UIView and I'm loading it with a nib file.
I do this:
MySubView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MySubView" owner:self options:nil];
[self release];
self = [[nib objectAtIndex:0] retain];
self.tag = 1;
[self fire];
}
return self;
}
- (void)fire {
NSLog(#"Fired MySubView");
}
Now I want to create some variations, but I don't want to copy the nib file, so I try to subclass MySubView like this, changing the background color:
RedMySubView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor redColor];
[self fire];
}
return self;
}
- (void)fire {
NSLog(#"Fired RedMySubView");
}
The view is created, the background color is changed, but the fire action is not overridden by the subclass. If I call the fire method, the result is Fired MySubView in the console.
How can I resolve this?
I want to keep the nib layout, but give it a new class.
I would say that with [self release] in the MySubview initializer initWithFrame you are throwing away the class you want to create with the initializer. The class is loaded by the loadNibName method and therefore has the same class as defined in the nib.
So it is useless to call the initializer in the subclass.
Try to implement your own nib constructor in MySubview (e.g. initWithNibFile):
- (id) initWithNibFile:(NSString *) nibName withFrame:(CGRect) frame
etc. and call this constructor in RedMySubview
- (id) initWithNibFile:(NSString *) nibName withFrame:(CGRect) frame {
self = [super initWithNibFile:mynib withFrame:MyCGRect];
if (self)
....
If you now look up that your nib file really has RedMySubview as class, fire should be
overridable. If you use both MySubview and RedMySubview, you must duplicate the xib.
Or you create an abstract class (a stub) which implements only the initWithNibFile initializer and the UIViews you want to create are subclasses of it:
MyAbstractNibUIView initWithNibFile:withFrame:
MyRedSubview : MyAbstractNibUIView red.xib
MyGreenSubview :MyAbstractNibUIView green.xib
MyBlueSubview : MyAbstractNibUIView blue.xib
When you call self = [[nib objectAtIndex:0] retain] you basically override your "self" object to become a MySubView, since a MySubView is the base object in the nib file. This is undesired because if the calling class is a RedMySubView, then it will be overridden into a MySubView.
Instead you want to change your - (id)initWithFrame:(CGRect)frame in MySubView into this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MySubview" owner:self options:nil];
// The base of the nib file. Don't set self to this, instead copy all of its
// subviews, and "self" will always be the class you intend it to be.
UIView *baseView = [nib objectAtIndex:0];
// Add all the subviews of the base file to your "MySubview". This
// will make it so that any subclass of MySubview will keep its properties.
for (UIView *v in [baseView subviews])
[self addSubview:v];
self.tag = 1;
[self fire];
}
return self;
}
Now, everything should work in the initializer of "MyRedSubView", except that fire will fire twice, since you call it both in MySubView and RedMySubView.