I have a UIViewController with a tableView in it with CustomCells, the CustomCell load a PLAY Button for some of the cells, this button are generated within the CustomCell.m
- (UIButton *)videoButton {
if (!videoButton) {
videoButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
videoButton.frame = CGRectMake(120, 319, 50, 30);
[videoButton setTitle:#"Play" forState:UIControlStateNormal];
[videoButton addTarget:self action:#selector(PlayClicked:) forControlEvents:UIControlEventTouchUpInside];
videoButton.backgroundColor= [UIColor clearColor];
[self.contentView addSubview:videoButton];
}
playIMG.hidden = false;
return videoButton;
}
- (IBAction)PlayClicked:(id)sender {
TableView *tvvc = [[TableView alloc] init];
[tvvc PlayBtnClicked:[NSString stringWithFormat:#"%d", [sender tag]]];
}
in my TableView.m
- (IBAction)PlayBtnClicked:(id)sender {
NSString *tag = [NSString stringWithFormat:#"%#", sender]; //OK
int tagNumber = [tag intValue]; //OK
NSString *Media = [arrayID objectAtIndex:tagNumber]; //Not Getting Result !
NSLog(#"%#", arrayID); //Array is empty when logging it from this action
}
arrayID is NSMutableArray
arrayID = [[NSMutableArray alloc] init];
[arrayID addObject#"123"];
[arrayID addObject#"321"];
[arrayID addObject#"231"];
soo i checked the array by adding an action button in my TableView to log the array and its not empty.
how can i fix the empty array which is actually not empty ?
I solved the problem in much easier way than the delegate thing
removed
[videoButton addTarget:self action:#selector(PlayClicked:) forControlEvents:UIControlEventTouchUpInside];
and the action in the custom cell .m file
- (IBAction)PlayClicked:(id)sender {
TableView *tvvc = [[TableView alloc] init];
[tvvc PlayBtnClicked:[NSString stringWithFormat:#"%d", [sender tag]]];
}
in the tableView.m i added edited the action i want to link to this
- (IBAction)PlayBtnClicked:(UIButton*)button {
NSLog(#"Button Clicked Tag: %d", button.tag);
}
and in the cellForRowAtIndexPath method i added the action to the button in the custom cell
if ([MediaType isEqualToString:#"video"]) {
cell.videoButton.hidden = NO;
cell.videoButton.tag = indexPath.row;
[cell.videoButton addTarget:self action:#selector(PlayBtnClicked:) forControlEvents:UIControlEventTouchUpInside];//here linking the action to the button
cell.playIMG.hidden = NO;
}
now everything is working very well :)
The array isn't your problem - you're breaking the model-view-controller nature of the table view controller and its cells. If you want a control in the cell to call a method in the table view controller, make the table view controller the delegate of the cell, and call the delegate method from the cell's button.
If the code you've posted is correct, then you're creating a new table view instance in the PlayClicked method, which is definitely not the way to do it.
As a minor stylistic point, the Cocoa convention of method naming is camelCase, with a lower-case initial letter.
Related
I am fairly new to objective-c and I have ran into an issue that I have had a hard time solving..
I am using ShinobiDataGrid and using their prepareCellForDisplay.
To get text to display I would do this:
if([cell.coordinate.column.title isEqualToString:#"Task"]){
textCell.textField.textAlignment = NSTextAlignmentLeft;
textCell.textField.text = cellDataObj.task;
}
but for a particular cell, I am trying to add a custom button:
if([cell.coordinate.column.title isEqualToString:#"selected"]){
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 10 , 32, 32);
if(cellDataObj.selected){
[button setImage:[UIImage imageNamed:#"checked-box.png"] forState:UIControlStateNormal];
}else{
[button setImage:[UIImage imageNamed:#"unchecked-box.png"] forState:UIControlStateNormal];
}
button.tag = cell.coordinate.row.rowIndex;
[button addTarget:self action:#selector(CheckBoxPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:button];
[button removeFromSuperview];
}
and I first run the app, the checkbox appears. but when I reload my ShinobiDataGrid the checkbox appears again. I tried removing the button from the superview, but that didn't work...any suggestions ? When I reload my ShinobiDataGrid a 3rd time, the checkbox does not appear for a 3rd time, just adds another when I reload the ShinobiDataGrid the 2nd time. Here is the whole method:
- (void)shinobiDataGrid:(ShinobiDataGrid *)grid prepareCellForDisplay:(SDataGridCell *)cell
{
SDataGridTextCell* textCell = (SDataGridTextCell*)cell;
CellData *cellDataObj = [cellHolderDisplay objectAtIndex:cell.coordinate.row.rowIndex];
textCell.textField.textAlignment = NSTextAlignmentCenter;
if([cell.coordinate.column.title isEqualToString:#"selected"]){
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 10 , 32, 32);
if(cellDataObj.selected){
[button setImage:[UIImage imageNamed:#"checked-box.png"] forState:UIControlStateNormal];
}else{
[button setImage:[UIImage imageNamed:#"unchecked-box.png"] forState:UIControlStateNormal];
}
button.tag = cell.coordinate.row.rowIndex;
[button addTarget:self action:#selector(CheckBoxPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:button];
[button removeFromSuperview];
}
if([cell.coordinate.column.title isEqualToString:#"Task"]){
textCell.textField.textAlignment = NSTextAlignmentLeft;
textCell.textField.text = cellDataObj.task;
}
if([cell.coordinate.column.title isEqualToString:#"BLine. Start"]){
textCell.textField.text = cellDataObj.baselineDate;
}
}
Any suggestions ?
Here is the CheckBoxPressed method:
- (void)CheckBoxPressed:(UIButton *)sender
{
CellData *cell = [cellHolder objectAtIndex:sender.tag];
if([cell selected] == YES)
{
[[cell actualDate]setString:#""];
[[cell finishedDate] setString:#""];
[cell setSelected:NO];
}
else
{
if(([[cell actualDate] isEqualToString:#""]) && ([[cell finishedDate] isEqualToString:#""]))
{
[[cell actualDate]setString:[NSString stringWithFormat:#"%# 8:00:00 AM",[self SetSpecialDateFormat:[NSDate date]]]];
[[cell finishedDate]setString:[NSString stringWithFormat:#"%# 4:00:00 PM",[self SetSpecialDateFormat:[NSDate date]]]];
}
//actual date not populated but finish date populated
else if(([[cell actualDate]isEqualToString:#""]) && !([[cell finishedDate] isEqualToString:#""]))
{
[[cell actualDate]setString:[cell finishedDate]];
}
//finish date not populated but actual date populated
else if(!([[cell actualDate] isEqualToString:#""]) && ([[cell finishedDate] isEqualToString:#""]))
{
[[cell finishedDate]setString:[cell actualDate]];
}
[cell setSelected:YES];
}
[self UpdateEdittedCells:cell];
[self SetDisplayHolder];
//refresh grid
[gridReference reload];
}
(It's not entirely clear what effect you're after here, so I'm assuming that you would like to have a column filled with checkboxes, which are either checked or un-checked according to your data model)
The problem you are encountering is that the shinobi data grid re-uses cells, in order to optimise the memory usage. Therefore adding a button to a cell will mean that the button will be there as the cell is reused (when the grid is reloaded or scrolled).
You could remove the contents of the cell at the beginning of prepareCellForDisplay:, and then you'd have an empty cell to which you can add your button:
- (void)shinobiDataGrid:(ShinobiDataGrid *)grid prepareCellForDisplay:(SDataGridCell *)cell
{
if([cell.coordinate.column.title isEqualToString:#"selected"]){
NSArray *cellSubviews = [cell.subviews copy];
for (UIView *subview in subviews) {
[subview removeFromSuperview];
}
// Now safe to add the button
}
}
However, this isn't the best approach.
You'd be much better creating a custom cell subclass for the selected column. This cell would always have a button, and would therefore make much better use of the cell reuse mechanism.
To do this, create a subclass of SDataGridCell, and register is with the grid, in the same way as you normally would. Then, when you get a callback in prepareCellForDisplay:, you know that the type will be your custom one.
You can find specific instructions on achieving this in the ShinobiDataGrid userguide:
https://www.shinobicontrols.com/docs/ShinobiControls/ShinobiGrids/2.8.0/Standard/Normal/docs/markdown_files/DataGridUserGuide.html#How to: Creating custom cells
The example you're looking for is called "Creating custom cells", and is the last of the "How-tos", right at the bottom of the page.
This example is accompanied with a complete working sample, provided in the samples folder in the dmg that you downloaded. The difference with the sample is that it is using the datasource helper. Your code isn't, but it is fairly simple to translate the populateCell method of the data source helper into the prepareCell method you are used to using.
Question: How do I target one of many dynamically created UIButtons so I can change its properties?
Background: I have a storyboard with a UIViewConroller. When this UIVC loads a UIScrollView is added, to which a UIImageView is placed that has an exhibition floor plan. Each exhibitor has an entry in a database that contains its location on the floor plan. When the UIVC is loaded a loop is run for all exhibitors and each one has a UIButton drawn on the UIIV. When a UIB is clicked the button background colour is changed (to confirm which exhibitor has been selected) and a UIAlertView is shown with information about that exhibitor. When the UIAV's 'cancel' (ok) button is pressed the UIAV closes and the background highlight colour that was applied previously should be removed but here is where I am having the problem. I am unable to target the UIButton so I can change its background colour.
What I have tried so far: As each button is created I am giving it a tag and a title and recording both in an array. When the 'cancel' button is pressed on the UIAlertView I have tried checking the tag in the array but I still cannot actually target the UIButton.
I was thinking something like this:
// obviously not correct syntax but the kind of thing I want
[exhibitorBtn(tag) setBackgroundColor:[UIColor greenColor]];
So, say I have 12 UIButtons all called exhibitorBtn but with different titles and tags:
Object ----- Name ---------- Title -------- Tag
UIButton -- exhibitorBtn -- Glaxo ------ 1
UIButton -- exhibitorBtn -- Porsche --- 2
UIButton -- exhibitorBtn -- Rolex ------- 3 < How would I target that button's properties?
Edit - added the code that creates the buttons just to clarify:
for (NSDictionary *dict in exhibitorStandArray) {
NSInteger currentPosition = [exhibitorStandArray indexOfObject:dict];
NSLog(#"Position in array = %li", (long)currentPosition);
if (![dict[#"locCoords"] isEqual: #""]) {
NSInteger buttonTag = [exhibitorStandArray indexOfObject:dict];
NSString *standNo = dict[#"locStandNo"];
NSMutableDictionary *buttonDictionary = [[NSMutableDictionary alloc] init];
[buttonDictionary setObject:[NSNumber numberWithInteger:buttonTag] forKey:#"buttonTag"];
[buttonDictionary setObject:standNo forKey:#"buttonStandNo"];
[_masterButtonList addObject:buttonDictionary];
NSString *locCoords = dict[#"locCoords"];
NSArray *tempArray =[locCoords componentsSeparatedByString:#","];
tlcTop = [[tempArray objectAtIndex:0] integerValue];
tlcLeft = [[tempArray objectAtIndex:1] integerValue];
brcTop = [[tempArray objectAtIndex:2] integerValue];
brcRight = [[tempArray objectAtIndex:3] integerValue];
buttonWidth = brcRight - tlcLeft;
buttonHeight = brcTop - tlcTop;
testBtn = [UIButton buttonWithType:UIButtonTypeCustom];
testBtn.frame = CGRectMake(tlcTop, tlcLeft, buttonHeight, buttonWidth);
testBtn.titleLabel.text = standNo;
testBtn.tag = buttonTag;
NSLog(#"UIButton Title = %#", testBtn.titleLabel.text);
NSLog(#"UIButton Tag = %li", (long)testBtn.tag);
testBtn.titleLabel.hidden = YES;
[testBtn addTarget:self action:#selector(displayInfo:) forControlEvents:UIControlEventTouchUpInside];
[_buttonArray addObject:testBtn];
[_imageView addSubview:testBtn];
}
}
Why can't you just use viewWithTag:
UIButton * button = (UIButton *)[self.scrollView viewWithTag:tag];
Your code probably looks something like this:
for (int i = 0; i < 5; i++)
{
UIButton *exhibitorBtn = [[UIButton alloc] initWithFrame:etc..];
exhibitorButton.tag = i;
[scrollView addSubview:exhibitorBtn];
}
Just change the loop so every button will be added to an array, too. Declare a NSMutableArray as a property: #property (strong, nonatomic) NSMutableArray *buttonsArray;
And #synthesize and initialise it in your init method. buttonsArray = [[NSMutableArray alloc] init]
Then, change the loop like I said above:
for (int i = 0; i < 5; i++)
{
UIButton *exhibitorBtn = [[UIButton alloc] initWithFrame:etc..];
exhibitorButton.tag = i;
[buttonsArray addObject:exhibitorBtn];
[scrollView addSubview:exhibitorBtn];
}
Finally, when you want to access the buttons:
for (int i = 0; i < [buttonsArray count]; i++)
{
UIButton *button = [buttonsArray objectAtIndex:i];
if (button.tag == 3) { // This is the button you wanted to target!
[button setHidden:YES];
}
}
Let's make it clear: you did set target and action of UIButton to controller's IBAction method and get the button as sender argument. Now you show UIAlertView and after it is dismissed, you want to send some message to that button, right?
Another method is to set delegate property for UIAlertView and respond to – alertView:clickedButtonAtIndex: delegate method. Trouble is that sender is lost at that point. You may use objc_setAssociatedObject to associate UIButton with UIAlertView and the retrieve it back when delegate method fires:
#import <objc/runtime.h>
static char myButtonKey = 0; // to use address as a key (value is irrelevant)
- (IBAction)buttonDidClick:(id)button
{
UIAlertView *alertView = <setup alert>;
[alertView setDelegate:self];
objc_setAssociatedObject(alertView, &myButtonKey, button, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
<show alert>;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
UIButton *thatButton = objc_getAssociatedObject(alertView, &myButtonKey);
<use thatButton>;
}
Try to create button in this way and add selector to them :
for (int i = 0; i < 5; i++)
{
UIButton *button = [[UIButton alloc] initWithFrame:CGRectZero];
button.tag = i;
[button addTarget:self
action:#selector(buttonPressedMethod:)
forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Show View" forState:UIControlStateNormal];
// add button to subview here
}
In method, just do whatever, you want to do :
- (void) buttonPressedMethod : (id) sender {
UIButton *selectedButton = (UIButton *)sender;
if (selectedButton.tag == 0) {
}
else if (selectedButton.tag == 1) {
}
else {
}
}
i have a table view and i places button on a tableview as a checkbox ,now i want to maintain state of checkbox's .my problem is when i am navigate from one view controller to another OR terminating my application all checkbox's are unchecked which was previously checked. any suggestion appreciable.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *str=[NSString stringWithFormat:#"identfier%d",indexPath.row];
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:str];
UILabel *lblValue;
if (cell==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:str];
lblValue=[[UILabel alloc]init ];
lblValue.tag=indexPath.row+20000;
btnValue=[UIButton buttonWithType:UIButtonTypeRoundedRect];
btnValue.tag=indexPath.row+100;
}else{
lblValue=(UILabel*)[cell.contentView viewWithTag:indexPath.row+20000];
btnValue=(UIButton*)[cell.contentView viewWithTag:indexPath.row+100];
}
lblValue.backgroundColor=[UIColor clearColor];
lblValue.text=[[sortedArray objectAtIndex:indexPath.row]valueForKey:#"CategoryName"];
//lblValue.text=#"some value";
UIImage *buttonImage = [UIImage imageNamed:#"radiou.png"];
UIImage *buttonImage1=[UIImage imageNamed:#"radioc.png"];
[btnValue setBackgroundImage:buttonImage forState:UIControlStateNormal];
[btnValue setBackgroundImage:buttonImage1 forState:UIControlStateSelected];
[btnValue addTarget:self action:#selector(checkedButton:) forControlEvents:UIControlEventTouchUpInside];
btnValue.frame=CGRectMake(10, 15, 25, 25);
if([self.navigationItem.rightBarButtonItem.title isEqual:#"Done"]){
[UITableView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveEaseInOut animations:^{
lblValue.frame=CGRectMake(35+25, 17, 200, 20);
btnValue.hidden=NO;
} completion:nil];
}
else
{
[UITableView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveEaseInOut animations:^{
lblValue.frame=CGRectMake(35, 17, 200, 20);
btnValue.hidden=YES;
} completion:nil];
}
[cell.contentView addSubview:btnValue];
[cell.contentView addSubview:lblValue];
return cell;
}
For this, you can make a NSArray containing check box's values. Now when viewWillDissappear is called, you can save this array in NSUserDefaults and on viewWillAppear, you can retrieve the array and from NSUserDefaults and set values in checkboxes accordingly.
Suppose you have 10 rows in your table, then you can make an array with count as 10. Now if 4th and 5th row's checkbox is selected, then you can put 1 at index 3 and 4 and rest of the indexes should be 0.
int arrayCount = 10;//enter number of rows you have
NSMutableArray *checkStatusArray = [[NSMutableArray alloc]init];
for (int i=0; i<arrayCount; i++)
{
if([[checkBoxStatusArray objectAtIndex: i]integerValue] == 1)
// Above mentioned array should be made prior to this method from which checkbox will be loaded or you can make 0 entries here for every indexInitially
{
[checkStatusArray addObject:[NSString stringWithFormat:#"1"]];
}
else
{
[checkStatusArray addObject:[NSString stringWithFormat:#"0"]];
}
}
// To put value
[[NSUserDefaults standardUserDefaults]setObject:checkStatusArray forKey:#"statusArray"];
// Now to fetch value
NSArray *valuesArray = [[NSUserDefaults standardUserDefaults]objectForKey: #"statusArray"];
If you want to keep state in your viewControllers without saving and loading state to persistent storage, you should make your viewControllers properties of their parentViewControllers.
That way they won't be deallocated when you navigate away from them.
You must also remember to only alloc and init them once.
So basically I am making an API call to retrieve a list of Charities. I then place all this in an array and set UIButtons dynamically.
I then allow the user to select the charity and display a view with that index's data.
My loop is here;
for (int i = 0; i < [self.imageArray count]; i++) {
NSDictionary *listRoles = [self.imageArray objectAtIndex:i];
NSString *charityName = [listRoles objectForKey:#"name"];
NSString *charityDescription = [listRoles objectForKey:#"description"];
NSString *charityImage = [listRoles objectForKey:#"image"];
UIImage *pImage=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:charityImage]]];;
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[button setImage:pImage forState:UIControlStateNormal];
[button setTag:i];
[self.scrollView addSubview:button];
I then have a clicked method;
- (void)buttonClicked:(UIButton*)button
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
CharityProfileViewController *cpvc = [storyboard instantiateViewControllerWithIdentifier:#"CharityProfile"];
[self presentViewController:cpvc animated:YES completion:nil];
}
How can I retrieve the index, I know that I could set random tags for the UIButton but how would I still know which one?
You can set the tag value of button as
[button setTag:i+1];
then in Button Action
- (void)buttonClicked:(UIButton*)button
{
NSLog(#"Button Tag : %d",button.tag);
NSLog(#"Selected Index Object : %#",[self.imageArray objectAtIndex:button.tag-1]);
}
You can either retrieve the a tag for your button by button.tag or add your buttons to an array and use then [buttonsArray indexOfObject:button] in your buttonClicked: method
I'm having issues with my button that is located in side my UITableViewCell. I'm using storyboard and connected my button through IB. Within my cellforRowatIndexPath I added an action to my button:
cell.likeBtnPressed.tag = indexPath.row;
[cell.likeBtnPressed addTarget:self action:#selector(userDidTapOnLikeButton:photo:) forControlEvents:UIControlEventTouchUpInside];
Below you will see what is called when the button is pressed:
-(void)userDidTapOnLikeButton:(UIButton *)button photo:(PFObject *)photo{
//Disable the button so users cannot send duplicat requests
[button setEnabled:NO];
[button setTintColor:[UIColor redColor]];
//Set the new state of the button
BOOL liked = !button.selected;
[button setEnabled:liked];
//Get the current number of likes the post have
NSString *originalButtonTitle = button.titleLabel.text;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]];
//Update the like count in the ECDCache
NSNumber *likeCount = [numberFormatter numberFromString:button.titleLabel.text];
if (liked) {
likeCount = [NSNumber numberWithInt:[likeCount intValue] + 1];
[[ECDCache sharedCache] incrementLikerCountForPhoto:photo];
}else{
if ([likeCount intValue] > 0) {
likeCount = [NSNumber numberWithInt:[likeCount intValue] - 1];
}
[[ECDCache sharedCache] decrementLikerCountForPhoto:photo];
}
//Add the current user as a liker of the photo in ECDCache
[[ECDCache sharedCache] setPhotoIsLikedByCurrentUser:photo liked:liked];
//Update the button label
[button setTitle:[numberFormatter stringFromNumber:likeCount] forState:UIControlStateNormal];
//Call the appropriate static method to handle creating/deleting the right object
if (liked) {
[ECDUtility likePhotoInBackground:photo block:^(BOOL succeeded, NSError *error) {
[button setEnabled:YES];
[button setTitleEdgeInsets:UIEdgeInsetsMake(-1.0f, 0.0f, 0.0f, 0.0f)];
[[button titleLabel] setShadowOffset:CGSizeMake(0.0f, -1.0f)];
if (!succeeded) {
// Revert the button title (the number) if the call fails
[button setTitle:originalButtonTitle forState:UIControlStateNormal];
}
}];
}
}
When ever I press the button I receive this:
-[UITouchesEvent objectId]: unrecognized selector sent to instance 0x15ee5de0
I'm not sure what I did wrong
The problem is that the second parameter your method is receiving is not a PFObject, but a UIEvent.
There are three types of selectors you can send into addTarget:action:forControlEvents::
#selector(a): This method takes no parameters.
#selector(a:): This method has one parameter which is the UIControl which received the control event.
#selector(a:b:): This method has two parameters, the UIControl which received the control event, and the UIEvent that triggered it.
Since you are only looking to get the button, you should have a signature like this:
-(void)userDidTapOnLikeButton:(UIButton *)button
{
PFObject *photo = [self someLogicToGetThePhotoFromTheButton:button];
...
you can't add a target with more than one parameter in UIButton selector. When the selector is called receive only the sender parameter, in that case, the UIButton object. So I recommend that you use:
[cell.likeBtnPressed addTarget:self action:#selector(userDidTapOnLikeButton:photo:) forControlEvents:UIControlEventTouchUpInside];
-(void)userDidTapOnLikeButton:(id)sender{
//Your code
}
And then retrieve the photo using the tag of the cell for example.
Good luck ;)