UIButton inside UITableViewController - iphone

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.

Related

how to set hidden property to NO for particular cell in tableView

I am new to iphone.I have a small doubt that is,I have a table view with 66 rows initially i placed a progress view to all rows but in viewdidload i set it to hidden for all those like below in tableViewClass(ShowProgressViewCont)
cell.progressView.hidden = YES;
here (cell) is the the reference of the CustomCell class.In this class i declare a progress view and setter and getter properties also be set here in this class
here in tableview there is a download button in each cell.If we click on that download button(for example in 66th cell).we have to remove the hidden property to the progress view for that particular 66th cell only.The remaining 65 cells should have the progress view in hidden only like that for all cells.
If any body know this please help me....
Are you familiar with the concept of table cells being reused?
viewDidLoad is not an appropriate place to manipulate the content of a single cell. It may work fine if the table is so small that all of its cells fit on the screen (in both orientations).
When there are more cells in the table than beeing displayed on the screen, then those cell that became invisible recently is being reused and displayed again.
So if there are 6 cells on the screen at a time then table cell no. 7 (sometimes 8) will be identical with cell no. 1.
cellForRowAtIndexPath would be a better place to hide/unhide certain views of a cell.
If it is a custom cell already then the cell's layoutSubViews could be appropriate, too. However, only in the tableViewController's cellForRowAtIndexPath you will have easy access to both, the table's data and the associated cell.
cellForRowAtIndexPath is called every time when a cell is about to become visible. Use the default reusing-mechanism to ensure that a proper cell object will be reused. It is pretty much straight forward.
Get the cell at index 65 and then cast it to your custom cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:64 inSection:0]];
YourCustomCell *customCell = (YourCustomCell *)cell;
customCell.progressView.hidden = NO;
First set the row number in your download button in CellForRowAtInedexPath
downloadButton.tag = indexPath.row
[downloadButton addTarget:self action:#selector(actionDownload:) forControlEvents:UIControlEventTouchUpInside];
in actionDownload, make the IndexPath from button.tag and get the cell from "cellForRowAtIndexPath:",
finally update via reloadRowsAtIndexPaths: withRowAnimation:
Hide your progress view in your custom cell means make the default property of every progress view is hidden ,and then your button click method .
CutomeCell *cell = (CutomeCell *)[[button superview] superview];
[cell.progressView setHidden:NO];
NSIndexPath *rowToReload = [[self tableView] indexPathForCell:cell];
NSArray* rowsToReload = [NSArray arrayWithObjects:rowToReload, nil];
[UITableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationNone];
may this help you ...
your model should be tracking the progress for every button clicked. for purposes of this answer, let's say the model is held in a property called modelInformationArray (which would contain an array of objects that relate to each cell in the table)
when the download button is clicked for a cell, modelInformationArray is modified in such a way that its object knows a download is being processed for it.
that object reports the downloading processing in a member called downloadingProcessingStarted. there are many other ways to do that part.
to get to the answer to your question … to then unhide the progress view, you would do something as follows in your UITableViewDataSource implementation of tableView:cellForRowAtIndexPath: .
- (UITableViewCell*)tableView:tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
YourCustomCell* cell = ...
if ([[self.modelInformationArray objectAtIndex:indexPath.row] downloadingProcessingStarted])
cell.progressView.hidden = NO;
}

If I add a UISwitch control to each of my table view cells, how can I tell which cell it belongs to?

I have a UITableView with cells that contain a UISwitch control. It's similar to the table view in the iPhone's Clock app shown below...
(source: epicself.com)
In my app's cellForRowAtIndexPath method, I create and attach the UISwitch control like so...
CGRect frameSwitch = CGRectMake(215.0, 10.0, 94.0, 27.0);
UISwitch *switchEnabled = [[UISwitch alloc] initWithFrame:frameSwitch];
[switchEnabled addTarget:self action:#selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = switchEnabled;
My question is, when the switch is toggled by the user and the switchToggled method is called, how can I tell which table cell it belongs to? I can't really do much with it without knowing it's context.
Thanks so much in advance for your help!
In your action method, you can cast the superview of the passed-in UISwitch (which is the UITableViewCell itself), then pass that to the tableView's indexPathForCell method.
indexPathForCell will return a NSIndexPath object, which you can then use to index to your datamodel to modify. Then all you gotta do is call reloadData on your tableView.
Also, in cellForRowAtIndexPath, you should set the UISwitch's state based on your model.
First of all, fix the memory leak:
UISwitch *switchEnabled = [[[UISwitch alloc] initWithFrame:frameSwitch] autorelease];
Then pick one of these options:
switchEnabled.tag = someInt; // before adding it to the cell
NSLog(#"switched %d",switch.tag); // to see which switch was toggled
or
UITableViewCell *parentCell = switchEnabled.superview;
// + some magic to see which cell this actually is

UISwitch and UITableViewCell iOS4

Caveat: I have looked for the answer to my question and several come close, but I'm still missing something. Here is the scenario:
I want a way to create UITableViewCells that contain UISwitches dynamically at run time, based on the data in a table (which I can do). The problem becomes connecting the switches such that I can get their value when that view is changed (navigated away, closed, etc). I have tried to use the events UIControlEventValueChanged to be notified, but have failed to specify it correctly, because it dumps when that switch is tapped. Also, there doesn't seem to be any way to uniquely identify the switch so that if all the events are handled by a single routine (ideal), I can't tell them apart.
So...
If I have a UITableView:
#interface RootViewController : UITableViewController
{
UISwitch * autoLockSwitch;
}
#property (nonatomic, retain) UISwitch * autoLockSwitch;
-(void) switchFlipState: (id) sender;
#end
// the .m file:
#implementation RootViewController
// ...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"Cell";
int row = 0;
NSString * label = nil;
TableCellDef_t * cell_def = nil;
row = indexPath.row;
cell_def = &mainMenuTableCellsDef[ row ];
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
label = (NSString *) mainMenuTableCellsDef[indexPath.row].text;
[cell.textLabel setText:(NSString *) mainMenuItemStrings[ indexPath.row ]];
if (cell_def->isSpecial) // call special func/method to add switch et al to cell.
{
(*cell_def->isSpecial)(cell ); // add switch, button, etc.
}
else
{
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
}
and this is the 'special' function:
-(void) autoLockSpecialItem :(UITableViewCell *) cell
{
autoLockSwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
[autoLockSwitch addTarget:self action:#selector(switchFlipState:) forControlEvents:UIControlEventValueChanged ];
[cell addSubview:autoLockSwitch];
cell.accessoryView = autoLockSwitch;
}
and finally:
-(void) switchFlipState: (id) sender
{
NSLog(#"FLIPPED");
}
==============================================================
Questions:
Why would it crash (bad selector) when the switch was tapped? I believe that my code follows all the example code that I have seen, but obviously something is wrong.
I cannot put a instance method into a table as a function pointer; and it doesn't seem to like a class method either. If I make it a 'C/C++' function, how do I get access to the class/instance member variables? That is, if I want to put a call to autoLockSpecialItem into a static table (or reasonable facsimile) such that I can get autoLockSwitch member variable? If I make it a class method and the autoLockSwitch var a static, will that be valid?
More simply: how do I connect the UIControlEventValueChanged to my view (I have tried and failed) and can I differentiate at runtime within the event handler which switch has changed?
Is there a better way? I cannot believe that I am the first person to have to solve this type of problem.
Apologies for the length, appreciation for attention and grateful for any and all help.
:bp:
Don't know about why your method isn't connected, but a simple way to "differentiate at runtime within the event handler which switch has changed" is to take the (id)sender given to your event handler, walk your tableview, and compare the sender to any switches, if present, in each table item. If that's too slow, a hash table connecting senders to table cells, or something like that, is a possible optimization.
If you want to use C function pointers, you need to pass the object to the function to use it to call the object's property accessor methods within the function. (Or you could assign the object to a global variable if it's clearly a singleton, but that's a very politically incorrect answer.)
First, and easy way to define your different switches would be defining their tag based on the row number. When one of the switches is tapped you can access sender.tag to get the row number this way.
Also, you should probably be adding the switch the the cells content view, not the actual cell, [cell.contentView addSubview:autoLockSwitch]. Also the frame does need to be set (note CGRectZero, cocoa will ignore the width and height but uses the x,y coords to define where you want the switch in the cell.

How to pass values between two Custom UITableViewCells in iphone

I have two customized cells in an iphone UITableViewController and would like to capture the data in one cell into another.
I am currently unable to post images and please assume the following points.
There are two custom UITableViewcells
Notes Textfield in one customcell
Submit button in another text field.
Now I want to pass data typed in the notes textfield to be available when I click the Submit button. Is this possible? Please help :(
In general you should store your data separate from views. So if you have some table controller there must data objects like array or dictionary or array of dictionaries. And when you click submit you should get associated data object and work with it.
In your case when textfield lose focus you must get textfield value and store it in your data store.
UPD: It not very clean but can help at first
- (void) textFieldDidEndEditing: (UITextField *) textField {
// At first need to get cell. findSuperviewWithClass is custom method to find proper superview object
MyCellClass *cell = (MyCellClass*)[textField findSuperviewWithClass:[MyCellClass class]];
// and indexPath for it
NSIndexPath *index = [tableView indexPathForCell:cell];
// tableData is NSArray of my objects
MyDataObjClass *dataObj = [tableData objectAtIndex:index.row];
dataObj.text = textField.text;
}

iPhone - How to determine in which cell a button was pressed in a custom UITableViewCell

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);
}