I have a play button, which when pressed, plays a sound, and then changes the image to a pause button. The button is a property of my cell which is in my table view.
I'm able to access the cell when the user hits the button because I can do this:
UIButton *b = (UIButton *)sender;
myTableViewCell* cell = (myTableViewCell*)[[b superview] superview];
And then I can say
[cell.button setImage:myImage];
But I want to change the image of the button back to a play button once the sound is over. I know about the delegate method
-(void)audioPlayerDidFinishPlaying:
(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"audioPlayerDone");
}
And I do get the NSLog when it is completed, but how could I access the cell and change the image? I don't have the luxury of calling superview twice as I did before as there is no sender in this delegate method. Any help??
The table view represents some content (audio), therefore it makes sense you have a model (object) behind each table view cell.
For each cell you could create an object with 2 or more properties:
- audiofile: path to a file
- is playing: boolean value
The moment an audio file starts playing (button is pressed) you set the boolean value "is playing" to true and you update the table view so a different image is displayed on the button. To figure out which object you need to change state for, use the method -indexPathForCell:. The code will look somewhat like the following for your button press method:
UIButton *b = (UIButton *)sender;
myTableViewCell* cell = (myTableViewCell*)[[b superview] superview];
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
AudioObject *audio = [dataArray objectAtIndex:indexPath.row];
audio.isPlaying = true;
[tableView reloadData];
The moment the audio file stops playing you can check the array with models for any file that is playing and set it to false. Afterwards you update the table view again so the button image changes back to the default one.
An implementation like the one I described above will work fine if only 1 audio file is playing at any time.
Related
I am currently in the cellForRow method where I have a button infront of each row.
CellForRow:
JSONDecoder *decoder=[[JSONDecoder alloc] initWithParseOptions:JKParseOptionNone];
NSMutableArray *json=[decoder objectWithData:myData]; {This has a list of items in the dictionary format}
NSDictionary *dict=[json objectAtIndex:indexPath.row];
NSString *status=[dict valueForKey:#"status key in json"];
"dict" can be represented as:
{
status key in json="approved";
name="abc";
type="resourceName";
},
{
status key in json="rejected";
name="xyz";
type="bus";
},
{
status key in json ="pending";
name="pqr";
type="resource";
}...and so on .
Printing status will give me the status of all the rows.
approved
rejected
pending
But I only need the status of that particular row infront of which I am gonna click the button. This is because I need that status for each row separately to send it to the buttonPressed method when I click the button infront of that row. I don't want to do that in didSelectRow method.
How can I get the indexPath.row of a particular row(on clicking that row) so that I could write the method on click of the button corresponding to that particular row?
You just have to add the button as subview for your cell, and pass indexpath.row as a button tag.Thats it.
I have given the sample
//Create ur cell then do the following
UIButton *objBtn=[UIButton buttonWithType:UIButtonTypeCustom];
[objBtn setFrame:CGRectMake(100,5,50,50)];
[objBtn addTarget:self action:#selector(methodWhichGetsCallOnButtonClick:)forControlEvents:UIControlEventTouchUpInside];
objBtn.tag = indexPath.row;
[cell addSubview:objBtn];
Now in "methodWhichGetsCallOnButtonClick" method add your business logic.
Also take a look on this question.
EDIT:
Also See this link
Try this out, and revert me back if need any more help.
If I am not wrong you want indexPath of cell on button clicked.You can get the cell of that button(in button action) as follows:
UIButton *theButton = sender;
CreatedCell *cell = (CreatedCell *)[[theButton superview] superview];
After getting cell, you can get indexPath of that cell as follows:
theIndexPath = [[m_TableView indexPathForCell:cell] retain];
You can use tag to UIButton.
In cellForRowAtIndexPath Method
You set tag to Button like this
btn.tag=indexpath.row;
In Button Action method you can retrive tag value using this line
Action method for Button
-(void)btnAction:(id)sender
{
UIButton *btn=(UIButton *) sender;
int tagValue=btn.tag;
NSDictionary *dict=[json objectAtIndex:tagValue];
// From this dictionory you cann acces the stauts key value
}
I hope it works for you
I want to launch a method every time when user just highlights a cell (doesn't select it) in UITableView. Could you tell me please, how is it possible to do this ?
I want to do it because I have a custom cell with a pictures on it and I want to change pictures every time when user highlights the cell.
UPD: By highlight I mean that user just highlights a cell and don't release the finger from it. By select I mean when didSelectRowAtIndexPath is being launched (so the user releases the finger from the cell after he presses on it)
How do you envisage the user would 'highlight' a cell rather than 'selecting' one?
In iOS (or any touch based environment really) there is no concept of just highlighting a cell rather than selecting one. The only callback you get when the user touches a cell is didSelectRowAtIndexPath:
It might be worth reading up on the documentation on tables here.
UPDATE:
Ah OK, in that case you want to set the highlightedImage property of the cells imageView a bit like this;
cell.imageView.image = [UIImage imageNamed:#"normal_image.png"];
cell.imageView.highlightedImage = [UIImage imageNamed:#"highlighted_image.png"];
I didnt understand your question..you dont you want to use the method selectRowAtIndexPath?
if you want to execute a method when user select a row:
-you can use the method selectRowAtIndexPath and execute your method.
you can also create an invisible UIButton inside cell and when click in a cell you will click in button and execute your method . . .
From iOS 6.0, UITableViewDelegate has 3 methods to handle cell highlighting:
- tableView:shouldHighlightRowAtIndexPath:
- tableView:didHighlightRowAtIndexPath:
- tableView:didUnhighlightRowAtIndexPath:
You should use them, like in this example:
- (BOOL)tableView:(UITableView*)tableView shouldHighlightRowAtIndexPath:(NSIndexPath*)indexPath
{
return YES;
}
- (void)tableView:(UITableView*)tableView didHighlightRowAtIndexPath:(NSIndexPath*)indexPath
{
MyTableViewCell* cell = (MyTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
cell.myImageView.image = [UIImage imageNamed:#"myCellHigh"];
}
- (void)tableView:(UITableView*)tableView didUnhighlightRowAtIndexPath:(NSIndexPath*)indexPath
{
MyTableViewCell* cell = (MyTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
cell.myImageView.image = [UIImage imageNamed:#"myCellUnhigh"];
}
I have a value called member id and I want to send it to another view controller, If I place the following in didSelectRowAtIndexPath, The value passes to the variable "member".
int memberIndex = [indexPath indexAtPosition: [indexPath length] - 1];
member = [[tableDataSource2 objectAtIndex: memberIndex] objectForKey:#"Memberid"];
If it is placed in the cellForRow, of course it rewrites with every row created. I have a button in each row that launches a viewController, I want the button action to grab the rows "member" and pass it to the new controller. Is there a "didSelectButton at index path method" or a way to grab that on the fly?
any Ideas would be great. It's the first time I'm adding a button to a UiTableview.
Thanks
Why not use the accessory view? It is a built-in button that you can skin with any image you want to give the UI any kind of feel that is required. Then add this to the table's delegate:
accessoryButtonTappedForRowWithIndexPath: (NSIndexPath *) indexPath{ ... }
You will then be able to call your method on your table data source and launch your secondary view.
Just play with "tag".
Each UIView has an attribute tag (int).
in the cellForRowAtIndexPath :
//create yourButton and then
yourButton.tag = memberIndex;
and when you are using an IBAction just get the sender :
- (IBAction) didSelectButton:(id)sender
{
int memberIndex = ((UIButton *)sender).tag;
//
}
tips : to get the sender when you are setting the action property of your button don't forget the ":"
E.G
action = #selector(didSelectButton:);
In my application I'm doing dynamic resizing of cells and label in it depending upon the text in it.
I'm adding button to cells in uitableview.
I'm taking the label instance and button instance in a new label and button variable respectively and setting their frames to arrange them properly after resizing.
if(cel==nil)
{
//some code
original_label=[[UILabel alloc]init];
original_label.tag=111;
//SOME MORE CODE GOES HERE
original_button=[[UIButton alloc]init];
original_button.tag=222;
//SOME MORE CODE GOES HERE
}
new_label=(UILabel *) [cell viewWithTag:111]; //This' how I'm taking the label instance on cell and below button instance on cell in new variables
new_button = (UIButton * ) [cell viewWithTag:222];
Earlier I kept the tags of all the buttons on cells same, so it was easier to get button instances on cells properly and were being arranged properly. But now I want to recognize these buttons separately as I'm adding some functionality on button_click. I'm giving the buttons that are added to the cells incremental tags[1,2,3...9 and so on]. Now, how can I take these button tags in some range like[suppose 1-9]?
Can anybody help?
Thanks in advance.
You can keep the button tags the same as you had before.
Instead, in the button_click method, figure out which row the button is in like this:
- (void)button_click:(UIButton *)button
{
UITableViewCell *cell = (UITableViewCell *)[[button superview] superview];
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
//code to handle this indexPath.section and indexPath.row...
}
This assumes you have added the button to cell.contentView which is what the first superview gets. The second superview gets the cell.
The addTarget for the button should look like this (note colon after button_click):
[original_button addTarget:self action:#selector(button_click:) forControlEvents:UIControlEventTouchUpInside];
I currently have a UITableView that is populated with a custom UITableViewCell that is in a separate nib. In the cell, there are two buttons that are wired to actions in the custom cell class. When I click one of the buttons, I can call the proper method, but I need to know which row the button was pressed in. The tag property for each button is coming as 0. I don't need to know when the entire cell is selected, just when a particular button is pressed, so I need to know which row it is so I can update the proper object.
Much easier solution is to define your button callback with (id)sender and use that to dig out the table row index. Here's some sample code:
- (IBAction)buttonWasPressed:(id)sender
{
NSIndexPath *indexPath =
[self.myTableView
indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
NSUInteger row = indexPath.row;
// Do something with row index
}
Most likely you used that same row index to create/fill the cell, so it should be trivial to identify what your button should now do. No need to play with tags and try to keep them in order!
Clarification: if you currently use -(IBAction)buttonWasPressed; just redefine it as -(IBAction)buttonWasPressed:(id)sender; The additional callback argument is there, no need to do anything extra to get it. Also remember to reconnect your button to new callback in Interface Builder!
You could use the tag property on the button to specify which row the button was created in, if you're not using tags for anything else.
For a implementation that is not dependent on tags or the view hierarchy do the following
- (void)btnPressed:(id)sender event:(id)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:touchPoint];
}
I have the same scenario. To achieve this, I derived a custom cell. I added two properties, section and row. I also added an owner, which would be my derived TableViewController class. When the cells are being asked for, I set the section/row based on the indexPath, along with the owner.
cell.section = indexPath.section
cell.row = indexPath.row
cell.owner = self
The next thing that I did was when I created the buttons, I associate the button events with the cell rather than with the tableViewController. The event handler can read the section and row entry and send the appropriate message (or event) to the TableViewController. This greatly simplifies house keeping and maintenance by leveraging existing methods and housekeeping and keeping the cell as self contained as possible. Since the system keeps track of cells already, why do it twice!
Even easier:
-(IBAction) buttonPressed {
NSIndexPath *myIndexPath = [(UITableView *)self.superview indexPathForCell: self];
// do whatever you need to do with the information
}
Here's a Swift example...
The UIControl class is the superclass of various iOS widgets, including UIButton, because UIControl provides the target/action mechanism that sends out the event notifications. Therefore a generic way to handle this is as follows:
func actionHandler(control: UIControl)
var indexPath = tableView.indexPathForCell(control.superview!.superview! as UITableViewCell)!
var row = indexPath.row
}
Here's an example of setting up a button control to deliver the action. Alternatively, create an #IBAction and create the action visually with Interface Builder.
button.addTarget(self, action: "actionHandler:", forControlEvents: .TouchUpInside)
You can downcast UIControl parameter to UIButton, UIStepper, etc... as necessary. For example:
var button = control as UIButton
The superview of control is the UITableViewCell's contentView, whose subviews are the UIViews displayed in the cell (UIControl is a subclass of UIView). The superview of the content cell is the UITableViewCell itself. That's why this is a reliable mechanism and the superviews can be traversed with impunity.
You can access the buttons superview to get the UITableViewCell that contains your button, but if you just need the row number, you can use the tag property like the previous post deacribes.
There are multiple methods to fix the problem.
You can use the "tag" property
Give the value indexPath.row as the tag value for the button.
btn.tag = indexPath.row;
Then in the button function, you can easily access the tag value and it will be the index for the clicked button.
-(void)btnClicked:(id)sender
{
int index = [sender tag];
}
You can use the layer property
Add the indexPath as the value in the layer dictionary.
[[btn layer] setValue:indexPath forKey:#"indexPath"];
This indexPath is accessible from the button action function.
-(void)btnClicked:(id)sender
{
NSIndexPath *indexPath = [[sender layer] valueForKey:#"indexPath"];
int index = indexPath.row;
}
With this method you can pass multiple values to the button function just by adding new objects in the dictionary with different keys.
[btnFavroite setAccessibilityValue:[NSString stringWithFormat:#"%d",indexPath.row]];
[btnFavroite setAccessibilityLabel:btnFavroite.titleLabel.text];
[btnFavroite addTarget:self action:#selector(btnFavClick:) forControlEvents:UIControlEventTouchUpInside];
-(void)btnFavClick:(id)sender{
UIButton *btn=(UIButton *)sender;
int index=[btn.accessibilityValue integerValue]]
}
this solution also works in IBAction connected using storyboard cell prototype
- (IBAction)viewMapPostsMarker:(UIButton*)sender{
// button > cellContentView > cellScrollView > cell
UITableViewCell *cell = (UITableViewCell *) sender.superview.superview.superview;
NSIndexPath *index = [self.mapPostsView indexPathForCell:cell];
NSLog(#" cell at index %d",index.row);
}