message sent to deallocated instance issue - iphone

I have been debugging the following issue for the past couple hours and can't seem to come up with a good conclusion.
-[NSIndexPath row]: message sent to deallocated instance 0x506cf90
I am implementing a swipe recognizer on each cell on the UITableView. Whenever a swipe is detected, the code below will be called. What it does is to show/hide a subview in the cell. If the subview already shows then it hides, otherwise it shows. If a subview is already shown on cell 5 and a swipe is detected on cell 3 then we remove that subview first then add the subview on cell 3.
I tried the code below and it seems to works with some cells. However, there is a cell particularly at the bottom of the UITableView in which if I try to hide the subview it gives me the error above. It works just perfectly fine for the other cell, just cells at the bottom. Why is this?
The code is as follows:
- (void)swipe:(UISwipeGestureRecognizer *)recognizer direction:(UISwipeGestureRecognizerDirection)direction
{
if (recognizer && recognizer.state == UIGestureRecognizerStateEnded)
{
// Get the table view cell where the swipe occured
CGPoint location = [recognizer locationInView:self.table];
NSIndexPath* indexPath = [self.table indexPathForRowAtPoint:location];
ConvoreCell* cell = (ConvoreCell *) [self.table cellForRowAtIndexPath:indexPath];
[self.table beginUpdates];
NSLog(#"ROW is %d", global.row);
//removing the options view at the other cell before adding a new one
if (global != nil && global.row != indexPath.row){
[sideSwipeView removeFromSuperview];
[sideSwipeView release];
sideSwipeView = nil;
}
//options already exist, we need to remove it
if (sideSwipeView != nil){
[sideSwipeView removeFromSuperview];
[sideSwipeView release];
sideSwipeView = nil;
slide = NO;
} else {
//options do not exist and therefore we need to add it
NSArray * buttonData = [[NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"Mark Read", #"title", #"mark.png", #"image", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"Track", #"title", #"play.png", #"image", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"Leave", #"title", #"delete.png", #"image", nil],
nil] retain];
NSMutableArray * buttons = [[NSMutableArray alloc] initWithCapacity:buttonData.count];
sideSwipeView = [[UIView alloc] initWithFrame:CGRectMake(0, cell.frame.size.height-25, 320, 25)];
[sideSwipeView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
[sideSwipeView setBackgroundColor:[UIColor colorWithPatternImage: [UIImage imageNamed:#"dotted-pattern.png"]]];
[sideSwipeView setTag:-10];
CGFloat leftEdge = BUTTON_LEFT_MARGIN;
for (NSDictionary* buttonInfo in buttonData)
{
if (!([[buttonInfo objectForKey:#"title"] isEqualToString:#"Mark Read"] && [[[groups objectAtIndex:indexPath.row] unread] intValue] == 0))
{
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
button.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
UIImage* buttonImage = [UIImage imageNamed:[buttonInfo objectForKey:#"image"]];
if ([[[groups objectAtIndex:indexPath.row] tracked] intValue] == 1 && [[buttonInfo objectForKey:#"title"] isEqualToString:#"Track"]){
buttonImage = [UIImage imageNamed:#"pause.png"];
[button setSelected:YES];
} else
[button setSelected:NO];
button.frame = CGRectMake(leftEdge, 0, buttonImage.size.width, buttonImage.size.height);
UIImage* grayImage = [self imageFilledWith:[UIColor colorWithWhite:0.9 alpha:1.0] using:buttonImage];
[button setImage:grayImage forState:UIControlStateNormal];
if ([[buttonInfo objectForKey:#"title"] isEqualToString:#"Mark Read"]){
[button addTarget:self action:#selector(markRead:) forControlEvents:UIControlEventTouchUpInside];
} else if ([[buttonInfo objectForKey:#"title"] isEqualToString:#"Track"]){
[button addTarget:self action:#selector(track:) forControlEvents:UIControlEventTouchUpInside];
} else if ([[buttonInfo objectForKey:#"title"] isEqualToString:#"Leave"]){
[button addTarget:self action:#selector(leave:) forControlEvents:UIControlEventTouchUpInside];
}
[button setTag:indexPath.row];
[buttons addObject:button];
[sideSwipeView addSubview:button];
leftEdge = leftEdge + buttonImage.size.width + BUTTON_SPACING;
}
}
[cell.contentView addSubview:sideSwipeView];
[buttons release];
[buttonData release];
global = indexPath;
slide = YES;
}
[self.table endUpdates];
[self.table deselectRowAtIndexPath:indexPath animated:YES];
}
}
Now the issue here is that the pointer to indexPath somehow got deallocated. I was able to get around this by making a copy of the indexPath instead of just having a reference to the pointer. So I did:
global = [indexPath copy];
for some reason after this method is called it deallocates indexPath and I am not sure who does it.. I think iOS does it... is it true?

NSIndexPath* indexPath = [self.table indexPathForRowAtPoint:location];
That returns an autoreleased instance. So, yes, your assignment to global needs to retain the object. copy effectively returns a retained instance.
Don't leak, though. If you just do:
global = [indexPath copy];
And you don't release the original value of global first, you'll leak.
Oh, and: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

Related

UISegmentedControl not updating view

I am building an app in Xcode 5, and I ran into some strange behavior of UISegmentedControl.
first some info on the app i'm building:
I'm building an app in which I want to allow users to order products at registered companies. As an extra service, I want to allow them to see the orders they did, and even to filter the orders on their status: All orders, Active orders & Delivered orders. the orders are displayed in a UITableView, and I created a UISegmentedControl inside the header view to filter the orders. when the selectedSegmentIndex of that UISegmentedControl changes, it executes an NSPredicate to filter the array and display only the desired orders.
now it's working all fine, except for 1 thing: The UISegmentedControl I have does not update it's view when I select another segment. It's default selectedSegmentIndex is 'Active', because users are probably most interested in the active orders, but when I change it to 'All', the tableview displays all orders (so the predicate is working), but the view remains on the same selectedSegmentIndex.
I did a lot of research to solve this but no answer solves my problem.. my code:
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if(section == 0) {
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f,0.0f, 320, 45)]; // x,y,width,height
//label
UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320, 20)];
title.text = #"Kies een filter...";
[headerView addSubview:title];
//segmentedcontrol
NSArray *itemArray = [NSArray arrayWithObjects: #"Alle", #"Actief", #"Afgehandeld", nil];
control = [[UISegmentedControl alloc] initWithItems:itemArray];
[control setFrame:CGRectMake(0.0f, 20.0f, 320.0, 35.0)];
control.userInteractionEnabled = YES;
control.tintColor = [UIColor blackColor];
[control setEnabled:YES];
[control addTarget:self action:#selector(changeFilter:) forControlEvents:UIControlEventAllEvents];
[headerView addSubview:control];
//label containing selected filter info
UILabel *info = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 55.0f, 320, 20)];
if ([selectedFilterInfo isEqualToString:#"Alle"]) {
info.text = #"Alle orders:";
}
else if ([selectedFilterInfo isEqualToString:#"Actief"]) {
info.text = #"Alle actieve orders:";
}
else if ([selectedFilterInfo isEqualToString:#"Afgehandeld"]) {
info.text = #"Alle afgehandelde orders:";
}
[headerView addSubview:info];
headerView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"bar.png"]];
return headerView;
}
}
and the action it triggers:
- (void)changeFilter:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
if (segmentedControl.selectedSegmentIndex == 0) {
selectedFilterInfo = #"Alle";
filteredArray = nil;
[segmentedControl reloadInputViews];
[segmentedControl setSelectedSegmentIndex:0];
}
else if (segmentedControl.selectedSegmentIndex == 1) {
NSPredicate *deliveredpredicate = [NSPredicate predicateWithFormat:#"SELF.delivered contains[c] %#", #"0"];
NSMutableArray *notDeliveredArray = [NSMutableArray arrayWithArray:[sortedArray filteredArrayUsingPredicate:deliveredpredicate]];
filteredArray = [NSMutableArray arrayWithArray:notDeliveredArray];
selectedFilterInfo = #"Actief";
[segmentedControl reloadInputViews];
[segmentedControl setSelectedSegmentIndex:1];
}
else if (segmentedControl.selectedSegmentIndex == 2) {
NSPredicate *deliveredpredicate = [NSPredicate predicateWithFormat:#"SELF.delivered contains[c] %#", #"1"];
NSArray *deliveredArray = [sortedArray filteredArrayUsingPredicate:deliveredpredicate];
filteredArray = [NSMutableArray arrayWithArray:deliveredArray];
selectedFilterInfo = #"Afgehandeld";
[segmentedControl reloadInputViews];
[segmentedControl setSelectedSegmentIndex:2];
}
[self.tableView reloadData];
}
I did not include the numberOfCellsInSection etcetera because the tableview part is working perfectly. the only thing that's not working is updating the view.
Any solution would be really appreciated, Thank you all in advance!!
reloadData will reload the tableView, and when you reload the tableView all headerViews will be reloaded too. When the tableView was reloaded there is a new header. And the UISegmentedControl you see now is not the one that you have tapped.
Create an ivar that holds your selected index
#implementation .. {
NSInteger selectedIndex;
}
when creating the view restore the saved index
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
/* ... */
control = [[UISegmentedControl alloc] initWithItems:itemArray];
control.selectedSegmentIndex = selectedIndex;
/* ... */
}
save the index when changing the segmentedControl
- (void)changeFilter:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
selectedIndex = segmentedControl.selectedSegmentIndex;
if (segmentedControl.selectedSegmentIndex == 0) {
selectedFilterInfo = #"Alle";
filteredArray = nil;
}
/* ... */
// reloads the table and all header views!
[tableView reloadData];
}
And btw. when you use UISegmentedControls there is no need to call setSelectedSegmentIndex: on it again, the tap sets the index already. And reloadInputViews is totally useless for a UISegmentedControl. But I guess this code was just added because it didn't work. Don't forget to remove it ;-)

ALAsset images issue

My code parts in two:
The first is the "cellForRowAtIndexPath" method for my table view. In this table view i'm supposed to show 3 buttons in each cell and each button is a different image. Images are in an NSMutableArray call "photosArray". It looks like this.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (photosArray.count > 0) {
for (int i = 0; i < 3; i++) {
if (photosArray.count > photoCounter) {
//position the UiButtons in the scrollView
CGRect frame = CGRectMake((i*100) + 30, 5, 60, 60);
//Create the UIbuttons
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTag:i];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
button.frame = frame;
button.layer.borderWidth = 2;
button.layer.cornerRadius = 5;
button.clipsToBounds = YES;
//Add images to the button
NSLog(#"PhotosArray count is: %d",photosArray.count);
if ([[photosArray objectAtIndex:0] isKindOfClass:[UIImage class]]) {
NSLog(#"UIImage class true");
}
if ([[photosArray objectAtIndex:0] isMemberOfClass:[UIImage class]]) {
NSLog(#"UIImage class true");
}
UIImage *btnImg = [self.photosArray objectAtIndex:photoCounter];
//UIImage *btnImg2 = [UIImage imageNamed:#"GameOver"];
photoCounter++;
[button setBackgroundImage:btnImg forState:UIControlStateNormal];
[cell.contentView addSubview:button];
}
}
}else{
UILabel *noImages = [[UILabel alloc] initWithFrame:CGRectMake(0, 25, 320, 20)];
noImages.text = #"No images";
noImages.textAlignment = NSTextAlignmentCenter;
[cell.contentView addSubview:noImages];
}
return cell;
}
The second part is where I load my pictures into "photosArray". I am using WSAssetPicker to load multiple photos from my asset library.
Here is the code:
- (void)assetPickerController:(WSAssetPickerController *)sender didFinishPickingMediaWithAssets:(NSArray *)assets
{
// Dismiss the WSAssetPickerController.
[self dismissViewControllerAnimated:YES completion:^{
arep = [[ALAssetRepresentation alloc] init];
for (ALAsset *asset in assets) {
UIImage *imageA = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
[self.photosArray addObject:imageA];
}
NSLog(#"%d",photosArray.count);
photoCounter = 0;
[self.tableView reloadData];
}];
}
And now for the issue, the buttons that are created have no image in it. I only get a transparent button with a border and rounded corners.
Why is that?
It was recently brought to my attention that I omitted an important piece from the README (which is now updated).
It is necessary to hold a strong reference of WSAssetPickerController if you are accessing ALAsset objects in the dismissViewControllerAnimated:completion: completion block.
If you do not hold a strong reference to the picker controller before calling dismissViewControllerAnimated:completion: the picker controller will be released before the completion block is executed resulting in the ALAssetsLibrary (used internally in the WSAssetPicker code) from being released before you try to access the ALAssets in the assets array passed in the assetPickerController:didFinishPickingMediaWithAssets: delegate method.
If ALAssetsLibrary gets released, the ALAssets owned by the ALAssetsLibrary will expire and calls to asset.defaultRepresentation.fullScreenImage will return null.
The following will preserve the ALAssetsLibrary while dismissing the picker controller's view:
- (void)assetPickerController:(WSAssetPickerController *)sender didFinishPickingMediaWithAssets:(NSArray *)assets
{
// Hang on to the picker to avoid ALAssetsLibrary from being released.
self.picker = sender;
__block id weakSelf = self;
// Note: Instead of `id` you could specify the class of `self` order to access properties with dot notation.
// Dismiss the WSAssetPickerController.
[self dismissViewControllerAnimated:YES completion:^{
// Do something with the assets here.
// Release the picker.
[weakSelf setPicker:nil];
}];
}
An alternative would be to process the array of ALAssets before calling dismissViewControllerAnimated:completion:, but that could cause the picker controller's dismissal to delay resulting in a poor user experience.

clear uibutton title and reset again

I Follow the comment and try to fix my problem,but still not working.
when i run the test demo on the simulator,i get this:
run the demo project view
and i click the test2, i want to change button title before clear the button title, but i
get this :
after i click test2 button
i can not clear the button title when click another button.
anyone can help ??
Here is my code
-(void)addbutton
{
for (int i=0; i<3; i++)
{
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake((i*100), 0, 100, 100)];
button.titleLabel.text = #"";
[button setTag:i];
[button setTitle:[self.demoDataArray objectAtIndex:i] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.backgroundColor = [UIColor clearColor];
[button addTarget:self action:#selector(setButtonTitle:) forControlEvents:UIControlEventTouchUpInside];
[self.demoView addSubview:button];
}
}
-(IBAction)setButtonTitle:(id)sender
{
if ([sender tag] == 0)
{
self.demoDataArray = [[NSArray alloc] initWithObjects:#"test5", #"test6", #"test7", #"test8", nil];
[self addbutton];
}
else if([sender tag] == 1)
{
self.demoDataArray = [[NSArray alloc] initWithObjects:#"test9", #"test10", #"test11", #"test12", nil];
[self addbutton];
}
else if([sender tag] == 3)
{
self.demoDataArray = [[NSArray alloc]initWithObjects:#"test13", #"test14", #"test15", #"test16", nil];
[self addbutton];
}
}
Every time any button is pressed, you instantiate new buttons and add them on top of the other ones. You probably want to just update the existing buttons. Try this for your -addButton method instead:
-(void)addbutton
{
for (int i=0;i<3;i++)
{
NSInteger tag = i+1;
UIButton *button = (UIButton *)[self.view viewWithTag:tag];
if (!button)
{
button = [[UIButton alloc] initWithFrame:CGRectMake((i*100), 0, 100, 100)];
[button addTarget:self action:#selector(setButtonTitle:) forControlEvents:UIControlEventTouchUpInside];
[button setTag:tag];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.backgroundColor = [UIColor clearColor];
[self.demoView addSubview:button];
}
button.titleLabel.text = #""; // Not actually sure you need this...
NSString *titleText = nil;
if (i < self.demoDataArray.count)
titleText = [self.demoDataArray objectAtIndex:i];
[button setTitle:titleText forState:UIControlStateNormal];
}
}
Now the button is instantiated, given a target and action, tagged, and added into the button hierarchy only when it doesn't already exist. Every time a button is tapped after that, only the titles will be updated.
Also, very important:: in order for -viewWithTag: to work, you have to use a tag for your buttons that is not equal to 0, the default tag - otherwise the button's superview will be returned. This means you'll need to make the following changes in your button handler, which already had a bug with checking the tags (checking against 3 rather than 2). Increment the tags to start at 1 and end with 3 like so:
-(IBAction)setButtonTitle:(id)sender
{
if ([sender tag] == 1)
{
self.demoDataArray = [[NSArray alloc] initWithObjects:#"test5",#"test6",#"test7",#"test8", nil];
[self addbutton];
}
else if([sender tag] == 2)
{
self.demoDataArray = [[NSArray alloc] initWithObjects:#"test9",#"test10",#"test11",#"test12", nil];
[self addbutton];
}
else if([sender tag] == 3)
{
self.demoDataArray = [[NSArray alloc]initWithObjects:#"test13",#"test14",#"test15",#"test16", nil];
[self addbutton];
}
}
That method could use some cleanup as well to eliminate duplicated code and it's probably more appropriate to use a switch statement here anyways. But that's neither here nor there.
OK, this should probably get you up and running. I didn't test or compile this so if you do have some problem with the compiler that you're not able to work through on your own please let me know and I'll see if I can spot the issue.
try this
- (IBAction)firstbtn:(UIButton *)sender {
[secondbtn setTitle:#"" forState:UIControlStateNormal];
}

How to customize Buttons in UIActionSheet?

I want to change the image of the buttons of UIActionSheet, I have images for each button, and I want each button show the image I want. I searched the web I found a lot of answers but they didn't work.
Here is the initialization of the UIActionSheet
-(IBAction)showActionSheet:(id)sender {
UIActionSheet *popupQuery = [[UIActionSheet alloc] initWithTitle:#"title" delegate:self cancelButtonTitle:#"Cancel Button" destructiveButtonTitle:nil otherButtonTitles:#"Other Button 1", #"Other Button 2", nil];
popupQuery.actionSheetStyle = UIActionSheetStyleDefault;
[popupQuery showInView:self.view];
[popupQuery release];
}
I create a subclass CustomActionSheet derived UIActionSheet, and implement a method called customizeGUI.
I use CustomActionSheet like UIActionSheet, except I must call method customizeGUI of subclass in willPresentActionSheet delegate:
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
if ([actionSheet isKindOfClass:[CustomActionSheet class]]) {
CustomActionSheet *sheet = (CustomActionSheet*)actionSheet;
[sheet customizeGUI];
}
}
(CustomActionSheet.m)
- (void)customizeGUI {
UIImageView *imgvwBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bg_actionsheet.png"]];
CGRect rect = self.bounds;
imgvwBackground.frame = rect;
[self insertSubview:imgvwBackground atIndex:0];
[imgvwBackground release];
int buttonIndex = 0;
UIImage *imgButtonDestructive = [UIImage imageNamed:#"btn_actionsheet_destructive.png"];
UIImage *imgButtonCancel = [UIImage imageNamed:#"btn_actionsheet_cancel.png"];
UIImage *imgButtonNormal = [UIImage imageNamed:#"btn_actionsheet_normal.png"];
for (UIView *sub in self.subviews) {
NSString *className = [NSString stringWithFormat:#"%#", [sub class]];
if ( ([className isEqualToString:#"UIThreePartButton"]) //iOS 4
|| ([className isEqualToString:#"UIAlertButton"]) ) { //iOS 5
rect = sub.frame;
sub.hidden = YES;
UIButton *btn = [[UIButton alloc] initWithFrame:rect];
UIImage *imgForButton = nil;
if (buttonIndex == self.cancelButtonIndex) {
imgForButton = imgButtonCancel;
}
else if (buttonIndex == self.destructiveButtonIndex) {
imgForButton = imgButtonDestructive;
}
else {
imgForButton = imgButtonNormal;
}
NSString *sTitle = [self buttonTitleAtIndex:buttonIndex];
btn.titleLabel.font = [UIFont boldSystemFontOfSize:19];
[btn setBackgroundImage:imgForButton forState:UIControlStateNormal];
[btn setTitle:sTitle forState:UIControlStateNormal];
btn.tag = buttonIndex;
[btn addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:btn];
[btn release];
buttonIndex++;
}
}
}
- (void)buttonClicked:(id)sender {
UIButton *btn = sender;
if ([self.delegate respondsToSelector:#selector(actionSheet:clickedButtonAtIndex:)]) {
[self.delegate actionSheet:self clickedButtonAtIndex:btn.tag];
}
[self dismissWithClickedButtonIndex:btn.tag animated:YES];
}
Hope this way can help you and give you an idea to customize UIActionSheet
If you change these sizes, not only might you make your application less usable, but that may violate Apple's Human Interface Guidelines, leading to a rejection of your application when submitted.
Here is the example how to use UiActionsheet by Apple
Looks like you'll just have to create your own class. I'd look into subclassing either UIView or UIActionSheet. There are those PSD things around that you can get the images you'll need, pending copyright.
EDIT: If you do end up creating your own class, and its any good, consider uploading it to http://cocoacontrols.com/

Uibutton events

-(void)mybuttonclick{
UGFloat p=120;
for(int i=0;i<3;i++){
UIButton *aa = [UIButton buttonWithType:UIButtonTypeCustom];
if(i==0){
NSString *name=[[NSString alloc]initWithString#"ZERO"];
}else if(i==1){
NSString *name=[[NSString alloc]initWithString#"ONE"];
}else if(i==2){
NSString *name=[[NSString alloc]initWithString#"Two"];
}else{
NSString *name=[[NSString alloc]initWithString#"Three"];
}
[aa setFrame:CGRectMake(0.0f, 0.0f, 500.0f, 40.0f)];
[aa setCenter:CGPointMake(100.0f,p)];
[aa setBackgroundColor:[UIColor blueColor]];
[aa addTarget:self action:#selector(fullscreen:) forControlEvents:UIControlEventTouchUpInside];
[self.window addSubview:aa];
p=p+50;
}
}
//----------------fullscreen method--------------------
-(void) fullscreen:(id)sender{
NSLog(#"button pressed on %#",[sender Stringvalue]);
}
Here I created 3 UIButtons dynamically. When I press the first button I want to display ZERO. When pressing the second one, print ONE... then Two, then Three. How can I do this? I know it can be done using selector key work, but I don't know how.
You aren't telling the button about name anywhere. You could set it in the title buttons title with [aa setTitle:name forState:UIControlStateNormal]; then access it in fullscreen: with [sender currentTitle].
If you want the message to be independent of the title you could either make your own subclass of UIButton to store the message or have a different target action for each button.
Also, the way you are constructing the names is fragile. Something like this might be better:
NSArray* names = [NSArray arrayWithObjects:#"ZERO",
#"ONE",
#"Two",
#"Three",
nil];
for (NSString* name in names) {
// Do stuff with name
}