Cast NSString to UIButton type - iphone

Im trying to cast a string to a button type. Basically, Im looping through, say 5 buttons, named btn1,btn2..btn5. Here's the snippet:
- (IBAction)replaceImg
{
UIButton* b;
for(int i=0; i<5; i++)
{
b = (UIButton*)[NSString stringWithFormat:#"btn%d",i]; //1!
if([b isHighlighted])
{
int imgNo = (arc4random() % 6) + 1;
UIImage *img = [UIImage imageNamed:[NSStringstringWithFormat:#"%d.png", imgNo]];
[b setImage:img forState:(UIControlState)UIControlStateNormal];
}
}
}
The line marked 1 is giving a problem, if I swap it with b = btn1, it works perfectly. Please help!
I couldnt find a way to access a button by its name either. Like UIImage has something like imageNamed.

you can't cast NSString to UIButton because both are completely different type.
Use tag property of UIView to assign and unique number to each UIButton and at any point of time you could access them by using viewWithTag .

You can't 'cast' an NSString to a button. To get specific buttons, or buttons by name, depends on how you created them. Store your created buttons into an array, or if they come from a NIB then gather their pointers into an array, then loop through the array of button pointers. Controls are also UIViews, so you can assign a numeric 'tag' to each button in Interface Builder then use UIView's viewWithTag: method to search for a specific view with a specific tag.

An NSString isn't a UIButton, so casting it to one isn't going to work. Well, it might work as far as syntax goes, but logic-wise, it will fail. You simply cannot interact with an NSString the same way you can a UIButton. If you need to find your button by name, then you can either retain pointers to those buttons (either in an array, a map, or just as plain instance variables, to name a few ways), or you could alternatively use something like viewWithTag:.

Related

How To point to an object from string

My question is kind of general; in my project I have 10 UIButton objects
named Button1, Button2, Button3, Button4, Button5, Button6, Button7, Button8, Button9, and Button10
I also have UIImageview That are named exactly like the buttons from 1 to 10.
I want to write a code that will manipulate the image by the last character of the button (always a number from 1 to 10) and will affect the UIImageview the same way
Something like this
buttonlastcharacter = i;
if(sender.lastcharacternumber is:i){
Button%,i.frame = //Some manipulation
But basically all that I want is to have access to a certain object by string
How can I implement such a behavior?
There are a couple of better ways to do this. If these buttons are all static and in IB you can use an IBCollection array for image views and buttons to simply call them up by matching indexes.
Better yet just use the tag value for the buttons or image views.
It is maybe not the ideal solution in your case, but you can do it different ways:
using kvo
UIButton* myButton = [self valueForKey:[NSString stringWithFormat:#"button%i",i]];
or with selectors and properties
UIButton* myButton = [self performSelector:NSSelectorFromString([NSString stringWithFormat:#"button%i",i])];
Hm, you could use an array for your buttons and a tag for your UIImageView objects. They all inherit from UIView, which provides you with a .tag propterty. It is of type NSInteger* .
For convenience reasons I would suggest to name the buttons from 0 to 9. It does not really matter but the first index in the array would be 0 and therefore naming them accordingly just makes things easier.
Define
NSArray *buttonArray;
You may opt for NSMutableArray depending what else you may want to do with it.
In viewDidLoad code:
buttonArray = [NSArray arrayWithCapacity:10];
buttonArray[0] = button0;
..
buttonArray[9] = button9;
In your XIB file in Interface Builder, or whereever you may create the UIImages programmatically, add the tags accordingly.
image0.tag = 0;
...
image0.tag = 9;
assuming you name them image0 to image9.
In your appropriate action method code:
buttonArray[sender.tag] = someManipulation;
You can do it like this,
In your IBAction method:
- (IBAction)click:(id)sender
{
UIButton *but = (UIButton *)sender;
but.frame = your manipulation code;
}
or you can check the title like:
if([but.currentTitle isEqualToString:#"Button1])
{
//Manipulate button 1
}
if you have added tags for the buttons from 1-10 you can use,
if(but.tag == 2)
{
//Manipulate button 2
}

Adding arguments to selector in addTarget

I'm having trouble adding arguments to the selector of a button (programmatically created). I've looked around the internet and tried some things, but I can't figure it out.
I create a button with the following line:
NSString *someThing = [[NSString alloc] initWithString:#"someThing"];
int counter = 4;
[anotherButton addTarget:self action:#selector(alertPressed:) forControlEvents:UIControlEventTouchUpInside];
I've got the function alertPressed:
-(void)alertPressed:(id)sender {
}
How can I transfer those two variables to alertPressed?
You can subclass UIButton, with a custom button that contains those attributes. Then, your (id)sender can be cast to your custom button and you can obtain the set values.
Why not global variables or properties in your class?

Is it possible to add a second tag to an UIbutton?

I was wondering if it is possible to add a second tag to a UIButton? I've created a number of buttons programatically in a for-loop and need a reference to the number of the button (e.g. 0, 1, 2) and another reference (integer) in which I store a reference to the page the button links to (e.g. 22, 30, 49). The numbers are not related so I can't determine the first through the second.
This is what I'd like to have:
for (int k=0; k < numberOfTabs; k++) // k < 4 (e.g. 3 < 4)
{
UIButton* btn = [[[UIButton alloc] initWithFrame:frame] autorelease];
btn.tag = k;
btn.tag2 = someReference;
btn.frame = CGRectMake(-10, 0, buttonWidth, buttonHeight);
[btn addTarget:self
action:#selector(tabAction:)
forControlEvents:UIControlEventTouchUpInside];
[btn addTarget:self
action:#selector(tabDelete:)
forControlEvents:UIControlEventTouchDragOutside];
/...
Any suggestions would be very much appreciated.
No you cant. Not directly at least. You can subclass UIButton and add another int property but that seems like an overkill for what you want..
I think it would be better to just decide on how you can fit both the values in the single tag integer...
e.g. if you have pageNumber and buttonNumber you can create the buttons tag like:
button.tag = pageNumber*100 + buttonNumber;
and when you want to which page a button belongs to or what is the index of a button on a page, you can dacode the tag:
int pageNum = button.tag /100;
int buttonNum = button.tag % 100;
Create a subclass of UIButton with your second tag declared as property.
You could as well create an array to map the tag of your button to a page, which would prevent creating a subclass but will introduce some array management method.
I'd prefer the array solution, as I try to prevent subclassing whenever I can.
Why not store the second (any more, if needed) parameters in something like an NSMutableArray?
NSMutableArray *button_to_page = [[NSMutableArray alloc] init];
...
for(...)
{
// Your button creation code
[button_to_page addObject:[NSNumber numberWithInt:my_button.tag];
}
You can get your page number at any time by simply indexing into the button_to_page array.
You can also search the array for a page number and get the button index (if needed).
Now, having said that, here you are creating a new NSNumber object for each button's page tag and also carrying around an NSMutableArray to boot. I really think that subclassing UIButton is the way to go. I don't like the idea of encoding stuff into the single tag unless there's a real compelling reason. If you subclass you are still keeping the UIButton pretty lightweight and all your data is encapsulated within the same object very cleanly:
MultiTag_UIButton.h
#import <Foundation/Foundation.h>
#interface MultiTag_UIButton : UIButton {
}
#property (nonatomic, assign) int page;
#end
MultiTag_UIButton.m
#import "MultiTag_UIButton.h"
#implementation MultiTag_UIButton
#synthesize page;
#end
It really is that simple, you don't have to write any code, just add the page property and you are off to the races. Then you can do this:
MultiTag_UIButton *test_button = [[MultiTag_UIButton alloc] init];
test_button.tag = 1;
test_button.page = 23;
NSLog(#"tag %i page %i", test_button.tag, test_button.page);
[test_button release];
Clean and simple. Realistically you'd have to do a little more in the new class, but you get the idea.
According to what I can see looking at Apple's documentation for UIView where the tag property is defined (since UIButton inherits from UIView), it appears you can only have the one.
There's nothing stopping you from subclassing UIButton to add another tag property if necessary as mentioned.
You could subclass, but then when you handle it (if you intermix it with non subclassed buttons), you are going to have to ask the object if it is is the new object type or implements the new accessor to get at it, which is a bit unpolymorphic.
What about if you just leave the class as is, but partition up the existing bits of the tag so that the lower 16 bits are for one purpose and the upper are for your other purpose? Nothing changes interface wise, you just do some masking on the .tag to get your values.

UIImage and UIImagePicker and UIImageView

I have a UISegmentedControl and I have some photos, my initial aim is when ever I click on a particular segment one particular image should appear on the view.
For example: If i have four segments and four images then upon each segment I click I must see a image.
Here I have taken an NSArray and Uploaded all these images using this particular below code:
NSArray * images = [NSArray arrayWithObjects:[UIImage imageNamed:#"f.ppg"], [UIImage imageNamed:#"t.ppg"....etc ], nil];
and after this I want to attach each particular image to a segment index. So here is the code which I wrote.
-(IBAction) segmentActionChanged:(id)sender
{
int segment = mSegment.selectedSegmentIndex;
mImageView.image=[images objectAtIndex:segment];
}
While compiling the application I am not getting any Images displayed and it is quiting abruptly.
Any help regarding this.
To Answer questions 1 and 3 (I think).
to display an image, you will need a UIImageView somewhere. a simple way to do this would be to create an method such as this.
//somewhere in viewDidLoad perhaps?
//title array is an array of strings to use as the label on each section of your control
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:titleArray];
//this sets the segmentAction: method as the selector for your particular UISegmentedControl
//(so when its value is changed, segmentAction: gets called)
[segmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
//the segmentAction: method
- (IBAction) segmentAction:(id)sender
{
//This assumes you have declared the imageView elsewhere, as well as the array and that you
//are using Interface Builder to create/hookup the segmentControl. Basically
myImageView.image = [myImageArray objectAtIndex:sender.selectedSegmentIndex];
}
A few Notes: this does not take advantage of lazy loading, since you are creating and holding all of your images in an array prior to showing them. I would suggest creating an instance variable for the UISegmentedControl so you can access it anywhere also.
Disclaimer: this code is not complete and assumes some initializations.
To answer question number two:A UIImagePickerController is used when dealing with the phone's/devices camera or saved photos album. It is covered in the documentation.
Looks like you're missing a retain.

UIButton inside UITableViewController

I have a UIButton that is created inside of each table cell. I want to hook up a touch event like so:
[imageButton addTarget:self
action:#selector(startVote:)
forControlEvents:UIControlEventTouchUpInside];
I want to pass data about the current row (the id of the object for that row) to the startVote method. Is there a method that I am missing to do this or am I breaking some best practice. This seems like a very normal thing to do?
I assume you have some sort of NSArray with the data that gets passed on to the buttons in cellForRowAtIndexPath.
Try this in startVote:
- (void)startVote:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDictionary *myData = [myArray objectAtIndex:indexPath.row];
}
EDIT:
If for some reason the row is not selected, you can assign a unique tag to every button upon creation and then:
- (void)startVote:(id)sender {
int myTag = [(UIButton *)sender tag];
NSDictionary *myData = [myArray objectAtIndex:myTag];
}
Maybe you would do some sort of operation with the tag so it can be used as an index (I add a certain amount to every tag so it will not conflict with "automatic" tagging used by the OS.
The UITableViewCell doesn't know, out of the box, what row it's displaying in the table. Remember, the intent is that the same cell instances are re-used all over the table to display its data. That said, your UITableViewController is responsible for setting up the cells and passing them to the system (and has the index path, of course). You could, at that point, do something like:
Assuming it's a custom cell class, set a property on the cell instance to identify what row it's displaying, and which your button can later use.
If you're putting these buttons in the cells as their accessory views, take a look at the table delegate's tableView:accessoryButtonTappedForRowWithIndexPath: method.
If it's a one-section table, you could do something really cheesy like store the row index in the button's tag property. Your startVote: method is passed the button, and could then extract its tag.