I am subclassing an UIButton which is going to be present in all of my app's ViewControllers, kinda Navigation Button. I would like just to put it to my VC and apply custom class, without any code in ViewController itself. So, the questions:
1. is it possible?
2. I am using this code now in my UIButton custom class. What is wrong?:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addTarget:self action:#selector(didTouchButton) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)didTouchButton {
NSLog(#"YEAH, it works, baby!");
}
UPD: seems that even initWithFrame method is not being called at all.
Loading from the nib I think.The initWithFrame method doesn't work if not called programatically.
Try -awakeFromNib Method
See this question
Related
I came across the exception 'NSInternalInconsistencyException' when I write the code below in a viewcontroller which is associated with storyboard:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.view.backgroundColor = [UIColor blackColor];
}
return self;
}
However, if I delete the line 'self.view.backgroundColor = [UIColor blackColor];', there is no problem. I don`t know the reason.
Actually, in the stroyboard I have several controllers, and I want to make a base controller for all the controllers. The purpose is that I want to set a background view(UIView) for them.
I try to add a UIView in the viewDidLoad method of the basecontroller, but the view covers any components that set in the storyboard. I think the reason is that the components on the sub viewcontrollers are initialized in the 'initWithCoder' method before the 'ViewDidLoad' method is called. So I try to add the bgView in the 'initWithCoder' method of the basecontroller, and this led to the problem above.
So I also want to know a right way to achieve my purpose. Thanks!
dont set graphics related properties in init and bring subview to front. Set them from
-(void)awakeFromNib
{
[self.view.superview bringSubviewToFront:self.view];
self.view.backgroundColor = [UIColor blackColor];
}
I'm trying to subclass a UIToolbar but the code doesn't seem to be working when I add the class to a UIToolbar in Interface Builder. What am I doing wrong here? I'm also looking for adding UIBarbuttonitems etc., this is just a test eg., daniel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Customization//
UIImage *ToolbarBackgroundImage;
ToolbarBackgroundImage = [UIImage imageNamed:#"DefaultNavBar"];
[self setBackgroundImage:ToolbarBackgroundImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
[self setShadowImage:[[UIImage alloc] init] forToolbarPosition:UIToolbarPositionAny];
}
return self;
}
-(void)drawRect:(CGRect)rect {
//do nothing//
}
If you are using a storyboard, you should also implement initWithCoder:.
When a storyboard is being loaded, a decoder is used to create objects defined in the storyboard. initWithCoder: comes from the NSCoding protocol, and is a way for classes to deserialize themselves from coders. You can read about it in detail here.
If you wish to support both nibs and storyboards, there is a method available you can implement in your views, awakeFromNib.
In my application I've subclassed UIView and it has three ivars declared in .m file (2UIButton and an UITextField ). I use this object by drag the UIView from the objects library and convert them to my subclass. below is my implementation
#interface Prescriber()<UITextFieldDelegate>
{
UIButton *_addButton;
UIButton *_lessButton;
UITextField *_valueField;
}
#end
#implementation Prescriber
#synthesize value=_value,intValueofStrig=_intValueofStrig;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
NSLog(#"prescriber called");
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
-(void)layoutSubviews
{
if (!_addButton) {
_addButton=[[UIButton alloc]init];
[_addButton addTarget:self action:#selector(addbuttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[_addButton setBackgroundImage:[UIImage imageNamed:#"add1.png"] forState:UIControlStateNormal];
_addButton.layer.cornerRadius=5;
}
if (!_lessButton) {
_lessButton=[[UIButton alloc]init];
[_lessButton addTarget:self action:#selector(lessButoonClicked:) forControlEvents:UIControlEventTouchUpInside];
[_lessButton setBackgroundImage:[UIImage imageNamed:#"Minus.png"] forState:UIControlStateNormal];
}
if (!_valueField) {
_valueField=[[UITextField alloc]init];
_valueField.delegate=self;
}
///positioning the addbutton on left corner and right corner and position and
textfield in the center
}
I've done the configuring code in the -(void)layoutSubviews method as my -initwithFrame not get called as they are already in the xib file.
what I want to know is ,Is it right to do our initialization in the -layoutSubViews as there is a chance for this method to get called another time. Or am I able to invoke the
-viewDidLoad like delgate method for my subclassed View too?
Edit:- It is possible by creating an IBOutlet but that way is a constraint for me.
Do it in a private setup method:
#interface MyViewClass ()
- (void)_setup;
#end
#implementation MyViewClass
- (void)_setup
{
_addButton=[[UIButton alloc]init];
[_addButton addTarget:self action:#selector(addbuttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[_addButton setBackgroundImage:[UIImage imageNamed:#"add1.png"] forState:UIControlStateNormal];
_addButton.layer.cornerRadius=5;
_lessButton=[[UIButton alloc]init];
[_lessButton addTarget:self action:#selector(lessButoonClicked:) forControlEvents:UIControlEventTouchUpInside];
[_lessButton setBackgroundImage:[UIImage imageNamed:#"Minus.png"] forState:UIControlStateNormal];
_valueField=[[UITextField alloc]init];
_valueField.delegate=self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
[self _setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self != nil)
{
[self _setup];
}
return self;
}
#end
For IB instances, you can setup you view and initialize its subviews in -awakeFromNib:
- (void)awakeFromNib
{
[super awakeFromNib];
// your initialization code here
}
Using this method, your instance is fully initialized and the IB connections have been established. It is also guaranteed to be called exactly once (per instance). If you also create instances of this type programmatically, then you should consider a shared initialization method which can be called in -awakeFromNib.
The problem with adding subviews in the initializer is that your view is in a partially constructed state, and ultimately there may be side effects or difficulty initializing your view/object graph in a well defined and deterministic manner; -awakeFromNib at least ensures all NIB objects have been created and the connections exist, although the order in which -awakeFromNib is called is not defined. If you need exact ordering, then you should really approach it programmatically.
I've done the configuring code in the -(void)layoutSubviews method as
my -initwithFrame not get called
UIView has two designated initializers. When the view is loaded from a nib, -initWithCoder: is called instead of -initWithFrame:. You can put initialization code in that method, or put it in a common initialization method that you call from both initializers.
Is it right to do our initialization in the -layoutSubViews as there
is a chance for this method to get called another time.
That is, indeed, the problem with using -layoutSubviews for initialization: it may be called more than once, and not always at the beginning of your view's lifetime. Better to use the initialization methods.
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
I am wanting to create a custom UIView class that will show a dynamic number of UISegmentedControl objects depending on some input. For example, if a client has 5 products in their cart, the UIView should generate 5 UISegmentedControl objects that I will then link with each item.
The problem I am having is getting this to work in a UIView. Here is what I have done so far. I am successfully able to create a UISegmentedControl object and display it programmatically within my main UIViewController. I don't get any display when adding it to my UIView class. Here is the implementation code for the UIView class:
#import "ajdSegmentView.h"
#implementation ajdSegmentView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *itemArray = [NSArray arrayWithObjects:#"Yes", #"No", nil];
UISegmentedControl *button = [[UISegmentedControl alloc] initWithItems:itemArray];
button.frame = CGRectMake(35,44, 120,44);
button.segmentedControlStyle = UISegmentedControlStylePlain;
button.selectedSegmentIndex = 1;
[self addSubview:button];
}
return self;
}
#end
I created a new UIView object via Storyboard and placed it inside the UIViewController scene. I made sure to set the class from the generic UIView class to my new custom class. I added and outlet for the UIView in my UIViewController class. Here is the code inside the implementation of UIViewController:
#import "ajdViewController.h"
#interface ajdViewController ()
#end
#implementation ajdViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.segmentView = [[ajdSegmentView alloc] init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
That's all I have tried. I have been searching through a lot of pages and trying to implement this without asking here, but I seem to be looking in the wrong places.
First you need to check ajdSegmentView is UIVIew or UIViewController. It is fine if it is UIView. If it is type of UIViewController then you need to add this line while adding Segment.
[self.view addSubview:button];
In place of:
[self addSubview:button];
And One more thing You forget to add this View to your main after allocating so You can declare like this:
objajdSegmentView = [[ajdSegmentView alloc] init];
[self.view addSubview:objajdSegmentView.view];
I have just added this thing. i got result like this way.
Hope this will work for you.
You're initializing your custom view using the init method, but your initialization for ajdSegmentView is in your initWithFrame: method (which in your case is not getting called).
So replace:
self.segmentView = [[ajdSegmentView alloc] init];
with:
// Change the frame to what you want
self.segmentView = [[ajdSegmentView alloc] initWithFrame:CGRectMake(0,0,100,40)];
Also don't forget to add your view to the view controller's view also.
[self.view addSubview:self.segmentView];
Unless this view is being created with interface builder, in which case you will need to override initWithCoder: in your ajdSegmentView class.
I'm not familiar with Storyboard though, so maybe I'm missing something, but in a standard scenario what I said above will solve your problem.