This started as a simple problem, but I can't solve it. I have subclassed a UIButton (I know, controversial) but I can't seem to get it to work properly. My UIButton subclass looks like this:
.h
#interface SocialButton : UIButton
#property (nonatomic, strong) CustomClass *cust;
- (id)initWithObject:(CustomClass *)custom andFrame:(CGRect)frame;
#end
.m
#implementation SocialButton
#synthesize custom=_custom;
- (id)initWithObject:(CustomClass *)custom andFrame:(CGRect)frame;
{
self = [self initWithFrame:frame];
if (self)
{
[self addTarget:self action:#selector(buttonTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
self.backgroundColor = [UIColor blueColor];
self.cust = custom;
}
return self;
}
#end
Later, when I try to create a button from this class, like so:
SocialButton *social = [[SocialButton alloc] initWithObject:object andFrame:frame];
it doesn't appear to work. Any suggestions?
Seems like you forgot to call super. Change:
self = [self initWithFrame:frame];
to:
self = [super initWithFrame:frame];
Related
I have a AuthButtons.h header:
#import <UIKit/UIKit.h>
#interface AuthButtons : UIButton
#property (nonatomic, weak) UIColor *backgroundColor;
#end
The implementation AuthButtons.m class is as follows:
#import "AuthButtons.h"
#implementation AuthButtons
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
backgroundColor = [UIColor blueColor];
}
- (UIColor *)backgroundColor
{
return [UIColor whiteColor];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
Then in my viewcontroller.m class I have:
#interface ViewController ()
#property (nonatomic, weak) AuthButtons *registerButton;
#end
...
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_registerButton = [AuthButtons buttonWithType:UIButtonTypeSystem];
[_registerButton setTitle:#"Register" forState:UIControlStateNormal];
[_registerButton setBackgroundColor:[UIColor whiteColor]];
_registerButton.frame = CGRectMake(0.0, 0.0, self.screenWidth / 2.0, 100.0);
[_registerButton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
It keeps crashing when I try to create the button. What am I missing?
Your button is weak and not added as a subview so its deallocated right away
Use strong instead of weak(i always use strong type when using buttons in ARC enabled class,not sure whether its a bad habbit :-))
#property (nonatomic, strong) AuthButtons *registerButton;
and add it as your subview([self.view addSubview:_registerButton]).Hope you have implemeted the function called -(void)buttonPressed:(id )sender in your class.
I am slowly developing a custom button (while learning how to implement classes etc).
I have a ViewController, which imports a SuperButton.h (UIControl) and then creates an instance of the SuperButton. (This works, as proved by a NSLog.)
But I cannot get the method in SuperButton to display a Label.
I think this might have something to with the '.center' value or the 'addSubview' command?
I would really appreciate your help. Thanks.
Here is my SuperButton.m code:
#import "SuperButton.h"
#implementation SuperButton
#synthesize firstTitle;
#synthesize myLabel;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void) shoutName{
NSLog(#"My name is %#", firstTitle);
self.backgroundColor = [UIColor blueColor];
CGRect labelFrame = CGRectMake(0.0f, 0.0f, 100.0f, 50.0f);
self.myLabel = [[UILabel alloc] initWithFrame:labelFrame];
self.myLabel.text = #"Come on, don't be shy.";
self.myLabel.font = [UIFont italicSystemFontOfSize:14.0f];
self.myLabel.textColor = [UIColor grayColor];
self.myLabel.center = self.center;
[self addSubview:self.myLabel];
}
Here is the code in my ViewController:
- (void) makeButton{
SuperButton *button1 = [[SuperButton alloc] init];
button1.firstTitle = #"Mr. Ploppy";
[button1 shoutName];
}
(EDIT:) Just in case, here's the SuperButton.h code:
#import <UIKit/UIKit.h>
#interface SuperButton : UIControl
#property (nonatomic, strong) NSString *firstTitle;
#property (nonatomic, strong) UILabel *myLabel;
- (void) shoutName;
#end
I found an answer elsewhere.
I needed to addSubview the 'button'. My working code now looks like this:
- (void) makeButton{
SuperButton *button1 = [[SuperButton alloc] init];
button1.firstTitle = #"Mr. Ploppy";
[button1 shoutName];
[self.view addSubview:button1.myLabel];
[self.view sendSubviewToBack:button1.myLabel];
}
You are initializing your button not with initWithFrame: method, but with simple init. This makes the button of CGRectZero size. Change this line:
SuperButton *button1 = [[SuperButton alloc] init];
to this:
SuperButton *button1 = [[SuperButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 50.0f)];
or add a call to setFrame: after initializing your button.
I tried to subclass UIButton to include an activity indicator, but when i use initWithFrame:(since i'm subclassing uibutton i'm not using buttonWithType:) the button doesn't display. Also how would i set the button type in this case?:
my view controller:
ActivityIndicatorButton *button = [[ActivityIndicatorButton alloc] initWithFrame:CGRectMake(10, 10, 300, 44)];
[button addTarget:self action:#selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"Older Posts..." forState: UIControlStateNormal];
[cell addSubview:button];
[button release];
my activityindicatorbutton class:
#import <Foundation/Foundation.h>
#interface ActivityIndicatorButton : UIButton {
UIActivityIndicatorView *_activityView;
}
-(void)startAnimating;
-(void)stopAnimating;
#end
#implementation ActivityIndicatorButton
- (id)initWithFrame:(CGRect)frame {
if (self=[super initWithFrame:frame]) {
_activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
_activityView.frame = CGRectOffset(_activityView.frame, 60.0f, 10.0f);
[self addSubview: _activityView];
}
return self;
}
-(void) dealloc{
[super dealloc];
[_activityView release];
_activityView = nil;
}
-(void)startAnimating {
[_activityView startAnimating];
}
-(void)stopAnimating {
[_activityView stopAnimating];
}
#end
Favour composition over inheritance.
Create a UIView which contains the components you need and add them to your view.
I ran into a similar situation, and agree with Jeff that you don't really need to subclass UIButton. I solved this by subclassing UIControl, and then overriding layoutSubviews to do all of the configuration of the views I wanted on my "button". It's a much more simple implementation that subclassing UIButton since there does seem to be some hidden mojo going on under the hood. My implementation looked like this:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.opaque = YES;
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self addSubview:self.imageView];
self.textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:self.textLabel];
}
return self;
}
And layoutSubviews looked like this:
- (void)layoutSubviews {
[super layoutSubviews];
// Get the size of the button
CGRect bounds = self.bounds;
// Configure the subviews of the "button"
...
}
I have created a custom class, preferring composition over inheritance and it works perfect. My custom class has a button and it knows it's MCContact object. Also it draws a proper button and calculates frames automatically using MCContact object, that is passed.
Header file sample:
#import <UIKit/UIKit.h>
#protocol MCContactViewDelegate;
#interface MCContactView : UIView
{
}
#property (nonatomic, strong) MCContact *mcContact;
#property (nonatomic, weak) id <MCContactViewDelegate> delegate;
- (id)initWithContact:(MCContact*)mcContact delegate:(id <MCContactViewDelegate>)delegate;
#end
#protocol MCContactViewDelegate <NSObject>
- (void)contactViewButtonClicked:(MCContactView*)contactView;
#end
Implementation file:
#import "MCContactView.h"
#interface MCContactView()
{
UIButton *_button;
}
#end
#implementation MCContactView
- (id)initWithContact:(MCContact*)mcContact delegate:(id <MCContactViewDelegate>)delegate
{
self = [super initWithFrame:CGRectZero];
if (self) {
GetTheme();
_mcContact = mcContact;
_delegate = delegate;
_button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *normalBackgroundImage = [[UIImage imageNamed:#"tokenNormal.png"] stretchableImageWithLeftCapWidth:12.5 topCapHeight:12.5];
[_button setBackgroundImage:normalBackgroundImage forState:UIControlStateNormal];
UIImage *highlightedBackgroundImage = [[UIImage imageNamed:#"tokenHighlighted.png"] stretchableImageWithLeftCapWidth:12.5 topCapHeight:12.5];
[_button setBackgroundImage:highlightedBackgroundImage forState:UIControlStateHighlighted];
_button.titleLabel.font = [theme contactButtonFont];
[_button setTitleColor:[theme contactButtonTextColor] forState:UIControlStateNormal];
[_button setTitleEdgeInsets:UIEdgeInsetsMake(4, 6, 4, 6)];
NSString *tokenString = ([allTrim(mcContact.name) length]>0) ? mcContact.name : mcContact.eMail;
[_button setTitle:tokenString forState:UIControlStateNormal];
[_button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
CGSize size = [tokenString sizeWithFont:[theme contactButtonFont]];
size.width += 20;
if (size.width > 200) {
size.width = 200;
}
size.height = normalBackgroundImage.size.height;
[_button setFrame:CGRectMake(0, 0, size.width, size.height)];
self.frame = _button.frame;
[self addSubview:_button];
}
return self;
}
- (void)buttonClicked:(id)sender
{
[self.delegate contactViewButtonClicked:self];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
You have a pretty obvious problem that concerns your dealloc method: [super dealloc]; must be called AT THE END of your implementation, or else the line after that will try to access a memory space (the ivar space) that has been already deallocated, so it's going to crash.
For the other problem, I'm not sure it's a good idea to put an activity monitor as the subview of a button in general...
You don’t really want to subclass UIButton. It’s a class cluster, so individual instances will be something like UIRoundRectButton or some other private Apple class. What are you trying to do that requires a subclass?
I'm trying to remotely adjust an object myImageView's alphato 0 of MyViewControllerclass From Another class Assistant(it's a NSObject subclass).
in Assistant.h
#import <Foundation/Foundation.h>
#class MyViewController;
#interface Assistant : NSObject {
MyViewController *myViewController;
UIImageView *button;
}
- (void)adjustAlpha:(id)sender;
#property (nonatomic, retain) UIImageView *button;
in Assistant.m
#import "MyViewController.h"
#implementation Assistant
#synthesize button;
- (id)init
{
self = [super init];
if (self)
{
myViewController = [[MyViewController alloc] init];
NSLog(#"alloced?: %#", myViewController ? #"YES" : #"NO");
}
return self;
}
- (void)adjustAlpha:(id)sender
{
NSLog(#"method called");
myViewController.myImageView.alpha = 0;
}
the method did get called, But myViewController.myImageView.alpha didn't change, why? where i need to fix? thank you for reading ^_^
Edit
This is MyViewController class:
MyViewController.h
#class Assistant;
#class MyAppAppDelegate;
#interface MyViewController : UIViewController <UITextViewDelegate, UIScrollViewDelegate>
{
UIImageView *myImageView;
Assistant *assistant;
}
#property (nonatomic, retain) UIImageView *myImageView;
MyViewController.m
#import "MyViewController.h"
#import "Assistant.h"
#implementation MyViewController
#synthesize myImageView;
- (void)viewDidLoad
{
[super viewDidLoad];
assistant = [[Assistant alloc] init];
myScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,320,460);
[self.view addSubview: myScrollView];
myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"weather.png"]];
myImageView.frame = CGRectMake(19, 54, 48, 48);
myImageView.userInteractionEnabled = YES;
[myScrollView addSubview:myImageView];
//this button uses to call adjustAlpha
assistant.button = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"button.png"]];
assistant.button.frame = CGRectMake(19, 126, 48, 48);
assistant.button.userInteractionEnabled = YES;
[myScrollView addSubview:assistant.button];
//here is how i call adjustAlpha:
assistant.adjustAlphaTap = [[UITapGestureRecognizer alloc] initWithTarget:assistant action:#selector(adjustAlpha:)];
[assistant.button addGestureRecognizer:assistant.adjustAlphaTap];
}
this question got complex and need some experiments, so just hanging there, will make it clear when i have time. for developer met the same problems: declare the object you need to access in a singleton class, can be easily accessed from any other classes, and Merry Christmas!
This will only work if myImageView is declared as a global scope variable (also called an iVar) in the .h file of your MyViewController class AND that it has a property of (nonatomic, retain) with a matching #synthesize in the .m.
If this sounds like what you're doing then can you please post the contents of your MyViewController h and m files.
If you have everything defined like Thomas described, can you check if the adjustAlpha method is called on the main thread?
I am new to iPhone programming. And I have an issue. I need to create a custom user control that I will add to my UIScrollView dinamically. The control has an UITextField and an UIButton. See the code below:
#import <UIKit/UIKit.h>
#interface FieldWithValueControl : UIView {
UITextField *txtTagName;
UIButton *addButton;
}
#property (nonatomic, readonly) UITextField *txtTagName;
#property (nonatomic, readonly) UIButton *addButton;
#end
#import "FieldWithValueControl.h"
#define ITEM_SPACING 10
#define ITEM_HEIGHT 20
#define SWITCHBOX_WIDTH 100
#define SCREEN_WIDTH 320
#define ITEM_FONT_SIZE 14
#define TEXTBOX_WIDTH 150
#implementation FieldWithValueControl
#synthesize txtTagName;
#synthesize addButton;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
txtTagName = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, TEXTBOX_WIDTH, ITEM_HEIGHT)];
txtTagName.borderStyle = UITextBorderStyleRoundedRect;
addButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
[addButton setFrame:CGRectMake(ITEM_SPACING + TEXTBOX_WIDTH, 0, ITEM_HEIGHT, ITEM_HEIGHT)];
[addButton addTarget:self action:#selector(addButtonTouched:)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:txtTagName];
[self addSubview:addButton];
}
return self;
}
- (void)addButtonTouched:sender
{
UIButton *button = (UIButton*)sender;
NSString *title = [button titleLabel].text;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[txtTagName release];
[addButton release];
[super dealloc];
}
#end
In my code I create an object of that class and add it to scrollView on form.
FieldWithValueControl *newTagControl = (FieldWithValueControl*)[[FieldWithValueControl alloc] initWithFrame:CGRectMake(ITEM_SPACING, currentOffset + ITEM_SPACING, 0, 0)];
[scrollView addSubview:newTagControl];
The control looks fine, but if I click to the textbox or to the button nothing happens. Keyboard doesn't appear, the button is not clickable etc.
Set width and height values for your control more then 0:
FieldWithValueControl *newTagControl = [[FieldWithValueControl alloc] initWithFrame:
CGRectMake(ITEM_SPACING, currentOffset + ITEM_SPACING, 100, 200)];
try setting the delegate to self.