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.
Related
I'd like to be able to create elements (like UIViews) when for example the user touches a button
NSMutableString *myVar = [NSMutableString stringWithFormat:#"_view%i", num];
UIView * newView = [self valueForKey:myVar];
but without adding all the
UIView * _view1;
UIView * _view2;
...
in the .h file (if only this is possible..)
You can use an NSMutableArray to hold them. Each time you create a new view just add it to the array.
Here's sample code that should do what you want.
#interface MyViewController ()
#property (strong, nonatomic) NSMutableArray *listChildViews;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.listChildViews = [[NSMutableArray alloc] init];
}
- (IBAction)addChildViewTapped:(id)sender
{
int numChildViews = [self.listChildViews count];
++numChildViews;
// add new child view
NSString *labelForNewView = [NSString stringWithFormat:#"view %d", numChildViews];
CGFloat labelHeight = 28.0;
UILabel *childView = [[UILabel alloc] initWithFrame:CGRectMake(10, numChildViews*labelHeight, 120.0, labelHeight)];
childView.text = labelForNewView;
[self.listChildViews addObject:childView];
[self.view addSubview:childView];
}
#end
Here is code implementation of pauls answer:
- (IBAction)userTouchedButton:(id)sender{
for (int i = 0; i < 100; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)];
[view setBackgroundColor:[UIColor redColor]];//to distinguish theseViews from other views
[view setTag:i];//to identified it later
[_array insertObject:view atIndex:i];// globleMutble array
[self.view addSubview:view];
}
}
You need not add your views in the .h file. Just instantiate before and where you add them
-(void) addButton
{
UIView *view = [self view];
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button1 setTitle:#"My Button" forState:UIControlStateNormal];
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 50, 0, 0)];
[myLabel setText:#"My Label"];
[button1 sizeToFit];
[myLabel sizeToFit];
[view addSubview:button1];
[view addSubview:myLabel];
}
I have made a simple subclass of UIView which has a few subviews but its breaking for a reason I can't see.
In my storyboard view controller I have added a UIView and made it the shape I want it, and added my custom class to it. The contents of my subclass is below:
#implementation PersonDetailCell
#synthesize button, requiredMarker, textField;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)layoutSubviews
{
[self setBackgroundColor:[UIColor colorWithRed:87.0f/255.0f green:153.0f/255.0f blue:191.0f/255.0f alpha:1.0f]];
textField = [[DetailTextField alloc] initWithFrame:CGRectMake(10, 0, self.frame.size.width -20, self.frame.size.height)];
[textField setFont:[UIFont fontWithName:#"Helvetica" size:14.0f]];
[textField setTextColor:[UIColor whiteColor]];
[textField setHidden:YES];
[self addSubview:textField];
button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setHidden:YES];
[self addSubview:button];
requiredMarker = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 4, 22)];
[requiredMarker setBackgroundColor:[UIColor redColor]];
[requiredMarker setHidden:YES];
[self addSubview:requiredMarker];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Then in my view controller code I have imported my subclass and hooked up an IBOutlet of it to my view in my storyboard. In my viewDidLoad of my view controller I also try setting background colour of this view but nothing happens.
All of my code runs without crashing, but my issues:
I can't set the background colour from my view controller, (or any other properties for that matter).
When I run the app, the height is more than that I set in my storyboard (width is fine), but I change the height of the view nowhere.
Any ideas? Am i using the wrong approach somehow?
Thanks.
If it is a table cell, then the class should be extending UITableViewCell. Buttons and textfields can be added on the storyboard - then you can use struts and springs (Size Inspector pane) to ensure the view resizes properly. I don't think layoutSubviews should be used for adding new UI elements (maybe for resizing if you need to do it manually).
Here is some working code:
#interface ChildView()
#property (strong, nonatomic) UITextField *textField;
#property (strong, nonatomic) UIButton *button;
#property (strong, nonatomic) UIImageView *requiredMarker;
#end
#implementation ChildView
#synthesize textField= _textField;
#synthesize button = _button;
#synthesize requiredMarker = _requiredMarker;
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setBackgroundColor:[UIColor colorWithRed:87.0f/255.0f green:153.0f/255.0f blue:191.0f/255.0f alpha:1.0f]];
self.textField = [[UITextField alloc] init];
[self.textField setFont:[UIFont fontWithName:#"Helvetica" size:14.0f]];
[self.textField setTextColor:[UIColor whiteColor]];
self.textField.text = #"Test text";
[self addSubview:self.textField];
self.button = [[UIButton alloc] init];
self.button.backgroundColor = [UIColor greenColor];
[self addSubview:self.button];
self.requiredMarker = [[UIImageView alloc] init];
[self.requiredMarker setBackgroundColor:[UIColor redColor]];
[self addSubview:self.requiredMarker];
}
return self;
}
-(void)layoutSubviews
{
CGSize hostSize = self.frame.size;
float length = 22;
self.textField.frame = CGRectMake(10, length, hostSize.width, hostSize.height-length);
self.button.frame = CGRectMake(0, 0, hostSize.width, length);
self.requiredMarker.frame = CGRectMake(0, 0, 4, length);
}
#end
I added a text field in viewDidLoad but it did not show up on screen.
Here's .h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController{
UITextField *tfText;
}
#property (nonatomic, retain) UITextField *tfText;
#end
Here's .m
- (void)viewDidLoad{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor lightGrayColor]];
tfText.frame = CGRectMake(65, 100, 200, 50);
tfText.backgroundColor = [UIColor whiteColor];
[tfText setTextColor:[UIColor blackColor]];
tfText.placeholder = #"Test";
[tfText setBorderStyle:UITextBorderStyleNone];
[self.view addSubview:tfText];
}
seems that you need to initialize the object... I mean
UITextField *newTextField = [[UITextField alloc] init];
self.tfText = newTextField;
//....
//all your code here
//....
[newTextField release];
And dont forget to release your instance on dealloc method.
D33pN16h7 is correct, you need to instantiate your object. However I would do it a little differently than described above, there is no reason to create a UITextField instance, and then set your instance to it.
self.tfText = [[UITextField alloc] init];
UITextField entered2 = [[UITextField alloc] initWithFrame:CGRectMake(120.0, 125.0, 150.0, 25.0)];
[entered2 setBackgroundColor:[UIColor whiteColor]];
entered2.text=#"Active";
[self.view addSubview:entered2];
[entered2 release];
i have a problem with my app crashing when my custom TableViewCell gets released.
the Cell gets initialized like the following in cellForRowAtIndexPath:
SearchTableViewCell *cell = (SearchTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[SearchTableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.nameLabel.text = #"some text";
cell.addressLabel.text = #"some more text";
the cell class itself looks like this
#import <UIKit/UIKit.h>
#class EGOImageView;
#interface SearchTableViewCell : UITableViewCell {
UILabel *nameLabel;
UILabel *addressLabel;
EGOImageView *imageView;
}
#property (nonatomic, retain) UILabel *nameLabel;
#property (nonatomic, retain) UILabel *addressLabel;
- (UILabel *)labelWithColor:(UIColor*)color selectedColor:(UIColor*)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold frame:(CGRect)rect;
- (void)setThumb:(NSString*)thumb;
#end
.m
#import "SearchTableViewCell.h"
#import "EGOImageView.h"
#import "UIView+Additions.h"
#implementation SearchTableViewCell
#synthesize nameLabel = _nameLabel, addressLabel = _addressLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
// Initialization code
UIView *myContentView = self.contentView;
// Name
_nameLabel = [self labelWithColor:[UIColor blackColor] selectedColor:[UIColor whiteColor] fontSize:16.0f bold:YES frame:CGRectMake(140.0f, 16.0f, 181.0f, 21.0f)];
[myContentView addSubview:_nameLabel];
[_nameLabel release];
// Adress
_addressLabel = [self labelWithColor:[UIColor blackColor] selectedColor:[UIColor whiteColor] fontSize:13.0f bold:YES frame:CGRectMake(140.0f, _nameLabel.bottom, 181.0f, 21.0f)];
[myContentView addSubview:_addressLabel];
[_addressLabel release];
// Image
imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageNamed:#"placeholder.png"]];
imageView.frame = CGRectMake(9.0f, 9.0f, 120.0f, 80.0f);
[myContentView addSubview:imageView];
[imageView release];
}
return self;
}
- (UILabel *)labelWithColor:(UIColor*)color selectedColor:(UIColor*)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold frame:(CGRect)rect {
UIFont *font;
if(bold) {
font = [UIFont boldSystemFontOfSize:fontSize];
} else {
font = [UIFont systemFontOfSize:fontSize];
}
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.backgroundColor = [UIColor clearColor];
label.textColor = color;
label.highlightedTextColor = selectedColor;
label.font = font;
return label;
}
- (void)setThumb:(NSString*)thumb {
imageView.imageURL = [NSURL URLWithString:thumb];
}
- (void)willMoveToSuperview:(UIView *)newSuperview {
[super willMoveToSuperview:newSuperview];
if(!newSuperview) {
[imageView cancelImageLoad];
}
}
- (void)dealloc {
[_addressLabel release];
[_nameLabel release];
[imageView release];
[super dealloc];
}
#end
does anybody have an idea why my app crashes on releasing such a cell? commenting out the 2 labels and the image view on dealloc method, the app doesn't crash, but then there will be a memory leak right?
thanks for all hints! please leave a comment if something is unclear!
imageView is being released twice, once when it is created:
imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageNamed:#"placeholder.png"]];
imageView.frame = CGRectMake(9.0f, 9.0f, 120.0f, 80.0f);
[myContentView addSubview:imageView];
[imageView release];
and once in dealloc:
- (void)dealloc {
[_addressLabel release];
[_nameLabel release];
[imageView release];
[super dealloc];
}
You already release the memory for that labels after adding to view,again you are trying to release memory those objects are already relese in dealloc method that's why it is killing.if you remove 3 statements in dealloc method it will not crash.
I think the Problem is that you release your properties direct in the initWithStyle function and additional to that in dealloc again. Try removing the release from initWithStyle:
Furthermore you named your variables without _ in the interface but with _ #synthesize'ing in the implementation
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?