Inside my cellForRowAtIndexPath method, I would like to dynamically create some buttons and place them next to each other in the cells. The problem I'm running into is that the sizeToFit method always puts the x coordinate at the 0 position. Is there a way to offset the position to put it next to the previous button?
Instead of this: . I get this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
if ([indexPath section] == 0) {
UIButton *buttonClean = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonClean addTarget:self
action:#selector(aMethod:)
forControlEvents:UIControlEventTouchDown];
[buttonClean setTitle:#"Clean Cup" forState:UIControlStateNormal];
[buttonClean sizeToFit];
[cell addSubview:buttonClean];
UIButton *buttonAroma = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonAroma addTarget:self
action:#selector(aMethod:)
forControlEvents:UIControlEventTouchDown];
[buttonAroma setTitle:#"Aroma" forState:UIControlStateNormal];
[buttonAroma sizeToFit];
[cell addSubview:buttonAroma];
You should position UIViews by setting their frame property.
This is an option:
[buttonAroma setFrame:CGRectMake(buttonClean.frame.size.width,0,buttonAroma.frame.size.width,buttonAroma.frame.size.height)];
you should also add your views to the cell's contentView, instead of adding them to the cell directly:
[cell.contentView addSubview:buttonAroma];
See UIView's frame property here:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instp/UIView/frame
See UITableViewCell's contentView property here:
http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewCell_Class/Reference/Reference.html#//apple_ref/occ/instp/UITableViewCell/contentView
.. where it says:
If you want to customize cells by
simply adding additional views, you
should add them to the content view so
they will be positioned appropriately
as the cell transitions into and out
of editing mode.
It's not the sizeToFit method that's putting them at y=0, it's the fact that you never set any other position. You can move each button by assigning its center or frame properties; for the latter, you would of course want to read out the property, modify just the origin of the CGRect, and set it back to the property so as to not mess up the sizing.
Related
I have a UITableView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//Where we configure the cell in each row
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
self.myButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.myButton.tag = 5;
self.myButton.frame = CGRectMake(190.0f, 5.0f, 70.0f, 25.0f);
self.myButton.layer.borderColor = [UIColor blackColor].CGColor;
self.myButton.layer.borderWidth = 0.5f;
self.myButton.backgroundColor = [UIColor grayColor];
[self.myButton setTitle:#"Set Active" forState:UIControlStateNormal];
[self.myButton addTarget:self action:#selector(myButton:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview: self.myButton];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
-(void) myButton:(id)sender{
[sender setBackgroundColor:[UIColor redColor]];
}
Assume, there 3 rows in the tableview section. I have the button in 3 rows.
At the first instance, the button in the first row should have red color and the other 2 rows should have gray color.
If I select the button in the 2nd row, then the button in the 2nd row should be red and the other 2 gray and so on. I haven't figured out a way to do it. Any suggestions?
I am able to change the color of the button I have selected from cell. But at the same instance, the previously selected button and all the other buttons(except the selected button) should be default color. At a time only one button will be highlighted, and that will always be the selected button at that instance.
I think the right way to do this - subclass UITableViewCell and declare own protocol that will inform delegate in which cell button was pressed, and in the delegate - loop through all cells and set default color for all and red color for selected.
I made demo project
Easy way is set different tag for each button. Like this:
button.tag = indexPath.row + c;
when 'c' is constant.
And in you method:
-(void) myButton:(id)sender{
for( int i=0; i<numberOfRowsInTable; ++i )
[(UIButton *)[self.view viewForTag:i+c] setBackgroundColor:[UIColor grayColor]];
[(UIButton *)sender setBackgroundColor:[UIColor redColor]];
}
But if you have 2 or more section in table this way may be not work
Take a look at this: How can I loop through UITableView's cells?
Loop through the cells and set the "inactive" buttons to gray.
Just try this code:
-(void) myButton:(id)sender{
NSLog(#"sender Tag=== %d",[sender tag]);
UIButton *btn=(UIButton *)sender;
[btn setBackgroundColor:[UIColor redColor ]];
}
I am making a simple timer app. I have a uitable with multiple rows. In that table I have put a UIButton. Everything works and so far so good i.e. I see the button appear in each row. What I needed to do next was to set an image for the UIButton e.g. if the timer was running I wanted to show stop_button_image in the UIButton and if the timer had started I wanted to set the UIImage on the button to start_button_image
The problem is that if the table gets refreshed then I get a nasty traceback depending upon where I had put the UIButton setimage code. Here is my code for reference.
//WORKING CODE: (NOT THAT I WANT)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//TESTING - Button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil)
{
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//TESTING - Button
[button addTarget:self
action:#selector(timerStopStart:)
forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Start/Stop" forState:UIControlStateNormal];
button.frame = CGRectMake(5.0f, 5.0f, 72.0f, 77.0f);
button.tag = indexPath.row;
[cell addSubview:button];
//THIS WORKS IF I PUT THIS HERE
[button setImage:[UIImage imageNamed:#"button_pause.png"] forState:UIControlStateNormal];
}
else
{
//NSLog(#"went here 2");
button = (UIButton *) [cell viewWithTag:indexPath.row];
}
//some other logic
}
//CODE BREAKS HERE
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//TESTING - Button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil)
{
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//TESTING - Button
[button addTarget:self
action:#selector(timerStopStart:)
forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Start/Stop" forState:UIControlStateNormal];
button.frame = CGRectMake(5.0f, 5.0f, 72.0f, 77.0f);
button.tag = indexPath.row;
[cell addSubview:button];
}
else
{
//NSLog(#"went here 2");
button = (UIButton *) [cell viewWithTag:indexPath.row];
}
//CODE BREAKS HERE
[button setImage:[UIImage imageNamed:#"button_pause.png"] forState:UIControlStateNormal];
//some other logic
}
The problem is that when the table gets refreshed it calls the section with code button = (UIButton *) [cell viewWithTag:indexPath.row]; which I know is just a reference.
Here is the error I get in iOS simulator. I really want to put that image code outside the cell==null check. Any clues how this can be done?
Here is the error that I get in the simulator
2012-06-14 12:39:27.246 Timers[5381:10a03] -[UITableViewCell setImage:forState:]: unrecognized selector sent to instance 0x73706e0
2012-06-14 12:39:27.247 Timers[5381:10a03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCell setImage:forState:]: unrecognized selector sent to instance 0x73706e0'
*** First throw call stack:
(0x150b052 0x16bbd0a 0x150cced 0x1471f00 0x1471ce2 0x136c6 0x7b5e0f 0x7b6589 0x7a1dfd 0x7b0851 0x75b322
The problem is probably when the indexPath.row is zero. In that case, you call viewWithTag:0 but as the tag of any UIView is 0 by default, there are chances that a lot of your cell's subview have a tag with the value 0, not only your UIButton.
To avoid this issue, try adding a constant (like 100) to your indexPath.row before affecting it to the tag, so that your button at row 0 will have the tag 100 and not 0 (and button at row 1 will have the tag 101, and so on…), avoiding it to be confused with any other subview with tag 0.
Yeah, this will fail for row 0.
button = (UIButton *) [cell viewWithTag:indexPath.row];
will return cell when indexPath.row is 0, because cell.tag is 0.
As a general note, this approach will not work. The first time you reuse a cell the button will have the wrong tag. For example, if you reuse the cell from row 2 in row 18, then the tag on the button will be 2, but the tag you look for is 18. It will return nil.
why not define a constant #define kMyStopStartButtonTag 12345 and use that constant the whole time.
if (cell == nil)
{
…
button.tag = kMyStopStartButtonTag;
[cell addSubview:button];
…
} else {
button = (UIButton *) [cell viewWithTag:kMyStopStartButtonTag];
}
Say, for example, I was wanting to edit the title for the highlighted state, is this possible?
I know it is possible with custom UIButtons, but is it possible to edit the properties of the UITableViewCellAccessoryDetailDisclosureButton?
If not, is it possible to create a custom button and still call a UITableView delegate method?
If so, how would one pass the indexPath from a custom button cell?
Cheers!
You can create a custom UIButton and assign it to the accessoryView property of your cell.
If you want to do sth. really custom for the highlighted state of cells you should consider subclassing UITableViewCell and override setSelected:animated:
give your custom button.tag=indexPath.row and access row no. from button tag
ex:-
UIButton *deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
[deleteButton setFrame:CGRectMake(285, 5, 30, 30)];
deleteButton.contentMode = UIViewContentModeScaleAspectFill;
UIImage *newImage12 = [UIImage imageNamed:#"x.png"];
deleteButton.tag = indexPath.row;
[deleteButton setBackgroundImage:newImage12 forState:UIControlStateNormal];
[deleteButton setBackgroundImage:newImage12 forState:UIControlStateHighlighted];
[deleteButton addTarget:self action:#selector(deleteRemindersMethod:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:deleteButton];
-(void)deleteRemindersMethod:(UIButton *)sender
{
int rowNO=sender.tag;
}
To the best of my knowledge, you'll have to set your own button as the accessory view. There are several ways to get the indexPath and my preferred is to get the touch coordinates using locationInView: and then indexPathForRowAtPoint: to get the actual indexPath that you need:
CGPoint location = [sender locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
(code taken from here)
I have one doubt that how to get other control's value when we select a row or tap on button while having custom tableViewCell.
Suppose I have a TableView Cell with a TextField, a Slider and button. Button has action using:
btn.tag = indexPath.row;
[btn addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
Now I can get button on its action method. I need to get textField's value and slider's value on button tap.
The one option is I create a array and store values for these two control in that array and manage this. But I don't think this is correct way to manage it.
Please navigate me to the currect way to get value of UITableViewCell's subview's value.
Thanks
While creating your textfield and slider, give them a constant tag and then add to cell's contentView
textfield.tag= TEXTFIELDTAG; //TEXTFIELDTAG = 100 or any other constant
[cell.contentView addSubview:textfield];
and then in your buttonTapped: method
-(void)buttonTapped:(id)sender
{
int row = ((UIButton*)sender).tag;
NSIndexPath* indexpath = [NSIndexPath indexPathForRow:row inSection:0]; // in case this row in in your first section
UITableViewCell* cell = [table cellForRowAtIndexPath:indexPath];
UITextField* textfield = [cell.contentView viewWithTag:TEXTFIELDTAG];
}
Whenever you create subviews (button , text field etc. ) for your UITableViewCell don't forget to tagged them.
myTextField.tag = 0;
myTextLabel.tag = 1;
[cell addSubview:myTextField];
[cell addSubview:myTextLabel];
Once you tagged, you could access them using below method.
- (UIView *)viewWithTag:(NSInteger)tag
When you select,you could get the subviews of your UITableViewCell's by using ViewWithTag function of UIView.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableview cellForRowAtIndexPath:indexPath];
UITextField* myTextField = [cell viewWithTag:0];
UILabel* myTextLabel = [cell viewWithTag:1];
}
I have subclassed a UITableViewCell. What I want to have is when I click on the cell, I added a UIView to the bottom of the cell and adjust the height of the cell accordingly. Here's my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCell* cell = (MyCell *) [self.table cellForRowAtIndexPath:indexPath];
UIView * options = [[UIView alloc] initWithFrame:CGRectMake(0, cell.frame.size.height - 27, cell.frame.size.width, 27)];
UIButton* button1 = [UIButton buttonWithType:UIButtonTypeCustom];
[options addSubview:button1];
UIButton* button2 = [UIButton buttonWithType:UIButtonTypeCustom];
[options addSubview:button2];
UIButton* button3 = [UIButton buttonWithType:UIButtonTypeCustom];
[options addSubview:button3];
//did some layout calculation here to position the button
[cell addSubview:options];
}
Here's a video that illustrates the issue
Also I've tried to change the accessoryView of my cell from my custom UIButton to the regular UITableViewCellAccessoryDisclosure but it didn't work
MyCell * cell = (MyCell *)[self.table cellForRowAtIndexPath:temp];
[cell.accessoryView removeFromSuperview];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
If you're curious what I am trying to do, I am trying to create a tweetbot uitableviewcell, where you press the cell/row and it presents you with other options (retweets, etc) and also it was shown animated.
I don't know if I correctly understand what you are trying to do. But if you do want to animate the change of the UITableViewCell's height try to adjust it within - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath and call
[tableView beginUpdates];
[tableView endUpdates];
Put controls into another cell and use
insertRowsAtIndexPaths:withRowAnimation:
to show it and
deleteRowsAtIndexPaths:withRowAnimation:
to hide it.