UISlider inside a custom cell

I have a customcell which was defined in IB, the cell contains among other things a UISlider. I want to be able to set the UIslider value when the table loads and to save the value into an array if the slider is changed, but I just can't figure out how to do this.

In my tableviewcontroler I have:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CustomCellIdentifier = @"CustomCellIdentifier ";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
cell = (CustomCell *)[nib objectAtIndex:0];
}
NSUInteger row = [indexPath row];
cell.Qtext.text = [QuestionList objectAtIndex:row];
cell.answerlabel.text=[AnswerList objectAtIndex:row];

float val = [[AnswerList objectAtIndex:row] floatValue];
cell.slider.value=val;

return cell;
}


This works just fine for the label and the textview but does nothing for the Slider

Then in customcell.m I have:

-(IBAction)sliderChanged:(id)sender
{
slider=(UISlider *)sender;
int progressAsInt=(int)(slider.value+0.5f);
NSLog(@"slider value is =%i",progressAsInt);

switch (progressAsInt) {
case 0:
labelValue=@"NA";
labelValue2=@"0";
break;
case 1:
labelValue=@"None";
labelValue2=@"1";
break;
case 2:
labelValue=@"Inadequate";
labelValue2=@"2";
break;
case 3:
labelValue=@"Fair";
labelValue2=@"3";
break;
case 4:
labelValue=@"Good";
labelValue2=@"4";
break;
case 5:
labelValue=@"Excellent";
labelValue2=@"5";
break;
}

sliderLabel.text=labelValue;
}



Which also works just fine, but I haven't a clue how to save the slider value to an array in the tableview controller.

Can anyone help, ideally with some simple code I can pop into my app so that I can set and get the slider value.

Message was edited by: marklo

Message was edited by: marklo

MacBook, PC

Posted on Feb 10, 2010 6:25 AM

Reply
6 replies

Feb 11, 2010 3:37 AM in response to marklo

No answers hmm, someone must have an idea. After several hours of searching on the web and fiddling with code I've come up with a partial answer.

If I set the sliders tag in cellForRowAtIndexPath by adding
cell.slider.tag = nTag++;

and then set up an IBaction in my tableview controller it shows up in the first responders outlets in IB if I link it to the sliders touches up inside event I can nslog the slider value and the tag number.


- (IBAction)readSlider:(id)sender {

UISlider *theSlider=sender;
int progressAsInt=(int)(theSlider.value);
int tagValue=theSlider.tag;
NSLog(@"slider value is =%i",progressAsInt);
NSLog(@"slider tag is %i",tagValue);

}


But there are a few issues just yet. The Tag increments every time a new cell is drawn and doesn't correspond to the cell row number, also I don't seem to be able to set the slider in any of the cells except the first one. Well it's progress at least.

Feb 11, 2010 5:00 PM in response to marklo

marklo wrote:
The Tag increments every time a new cell is drawn and doesn't correspond to the cell row number

You're on the right track, but probably just need to use a tag number that conveys more information. Since I'm not clear on your overall objective, I'll just summarize a couple ideas in hopes one or both will lead you in a useful direction:

1) When the cells are created (inside the if-cell-is-nil branch of cellForRowAtIndexPath) give both the cell and its slider the same tag number. I.e. you can increment a static int nTag as you're doing now, but give the cell that same number. When the slider calls the action method, it will then be easy to find the cell and thus the index path. The data source can be an array of dictionaries and the keys can be NSIndexPath objects, or (my preference for storing in a plist) a compound array indexed by section no. and row no.;

2) Make helper methods that encode and decode an index path to/from an int. E.g. if there's only one section, encode by just adding the row number to a base number, kSliderTag. For multiple sections encode with something like tag=kSection*section rowkSliderTag. The decode is then something like section=tag/kSection and row=tag%kSection-kSliderTag (But please check my math, I'm typing a little too fast to know if it's right). Use [NSIndexPath indexPathForRow:row inSection:section] to get an index path object. The structure of the data source can be the same as in no. 1. kSection should be a large number, e.g. 0x10000 allows for 64k sections and 64k rows. The slider tag number will be the encoded index path, and should be assigned to the slider outside of the creation branch. When setting the slider tag, you can identify the slider if you know its position in the cell's subview array. E.g., if it was added as the base subview of cell.contentView in the creation branch:

UISlider slider = [[cell.contentView subviews] objectAtIndex:0];

also I don't seem to be able to set the slider in any of the cells except the first one.

Not sure what's going on with that, but I think we can assume you simply didn't have the correct address for the other sliders. Either of nos. 1 or 2 above should allow you to map a slider 1:1 to an index path or a dictionary key, so you shouldn't have any more problems finding the slider address you want. That said, I always recommend lots of logging when the methods you're calling don't seem to do anything. E.g.:

// method for setting each cell's slider in cellForRowAtIndexPath
- (void)setSliderForCell:(UITableViewCell*)cell {
// using above no. 1 so slider has same tag as parent cell
UISlider *slider = (UISlider*)[cell.contentView viewWithTag:cell.tag];
// sanity check: watch out for nil slider
NSLog(@"section=%d row=%d slider=%@",
indexPath.section, indexPath.row, slider);
// get the slider value from the data source, an array of section
// dictionaries, each of which includes an array of row dictionaries
NSDictionary *sectDict = [self.tableData objectAtIndex:indexPath.section];
NSArray *rows = [sectDict valueForKey:kRows];
NSDictionary *rowDict = [rows objectAtIndex:indexPath.row];
NSString *sliderValue = [rowDict valueForKey:kSliderValue];
slider.value = [sliderValue floatValue];
NSLog(@"slider.value=%2.0f", slider.value);
}

Finding and setting cell subviews comes up a lot, and the best solution tends to depend on the structure of your code. If the above isn't helpful, please describe a little more about what you're trying to do, and maybe someone around here will be able to hit the target a little better.

- Ray

Feb 12, 2010 4:13 PM in response to RayNewbie

Thanks for the help Ray, I need to sit down, digest it and incorporate it into my code now.

As for the
also I don't seem to be able to set the slider in any of the cells except the first one.
That was pair of noob errors, firstly I had forgoted to link up the slider in IB, secondly I was using dummy data for the sliders which wasn't being updated, once I figured out both of these I got the sliders updating just fine 🙂.

It does look like the answer is close, all I need to be able to get the row number from the tablecell by associating the cell and slider tags to be able to put the value in the right place in my nsmutablearray, all the rest is already in place.

Feb 14, 2010 7:39 AM in response to marklo

Hmm still stuck. I can set the slider value from my array and also set the tags successfully. I'm also able to change the slider value and use it within the cell.

I'm just not managing to get the tag or save the slider value to an array.


- (IBAction)readSlider:(id)sender {
CustomCell *cell;
UISlider *theSlider=sender;


int progressAsInt=(int)(theSlider.value);
int tagValue=theSlider.tag;
NSLog(@"slider value is =%i",progressAsInt);
NSLog(@"slider tag is %i",tagValue);
NSLog(@"celltag is", cell.tag);

}


When the above code runs I get the slider value ok, but get a 0 for the slider tag and a BAD_EXEC for the cell tag.
What I want to add in here is code to read the slider value, get the rowindex from the tags and then save it to an NSMutablearray .I can do the first and last bit but just can't figure out the middle bit 🙂.

Feb 17, 2010 1:03 AM in response to marklo


- (IBAction)readSlider:(id)sender {
CustomCell *cell; // <-- not set to the address of a cell
UISlider *theSlider=sender;
int progressAsInt=(int)(theSlider.value);
int tagValue=theSlider.tag; // <-- this is correct
NSLog(@"slider value is =%i",progressAsInt);
NSLog(@"slider tag is %i",tagValue); // <-- if this prints 0, the tag was never set
NSLog(@"celltag is", cell.tag); // <-- cell is a random address => crash
}

marklo wrote:
When the above code runs I get the slider value ok, but get a 0 for the slider tag and a BAD_EXEC for the cell tag.

The BAD_EXEC is easy to explain, since the cell variable was never set to the address of a cell. The above code to read and print the slider tag is correct, so it appears that tag wasn't set correctly.

Since your cells are made from a custom class, the reason the slider tags aren't being set could either be in your nib or in the cellForRowAtIndex (cell factory) data source method. For example, if an IBOutlet for the slider isn't connected in the nib, then cell.slider.tag=tag++ will fail in the cell factory because the 'slider' ivar will be nil.

Here's some sample code which tags the slider of each custom cell object and uses the tag to update an array of slider values:

// CustomCell.h
#import <UIKit/UIKit.h>
@interface CustomCell : UITableViewCell {
IBOutlet UILabel *nameLabel;
IBOutlet UISlider *slider;
}
@property (nonatomic, retain) UILabel *nameLabel;
@property (nonatomic, retain) UISlider *slider;
@end
// The nib for each cell consists of the custom cell, a label subview, and a slider subview, each of
// which is connected to the outlets defined above. In addition, the Value Changed event of the
// slider is connected to the myAction connector of File's Owner. Since File's Owner is set to 'self'
// in the CellsViewController implementation, the Value Changed event of each slider instance will
// fire the myAction method of the controller object
// CustomCell.m
#import "CustomCell.h"
@implementation CustomCell
@synthesize nameLabel;
@synthesize slider;
- (void)dealloc {
[nameLabel release];
[slider release];
[super dealloc];
}
@end
// CellsViewController.h
#import <UIKit/UIKit.h>
#define kTableViewRowCount 30
#define kTableViewRowHeight 66
@interface CellsViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate> {
UITableView *theTableView;
NSMutableArray *sliderValues;
}
@property (nonatomic, retain)IBOutlet UITableView *theTableView;
@property (nonatomic, retain)NSMutableArray *sliderValues;
- (IBAction)myAction:(id)sender;
@end
// CellsViewController.m
#import "CellsViewController.h"
#import "CustomCell.h"
@implementation CellsViewController
@synthesize theTableView;
@synthesize sliderValues;
- (IBAction)myAction:(id)sender {
// NSLog(@"myAction: sender=%@", sender);
UISlider *theSlider = sender;
CustomCell *cell;
NSUInteger row = -1;
for (cell in [theTableView visibleCells]) {
// NSLog(@" cell=%@", cell);
if (cell.slider.tag == theSlider.tag) {
row = [[theTableView indexPathForCell:cell] row];
break;
}
}
if (row >=0 && row < kTableViewRowCount)
[self.sliderValues replaceObjectAtIndex:row
withObject:[NSNumber numberWithFloat:cell.slider.value]];
NSLog(@"myAction row=%d slider.tag=%d slider.value=%2.2f",
row, cell.slider.tag, cell.slider.value);
}
- (void)viewDidLoad {
[super viewDidLoad];
// initialize the slider data
self.sliderValues = [NSMutableArray arrayWithCapacity:kTableViewRowCount];
for (int iRow = 0; iRow < kTableViewRowCount; iRow++)
[self.sliderValues addObject:[NSNumber numberWithFloat:0.0]];
}
- (void)dealloc {
[theTableView release];
[sliderValues release];
[super dealloc];
}
#pragma mark -
#pragma mark Table Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView*)tableView
numberOfRowsInSection:(NSInteger)section {
return [sliderValues count];
}
- (UITableViewCell)tableView:(UITableView)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {

static NSString *CustomCellIdentifier = @"CustomCellIdentifier";
static NSUInteger nTag = 100;

CustomCell *cell = (CustomCell*)[tableView
dequeueReusableCellWithIdentifier:CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell"
owner:self options:nil];
cell = [nib objectAtIndex:0];
if (nTag <= 100)
NSLog(@"nib=%@", nib);
cell.slider.tag = nTag++;
NSLog(@"new cell: identifier=%@ slider.tag=%d",
[cell reuseIdentifier], cell.slider.tag);
}
int row = indexPath.row;
cell.nameLabel.text = [NSString stringWithFormat:@"Row %d", row];
cell.slider.value = [[self.sliderValues objectAtIndex:row] floatValue];
NSLog(@"cellForRowAtIndexPath: row=%d slider.tag=%d slider.value=%2.2f",
row, cell.slider.tag, cell.slider.value);
return cell;
}
#pragma mark -
#pragma mark Table Delegate Methods
- (CGFloat)tableView:(UITableView*)tableView
heightForRowAtIndexPath:(NSIndexPath*)indexPath {
return kTableViewRowHeight;
}
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
NSLog(@"didSelectRowAtIndexPath: row=%d", [indexPath row]);
CustomCell *cell = (CustomCell*)[tableView cellForRowAtIndexPath:indexPath];
UISlider *slider = cell.slider;
NSLog(@"Selected row=%d slider.tag=%d slider.value=%2.2f",
indexPath.row, slider.tag, slider.value);
}
@end

The example is based on the "Cells" project in Chapter 8 of +Beginning iPhone 3 Development: Exploring the iPhone SDK+ by Dave Mark and Jeff LaMarche. I can't recommend a better first book for iPhone developers. The projects in Chapter 8 will explain almost everything you need to successfully use table views in your apps.

Hope that helps!
- Ray

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

UISlider inside a custom cell

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.