Creating UIButtons Programmatically - unrecognized selector sent to instance - ios

Relevant code:
- (void) createButtons {
NSMutableArray *buttonTitleArray = [[NSMutableArray alloc] init];
[buttonTitleArray addObject:#"Website"];
[buttonTitleArray addObject:#"Blah"];
[buttonTitleArray addObject:#"Blah"];
int xPosition = 20;
for (int i = 0; i <= buttonTitleArray.count-1; i++) {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(xPosition, 25, 90, 40)];
button.userInteractionEnabled = TRUE;
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:[buttonTitleArray objectAtIndex:i] forState:UIControlStateNormal];
button.backgroundColor = [UIColor colorWithRed:1 green:200.0/255.0 blue:0 alpha:1];
button.titleLabel.font = [UIFont systemFontOfSize:17];
[self.buttonArray addObject:button];
xPosition += 91;
}
for (UIButton *button in self.buttonArray) {
[button addTarget:self action:#selector(showWebsite:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:button];
}
}
- (void)showWebsite:(UIButton *)sender {
NSLog(#"Website");
}
In init:
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.buttonArray = tempArray;
For testing purposes I made all the buttons have the same target.
When I click the button, I get unrecognized selector sent to instance.
Any thoughts here?

For some reason your self in your posted code gets released and the pointer location gets re-used by the OS as a UIGestureDelayedTouch.
Make sure the object that you are creating these buttons in does not get released by the OS before you want to use the showWebsite function.
Sometimes people use code like:
-(void)function
{
UIViewController *controller = [[UIViewController alloc] init];
[someOtherView addSubview:controller.view];
}
In this case at the end of the function the controller gets released (as there are no further references to it) and anything that could point to it in the future will point to an invalid (deallocated and possibly re-used) object.
UIButton does not retain objects set as targets, so the OS thinks the objects are valid for release if they are not retained by anything else in the program.

Related

How to change title color of a set of UIButtons created programmatically?

In my app I have created a few buttons programmatically using a for loop as shown below.It is for a HORIZONTAL TAB MENU
In the action,I have to highlight the selected button(and greyout the remaining button titles).How to do this?
It should look almost like the image below.
When a button is clicked,the clicked button title color should be
white and all other buttons should have a grey color.I know I can
access sender.titlecolor in the button action.But What about the other
buttons?
-(void)createButtons
{
float buttonMinX=10;
float buttonMinY=0;
scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, _tabViewBackground.frame.size.width, _tabViewBackground.frame.size.height)];
ButtonIndex=0;
scrollView.showsHorizontalScrollIndicator = NO;
[_tabViewBackground addSubview:scrollView];
for (int i=0; i<_tabItemsListArray.count; i++)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.tintColor=[UIColor whiteColor];
button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.numberOfLines = 1;
button.tag=i;
[button addTarget:self action:#selector(action:) forControlEvents:UIControlEventTouchUpInside];
UIFont *font1 = [UIFont fontWithName:#"HelveticaNeue-Thin" size:20.0f];
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setAlignment:NSTextAlignmentCenter];
NSDictionary *dict1 = #{NSUnderlineStyleAttributeName:#(NSUnderlineStyleNone),
NSFontAttributeName:font1,
NSParagraphStyleAttributeName:style};
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] init];
[attString appendAttributedString:[[NSAttributedString alloc] initWithString:[_tabItemsListArray objectAtIndex:i] attributes:dict1]];
[button setAttributedTitle:attString forState:UIControlStateNormal];
buttonWidth= [self getWidthOfRect:button.titleLabel];
button.frame = CGRectMake(buttonMinX,buttonMinY,buttonWidth,_tabViewBackground.frame.size.height);
buttonMinX+=buttonWidth+05;
sublayer = [[UIView alloc]init];
sublayer.backgroundColor = [UIColor greenColor];
sublayer.tag=kButtonSelectrorTag+i;
sublayer.frame = CGRectMake(button.frame.origin.x,button.frame.size.height-2, button.frame.size.width,2);
[scrollView addSubview:sublayer];
sublayer.hidden=YES;
if (ButtonIndex==i)
{
sublayer.hidden=NO;
}
button.backgroundColor=[UIColor clearColor];
[scrollView addSubview:button];
}
scrollView.backgroundColor = [UIColor blueColor];
scrollView.contentSize = CGSizeMake(buttonMinX+10,_tabViewBackground.frame.size.height);
}
-(CGFloat)getWidthOfRect:(UILabel*)titleLabel
{
CGFloat widthIs =[titleLabel.text boundingRectWithSize:titleLabel.frame.size options:NSStringDrawingUsesDeviceMetrics attributes:#{ NSFontAttributeName:titleLabel.font }context:nil].size.width;
widthIs = ceilf(widthIs);
// NSLog(#"the width of yourLabel is %f", widthIs);
return widthIs+30;
}
- (void)action:(UIButton*)sender
{
for (int i=0; i<_tabItemsListArray.count; i++)
{
UIView *tabSelector = (UIView *)[self.view viewWithTag:kButtonSelectrorTag+i];
[tabSelector setHidden:YES];
}
UIView *tabSelector = (UIView *)[self.view viewWithTag:kButtonSelectrorTag+sender.tag];
[tabSelector setHidden:NO];
}
I have a button selector below every button.I should show one buttonSelector at a time.It is working great using the code in the action:
I've notice that you are using setAttributedTitle:forState:. You can set attributes for title in the same way but for UIControlStateSelected also.
Then, if you set button.selected = YES; attributes from UIControlStateSelected will apply.
If you set button.selected = NO; attributes from UIControlStateNormal will apply.
EDIT:
You can create your buttons like this:
NSInteger numberOfButtons = 10;
NSMutableArray *menuButtonsMutableArray = [[NSMutableArray alloc] initWithCapacity:numberOfButtons];
for (int i = 0; i < numberOfButtons; i++) {
UIButton *button = [UIButton new];
//layout your button somehow
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
[button setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
[button addTarget:self action:#selector(menuButtonDidTap:) forControlEvents:UIControlEventTouchUpInside];
[menuButtonsMutableArray addObject:button];
}
self.menuButtonsArray = [menuButtonsMutableArray copy];
Then in action method:
- (void)menuButtonDidTap:(UIButton *)sender {
for (UIButton *button in self.menuButtonsArray) {
button.selected = (button == sender);
}
}
#IBOutlet var AllButtons: [UIButton]!
for button in AllButtons {
if button.backgroundColor == UIColor.red {
button.backgroundColor = compare.backgroundColor
}
}
Drag and drop all your buttons in AllButtons, then use for loop to access all the buttons and do whatever you like to do.
This is a simple example, hope it helps.

How to pass target parameter for UIButton?

I write one category method in UIView+Extensions.m:
#interface UIView (Extensions)
+ (UIView*)configureMoreViewWithBtns:(NSArray*)btnsConf;
#end
+ (UIView*)configureMoreViewWithBtns:(NSArray*)btnsConf
{
UIView* moreView = [[self alloc] initWithFrame:CGRectMake(195, 180, 120, 100)];
[moreView setBackgroundColor:[UIColor lightGrayColor]];
for (int i = 0; i < btns.count; i++) {
NSDictionary* confDict = btnsConf[i];
UIButton* btn = [[UIButton alloc] initWithFrame:CGRectMake(0, i*30 + 10, 120, 20)];
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[btn setTitle:confDict[#"title"] forState:UIControlStateNormal];
[btn addTarget:self
action:NSSelectorFromString(confDict[#"selector"]
forControlEvents:UIControlEventTouchUpInside];
[moreView addSubView:btn];
}
return moreView;
}
But this implement is wrong, because i don't know how pass target parameter from my ViewController?
In my viewController, i called this method like this:
- (void)handleMoreImageTapped:(UITapGestureRecognizer*)gestureRecognizer
{
NSLog(#"%s", __FUNCTION__);
UITableViewCell* tappedCell = [UIView tableViewCellFromTapGestture:gestureRecognizer];
NSArray* btnsConf = #[
#{#"title": #"分享", #"selector": NSStringFromSelector(#selector(handleShare:))},
#{#"title": #"私信", #"selector": NSStringFromSelector(#selector(handleSiXin:))},
#{#"title": #"举报或屏蔽", #"selector": NSStringFromSelector(#selector(handleJuBao:))}
];
UIView* moreView = [UIView configureMoreViewWithBtns:btnsConf];
}
You need to also pass the target (the object the selector would be called on, in this case the viewcontroller from which you call the configuremoreviewwithbtns method) in the dictionairies.
So a dictionairy you add to the array would become
#{#"title": #"thetitle", #"selector": NSStringFromSelector(#selector(theselector:)), #"target": self},
and you'd have to change the UIView extension to this:
[btn addTarget:confDict[#"target"]
action:NSSelectorFromString(confDict[#"selector"]
forControlEvents:UIControlEventTouchUpInside];

Remove a subview from UIViewController

I'm adding a subview to the main view but then I cant remove it from this view.
NextViewController *nextView = [[NextViewController alloc] init];
nextView.transitioningDelegate = self;
nextView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.presentedViewController.view addSubview:nextView.view];
nextView.view.frame = CGRectMake(724, 80, 300, 150);
UIButton *stayButton = [[UIButton alloc] initWithFrame:CGRectMake(101, 94, 90, 49)];
[stayButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[stayButton setTitle:#"Detener" forState:UIControlStateNormal];
[stayButton addTarget:self
action:#selector(waitForNextView)
forControlEvents:UIControlEventTouchUpInside];
[nextView.view addSubview:stayButton];
stayButton.titleLabel.font = [UIFont systemFontOfSize:25];
[stayButton setTitleColor:[UIColor colorWithRed:(255/255.0) green:(255/255.0) blue:(255/255.0) alpha:1] forState:UIControlStateNormal];
here I'm adding a button to the view and then when the button is pushed it goes`-
(void)waitForNextView{
transition = NO;
NextViewController *nextView = [[NextViewController alloc] init];
SectionViewController *sectionView = [[SectionViewController alloc]init];
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
actualSection = 0;
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(dismissView2) userInfo:nil repeats:NO];
}
I've tried with:
[nextView.view removeFromSuperView];
[nextView.presentedViewController removeFromParentViewController];
But I don't know what else can I do :S
If you want more information, nextView is a UIViewController of 300x100 size. I want to present it and then remove it when I hit the button, that's all.
I would be great if someone can help me.
Thanks
You are creating different reference and then you're trying to remove it from its superview while not being added at all. I will try to explain:
1. When you're adding nextView.view to self.view you have created a instance like this
NextViewController *nextView = [[NextViewController alloc] init];
2. When you're trying to remove it, instead of referring to the instance above you're creating a brand new one again in your waitForNextView method like so again
NextViewController *nextView = [[NextViewController alloc] init];
and then you're trying to remove it. Please note that the last instance of nextView has not being added anywhere yet, and this is the reason your code doesn't work.
In order to work you should refer to the first reference you've added to self.view. Please adjust your code as shown below
In your ViewController.m
#interface ViewController()
{
NextViewController *nextView;
}
When adding your viewController
nextView = [[NextViewController alloc] init];
nextView.transitioningDelegate = self;
nextView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.presentedViewController.view addSubview:nextView.view];
nextView.view.frame = CGRectMake(724, 80, 300, 150);
UIButton *stayButton = [[UIButton alloc] initWithFrame:CGRectMake(101, 94, 90, 49)];
[stayButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[stayButton setTitle:#"Detener" forState:UIControlStateNormal];
[stayButton addTarget:self
action:#selector(waitForNextView)
forControlEvents:UIControlEventTouchUpInside];
[nextView.view addSubview:stayButton];
stayButton.titleLabel.font = [UIFont systemFontOfSize:25];
[stayButton setTitleColor:[UIColor colorWithRed:(255/255.0) green:(255/255.0) blue:(255/255.0) alpha:1] forState:UIControlStateNormal];
And finally in your IBAction method
-(void)waitForNextView{
transition = NO;
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
actualSection = 0;
[nextView removeFromSuperview];
}
The problem here is that you are adding the button as a subview to the view of nextView, so removing the view of nextView won't work.
If you add a tag to your button, then you can retrieve the subview by using the tag. Eg. When you define your button:
[stayButton setTag:1];
Then in the waitForNextView method, you can get the control like this:
UIButton *stayButton = (UIButton*)[nextView.view viewWithTag:1];
[stayButton removeFromSuperView];
use this code in the methode dismissView2
for (UIView *subview in view.subviews ){
[subview removeFromSuperview];
}

UIButton and selectors iOS

I have a button constructor class and in that class i have this method to design my buttons
- (void)setupWithPosition:(CGPoint)point withImage:(UIImage *)image withImageFloat:(NSString *)imageFloat withTitle:(NSString *)title withLineWidth:(CGFloat)lineWidth respondsToSelector:(NSString*)selector
{
// create button
ButtonWithImage* button = [[ButtonWithImage alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];
//[button setTitle:[title uppercaseString] forState:UIControlStateNormal];
[button setupViewWithText:title andImage:image andImageFloat:imageFloat];
[button addTarget:self
action:NSSelectorFromString(selector)
forControlEvents:UIControlEventTouchUpInside];
CGRect buttonFrame = button.frame;
CGFloat lineX = 0.0f;
// setup image
if ([[imageFloat lowercaseString] isEqualToString:#"right"]) {
lineX = buttonFrame.origin.x + buttonFrame.size.width;
} else {
lineX = 0.0f;
buttonFrame.origin.x = lineWidth;
button.frame = buttonFrame;
}
self.line = [[UIView alloc] initWithFrame:CGRectMake(lineX, buttonFrame.size.height/2, lineWidth, LINE_HEIGHT)];
self.line.backgroundColor = [UIColor lineNormalColor];
[self addSubview:self.line];
[self addSubview:button];
}
and i'm calling that using:
homepageButtons = [[NSMutableArray alloc] init];
CategoryButtonView* za = [[CategoryButtonView alloc] initWithFrame:CGRectMake(200, 250, 300, 46)];
[za setupWithPosition:CGPointMake(100, 100) withImage:[UIImage imageNamed:#"zas.png"] withImageFloat:#"right" withTitle:#"za" withLineWidth:80.0f respondsToSelector:#"buttonClicked"];
the buttons are placed correctly but when i click on them i get that error
CategoryButtonView buttonClicked]: unrecognized selector sent to instance 0xa1a46a0
2012-11-02 17:53:43.844 Man[3432:14c03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CategoryButtonView buttonClicked]: unrecognized selector sent to instance 0xa1a46a0'
i guess its a SEL problem how can i solve that?
You have this line:
[button addTarget:self
action:NSSelectorFromString(selector)
forControlEvents:UIControlEventTouchUpInside];
Do you really want 'self' here? This means that whatever selector you pass in must be a method in the CategoryButtonView class, not whatever class is used to create the CategoryButtonView.
Most likely you want to add a target parameter along with the selector parameter.
had to add that to make it work
NSString *temp = [NSString stringWithFormat:#"%#:",selector];
[button addTarget:sender
action:NSSelectorFromString(temp)
forControlEvents:UIControlEventTouchUpInside];

UIScrollView with a large number of UIButtons

What I want is a UIView with lots of UIButtons in it. They get placed and arranged according to data stored in an NSArray. Because there are quite a lot of buttons they don't fit on the screen all at once. The user should be able to zoom out to see all the buttons or to zoom in to see details (the label on them) and to easily select them.
I tried two different approaches:
1) I constructed a UIView subclass, put the buttons in it and an instance of this View inside a UIScrollview.
Effect: I can access all Buttons via their tag and scrolling and zooming works fine. BUT I can't get the buttons to handle any events (press on them)...
2) I wrote a UIViewController with exactly the same functionality and added an instance of it to the UIScrollView.
Effect: I can press the buttons now, but scrolling and zooming have stopped to work.
Here the relevant Code of the View:
- (UIView *)initWithArray:(NSArray *)nArray{
self = [super init];
if (self) {
int count = [nArray count];
for (int i=0; i<count; i++) {
UIButton *button = [[UIButton alloc]
initWithFrame:(__some Code to place the Button__);
button.tag = i+1;
NSString *title = [[NSString alloc] initWithFormat:__code for generating Title__];
[button setTitle:title forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:14];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchDown];
[self addSubview:button];
}
}
return self;
}
And the Code for the matrixController:
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *nArray = [[NSMutableArray alloc] __some code___];
int count = [nArray count];
for (int i=0; i<count; i++) {
UIButton *button = [[UIButton alloc]
initWithFrame:CGRectMake(__some Code to place the Button__];
button.tag = i+1;
NSString *title = [[NSString alloc] initWithFormat:__code for generating Title__];
[button setTitle:title forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:14];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:button];
}
}
And the code for the ScrollViewController:
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 768, 970)];
[self.view addSubview:scrollView];
[scrollView setBackgroundColor:[UIColor blackColor]];
//Zooming
[scrollView setMinimumZoomScale:0.25];
[scrollView setMaximumZoomScale:4.0];
[scrollView setDelegate:self];
// constructing the view
[scrollView addSubview:chartView];
[scrollView bringSubviewToFront:chartView];
OR
[scrollView addSubview:[matrixController view]];
How can I get this to work??
I'm able to get a scroll view containing multiple buttons to pan and zoom just fine, with the buttons still handling touch events:
- (void)didTapButton:(UIButton *)button
{
NSLog(#"Button %ld", (long)button.tag);
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.delegate = self;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * 3.0f, scrollView.frame.size.height * 3.0f);
scrollView.maximumZoomScale = 3.0f;
UIView *zoomView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, scrollView.contentSize.width, scrollView.contentSize.height)];
zoomView.backgroundColor = [UIColor whiteColor];
for (NSInteger index = 0; index < 100; index++)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake((scrollView.frame.size.width / 2.0f) - 50.0f, 10.0f + (50.0f * (CGFloat)index), 100.0f, 30.0f);
button.tag = index;
[button setTitle:[NSString stringWithFormat:#"Button %ld", ((long)index + 1)] forState:UIControlStateNormal];
[button addTarget:self action:#selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
[zoomView addSubview:button];
}
[scrollView addSubview:zoomView];
[self.view addSubview:scrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return [scrollView.subviews objectAtIndex:0];
}
EDIT: I said not to rely on tag in my comment below, yet I'm doing in here. :) It's merely so I could log the button number, so that part should be ignored.
for (int i=0; i<10; i++)
{
UIButton *scrollingbutton_outlet = [[UIButton alloc] init];
scrollingbutton_outlet = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *img = [UIImage imageNamed:#"box_normal.png"];
scrollingbutton_outlet.frame = CGRectMake(0, 100, img.size.width, img.size.height);
[scrollingbutton_outlet setTitle:[NSString stringWithFormat:#"%d",i+1] forState: UIControlStateNormal];
scrollingbutton_outlet.tag=i+1;
[buttonArray addObject:[NSString stringWithFormat:#"%d",i+1]];
buttonArray = [[NSMutableArray alloc] init];
[scrollingbutton_outlet setBackgroundImage:img forState:UIControlStateNormal];
[scrollingbutton_outlet addTarget:self
action:#selector(scrollbuttonpress:)
forControlEvents:UIControlEventTouchUpInside];
scrollingbutton_outlet.frame = CGRectMake(img.size.width*i,0, img.size.width, scrollviewoutlet.frame.size.height);
[scrollviewoutlet addSubview:scrollingbutton_outlet];
width = scrollingbutton_outlet.frame.size.width;
height = scrollingbutton_outlet.frame.size.height;
scrollviewoutlet.contentSize = CGSizeMake(width*i+scrollingbutton_outlet.bounds.size.width+5, height);
}

Resources