UITableViewCell button to expand text in a cell - how to do this? - iphone

I've got a UITableView with a list of 10 sections, each containing a textLabel.
I've limited the amount of text in each cell to 10 lines and put a button to allow to user to expand the text in the cell.
The problem is, I want to change the text on the button when it's clicked between "show more" (if cell is not expanded) and "show less" (if cell is expanded). How can I do that?
Right now I have this code in cellForRowAtIndexPath. It doesnt seem to work correct, sometimes the button displays "show less" when there are only 10 lines displayed.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ButtonCell"];
if (!cell) {
UIButton *readmoreButton = [UIButton buttonWithType:UIButtonTypeCustom];
[readmoreButton setTitle:#"Read More" forState:UIControlStateNormal];
[cell addSubview:readmoreButton];
}
else {
NSEnumerator *e = [cell.subviews objectEnumerator];
id object;
while (object = [e nextObject]) {
if ([object class]==UIButton.class && [((UIButton*)object).titleLabel.text isEqualToString:#"Read Less"])
((UIButton*)object).titleLabel.text = #"Read More";
}
}
The following method is called when the user clicks the button. The NSMutableArray ProposalInfo keeps track of which cell is/is not expanded.
- (void)toggleFullProposal:(id) sender {
// get cell
NSIndexPath *index = [self.tableView indexPathForCell:(UITableViewCell *)[sender superview]];
ProposalInfo *proposalInfo = [self.proposalInfoArray objectAtIndex:index.section];
if ([proposalInfo expanded]==YES) {
[proposalInfo setExpanded:NO];
[(UIButton *)sender setTitle:#"Read More" forState:UIControlStateNormal];
} else {
[proposalInfo setExpanded:YES];
[(UIButton *)sender setTitle:#"Read Less" forState:UIControlStateNormal];
}
// create index paths to reload
NSMutableArray *indexPathsToReload = [[NSMutableArray alloc] init];
[indexPathsToReload addObject:[NSIndexPath indexPathForRow:0 inSection:index.section]];
// refresh
[self.tableView reloadRowsAtIndexPaths:indexPathsToReload withRowAnimation:UITableViewRowAnimationNone];
}

When the button is clicked make sure that now you set the full text to the cell. And then only reload the cell and mind it you need to fix the height of label in layoutSubviews: and then return appropriate height for heightForRowAtIndexPath: as well.
((MyCell*)[tableview cellForRowAtIndexPath:buttonClickedindexPath]).fullText = myModel.fullText;
and in the layoutSubviews: method of the custom class cell expand the frame of the the label so as to fit the text inside, then return the appropriate height in heightForRowAtIndexPath: and that should do the trick.

Related

Unhighlight row returns a max int on indexpath when user scrolls table with a cell highlighted

I have a UILabel as subview of my cell. When user highlight a cell the label text change it's color and when the cell is unhighlighted the color changes back, just like an usual cell textLabel.
The problem is that if the user tap a cell and scroll the table the cell will unhighlight, the unhighlight delegate method will be called but it will return [2147483647, 2147483647] as index path and the text will not change, as there is no cell on that index path.
Here is the code:
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath*)indexPath{
NSInteger section = [indexPath section];
//only change the text on cells at section 1
if (section == 1) {
//get cell with given index path
UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:indexPath];
// retrieve the UILabel by it's tag
UILabel *cellLabel = (UILabel *)[currentCell viewWithTag:1011];
cellLabel.textColor = [UIColor darkGrayColor];
}
}
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath*)indexPath{
NSInteger section = [indexPath section];
if (section == 1) {
UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *cellLabel = (UILabel *)[currentCell viewWithTag:1011];
[cellLabel performSelector:#selector(setTextColor:) withObject:[UIColor lightGrayColor] afterDelay:0.2f];
}
}
How can i retrieve the correct cell to change it's UILabel subview text when the user scrolls table with a cell highlighted?
Testing those methods I had the same strange results for the indexPath (no idea, why these methods are not working properly).
But I would suggest to just write your own UITableViewCell subclass and override the following method (that is called every time a cell gets selected/deselected):
- (void) setSelected : (BOOL) selected animated : (BOOL) animated {
[super setSelected: selected animated: animated];
// your customisations to your UILabel
if (selected == NO) {
self.textLabel.textColor = [UIColor lightGrayColor];
}
}
Hope this answer helps you out with your problem

Getting a button press from a custom UITableViewCell's button

I have a project with a custom UITableViewCell (cell.h/.m/.xib) that has 2 labels (labelMain/labelSub), and 2 buttons on it (buttonName/buttonInfo) linked to 2 actions (showName/showInfo).
What I want to do is be able to access the 2 actions in my projects main viewcontroller so that when showName is pressed, a textfield.text in the viewcontroller (not in the cell) is set to that specific cell's labelMain.text.
Hope that makes sense. My problem is that if I write the action (showName) in the cell.m, I cannot access the textfield from my main viewcontroller. On the flip side, if I write the action in my viewcontroller, how do I know which button inside which cell was tapped?
Hope this makes sense...
Use tag can identify which button in which cell is being tapped.
- (UITableViewCell *)tableView:(UITableView *)tableViews cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//init identifier
if (cell ==nil)
{
//load nib file
}
buttonName.tag = indexPath.row;
[buttonName addTarget:self action:#selector(showName:) forControlEvents:UIControlEventTouchUpInside];
buttonInfo.tag = indexPath.row;
[buttonInfo addTarget:self action:#selector(showInfo:) forControlEvents:UIControlEventTouchUpInside];
}
}
-(void) showName:(UIButton*)button{
int row = button.tag; //you know that which row button is tapped
}
-(void)showInfo:(UIButton*)button{
int row = button.tag;//you know that which row button is tapped
}
---------------- EDIT -------------
If you need to know which button is pressed based on row & section, you may try this below. (in cellForRowAtIndexPath: method)
int tag = (indexPath.row+1)+(indexPath.section*100);
buttonName.tag = tag;
When button at
row = 5, section = 0 then tag = 6.
row = 4, section = 3 then tag = 305.
row = 0, section = 11 then tag =1101.
limitation, row cannot be more than 99. and DON'T use positive tag for other view. if you need use tag, try negative. (-1, -9, -100).
so from here, you can calculate back row & section of indexPath. using this :
-(NSIndexPath*)getIndexPathFromTag:(NSInteger)tag{
/* To get indexPath from textfeidl tag,
TextField tag set = (indexPath.row +1) + (indexPath.section*100) */
int row =0;
int section =0;
for (int i =100; i<tag; i=i+100) {
section++;
}
row = tag - (section*100);
row-=1;
return [NSIndexPath indexPathForRow:row inSection:section];
}
how to use :
-(void)showInfo:(UIButton*)button{
NSIndexPath *indexPath = [self getIndexPathFromTag:button.tag];
int row = indexPath.row;
int section = indexPath.section;
}
Try this in viewController.m file in cellForRowAtIndexPath method
[[customCell showNameBtn] setTag:indexPath.row];
[[customCell showNameBtn] addTarget:self action:#selector(showName:) forControlEvents:UIControlEventTouchDown];
-(void) showName:(id)sender
{
// check here the sender tag and then perform you action what you want.
}
I suggest to use delegate(protocol) method for this. You will be having full control on the button, cell and the view controller. And its very simple too
The simplest solution in my opinion:
In your CustomCell.h :
#property (nonatomic, copy) void (^buttonTapAction)(id sender);
In your CustomCell.m create the IBAction and connect it to your UIButton :
-(IBAction)cellButtonIsPressed:(id)sender{
if (self.buttonTapAction) {
self.buttonTapAction(sender);
}
}
Then in the ViewController.m where your UITableView is managed :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* cellIdentifier = #"customCell";
CustomCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.buttonTapAction = ^(id sender){
//Do what you want to do when the button inside THIS cell is clicked
};
}
Try to create the button dynamically, with its methods in same controller. Do this in cellForRowAtIndexPath: method:
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"WorkRequestedCC" owner:self options:nil];
{
for (id oneObject in nib) if ([oneObject isKindOfClass:[WorkRequestedCC class]])
cell = (WorkRequestedCC *)oneObject;
}
UILabel *costLbl=[[UILabel alloc]initWithFrame:CGRectMake(792, 13, 10, 15)];
costLbl.text=#"$";
[costLbl setBackgroundColor:[UIColor clearColor]];
UITextField *txtCost=[[UITextField alloc]initWithFrame:CGRectMake(805, 10, 94, 27)];
txtCost.backgroundColor=[UIColor whiteColor];
[txtCost setKeyboardType:UIKeyboardTypeNumberPad];
txtCost.text=obj.expCost;
[txtCost addTarget:self action:#selector(textChanged:) forControlEvents:UIControlEventEditingChanged];
[cell.contentView addSubview:costLbl];
[cell.contentView addSubview:txtCost];
cell.lblDes.text=obj.expDes;
}
- (void)textChanged
{
//your code here
}

Strange bug in tableView

I have a UITableView, I can add and delete cell to this table. I also have two buttons. 1 button adds "1" to the cell's text, Which is 1 so basically it counts when pressing the + button and subs when pressing the - button. My problem is with the very last cell. If i add 5 cells, its the 5th cell that has the problem. If i add 200 cells its the 200th cell, etc. The problem is when i press the - button, all the other cells keep turning blue when pressed, and this button stops turning blue. It stays white when i press it when the cells text is 0. I want it to keep turning blue like all the other cells when pressed. Here is my code:
- (IBAction)subtractLabelText:(id)sender
{
cell = (UITableViewCell*)[sender superview];
if ( [[cell.textLabel text] intValue] == 0){
[newBtn setEnabled:NO];
}
else{
cell.textLabel.text = [NSString stringWithFormat:#"%d",[cell.textLabel.text
intValue] -1];
[newBtn setEnabled:YES];
}
}
This method is hooked up to the sub "-" button. Also, when i press the button when the text is = 0, the button is there but when i press it, it selects the cell and the table cell turns blue as if i selected that! Please help! Thanks everybody!
cellForRow:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.imageView.image = [imageArray objectAtIndex:indexPath.row];
cell.textLabel.text = [cells objectAtIndex:indexPath.row];
newBtn = [[UIButton alloc]init];
newBtn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[newBtn setFrame:CGRectMake(260,20,55,35)];
[newBtn addTarget:self action:#selector(subtractLabelText:)
forControlEvents:UIControlEventTouchUpInside];
[newBtn setTitle:#"-" forState:UIControlStateNormal];
[newBtn setEnabled:YES];
[cell addSubview:newBtn];
subBtn = [[UIButton alloc]init];
subBtn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[subBtn setFrame:CGRectMake(200,20,55,35)];
[subBtn addTarget:self action:#selector(addLabelText:)
forControlEvents:UIControlEventTouchUpInside];
[subBtn setTitle:#"+" forState:UIControlStateNormal];
[subBtn setEnabled:YES];
[cell addSubview:subBtn];
return cell;
}
Based on your cellForRowAtIndexPath method, you are creating and adding new buttons to each cell regardless of whether they are being reused or 'brand new'.
Your code is doing something like this:
Check if there is a cell to be dequeued/reused
If yes, grab a reference to it. If no, create a new one.
Add buttons XYZ to the cell (your mistake is here)
Return the cell for the tableview to use
Imagine that in step 2, if you a reusing an existing cell, this cell already has the buttons XYZ added to it so you are essentially adding two extra button to it when you didn't need to.
You should move the code that sets up the cell buttons to inside the if (cell == nil) loop so the buttons are only created and added to the content view if you are initialising a new cell object.
Also make sure you look at the additional comments below which are not directly related to your question but may be relevant nonetheless.
==================================================================
[EDIT - ANSWER ABOVE, BUT THIS IS ALSO RELEVANT SO I LEFT IT HERE]
This will not solve your problem, but you should be adding your buttons to the cell's contentview instead of directly to the cell, i.e.:
[cell.contentView addSubView:buttonABC];
Once that's done, you will need to call superview twice to get the cell reference in your subtract/add method:
cell = (UITableViewCell*)[[sender superview] superview];
From Apple's documentation on UITableViewCell
The content view of a UITableViewCell object is the default superview
for content displayed by the cell. 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.
At a guess, I'd say it has to do with how the UITableViewCells are being reused, in that the UI doesn't 'know' about the subview buttons on those cells.
Try commenting out the code in -cellForRowAtIndexPath that dequeues the reusable cells (so in effect you're always creating a new UITableViewCell regardless of whether cell==nil or not).
Edit:
You're referencing newBtn in -subtractLabelText, but this is an ivar that doesn't necessarily refer to the button that sent the message. Try [sender setEnabled:NO] instead.

Updating UITableViewCell dynamically

How can one change the textLabel or the detailTextLabel of a specific UITableViewCell dynamically?
Basically, I am downloading a set of files from the internet whenever the user taps on a cell. Now instead of the progress indicator, I would like to display the name of the file currently being downloaded. I would like to keep updating the detailTextLabel to show the (current) filenames until all the files are downloaded.
Assuming you're in a method like tableView:didSelectRowAtIndexPath: (or have another way to get the index path to the cell), you can do something like this:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UILabel *label = nil;
for(UIView *subview in cell.contentView.subviews) {
if([subview isKindOf:[UILabel class]]) {
label = (UILabel*)subview;
break;
}
}
if(label)
label.text = #"New text";
This will find the first UILabel in the cell, and may not work if your cell has more than one label. A more robust way to do it is to set the tag of the label when you create the cell (this can be done in Interface Builder or in code). Then you can find the label like this:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UILabel *label = nil;
for(UIView *subview in cell.contentView.subviews) {
if([subview isKindOf:[UILabel class]] && [subview tag] == kDetailLabelTag) {
label = (UILabel*)subview;
break;
}
}
if(label)
label.text = #"New text";
Just in case anyone still has this problem I have had success with calls to the reloadData method. For example in my code in the viewWillAppear method, I have the line:
[tableSettings reloadData];
Where tableSettings is the name of my UITableView.

Button placement on table footer view

I have a table view wherein the number cells are not fixed and will keep on changing. I have to show two action buttons after my last cell in the table footer. How can I place them properly after my last cell? I know the pixel spacing between last cell and these buttons. Any code sample will be really helpful.
Have you tried sticking them in a view and setting that as the footer view?
You need to give it the correct height before you add it; the width is automatically set to the width of the table. Either set it to a sensible width (e.g. 320) and use autoresizing or use a custom UIView subclass and implement -layoutSubviews.
You could always add two buttons to the final cell.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [myCellDataArray count]+1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.row > [myCellDataArray count]) {
cell = [tableView dequeueReusableCellWithIdentifier:#"Button Cell"];
if (cell == nil) {
UIButton *firstButton = [[UIButton alloc] init];
//customization
UIButton *secondButton = [[UIButton alloc] init];
//customization
[cell addSubView:firstButton];
[cell addSubView:firstButton];
}
} else {
// normal stuff
}
If you want to customize existing buttons you need to set it's tag to something unique, i.e. firstButton.tag = 100 and then set firstButton by firstButton = (UIButton *)[cell viewWithTag:100];. Make sure you define firstButton so that it's in scope!
Hope this helps!