I have a TableView that has UISwitches in it. The switches are slightly off the screen. how do I make the display correctly.
Here is my code that displays the switches.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"POICell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(!cell){
//set the cell text to the
cell.textLabel.text = [self.catNames objectAtIndex:[indexPath row]];
NSString *toggle = [self.toggleArray objectAtIndex:[indexPath row]];
//add switch
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
if ([toggle isEqualToString: #"OFF"]) {
[switchView setOn:NO animated:NO];
}else{
[switchView setOn:YES animated:NO];
}
[switchView addTarget:self action:#selector(switchChanged: ) forControlEvents:UIControlEventValueChanged];
// Configure the cell...
}
return cell;
}
Rather than adding a UISwitch to the standard cell, I'd create a custom UITableViewCell that contained the text label and the switch.
But after looking at the Apple docs, it seems what you want to achieve is a valid approach.
Table View Programming Guide for iOS
And your code already looks a lot like this SO question: How to create a UITableViewCell with a UISwitch and get the data? So I am not sure why the UISwitch isn't displaying as expected.
I think the position of the default accessoryView can't be moved without a problem, as the TableViewCell will reposizion it in many situations.
So adding it as a subview might be the better idea, instead of trying to "hack" the default behavior.
Related
I didn't have this problem with iOS6, but am currently having it with iOS7. I have a UITableView and you can see 8 cells at the time the view is loaded. Each populated with different names from and array. If you scroll down, the next two cells look good, but everything past that gets text laid on top of it; That text being the contents of what was in the previous cells. So the 10th cell will have what was in the first cell, as well as what is supposed to be in the 10th cell laid on top of it.
Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
//Create Label for Name inside cell
UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake( 7.0, 5.0, 300.0, 30.0 )];
[name setText:[self.entriesArray objectAtIndex:indexPath.row]];
//Check to see if current person is a Parent or Child
NSString *class = [self.database getCellDataWithMembership:[self.MembershipArray objectAtIndex:indexPath.row] andColIndex:4];
if([class isEqualToString:#"CHILD"])
{
name.textColor = [UIColor colorWithRed:25.0f/255.0f green:111.0f/255.0f blue:45.0f/255.0f alpha:1.0f];
name.font = [UIFont fontWithName:#"Helvetica-Bold" size:17.0];
}
[cell.contentView addSubview:name];
return cell;
}
My skill with Table views is makeshift at best. I've been reading lots of documentation and researching solutions, but was not able to come up with a solution. I just find it odd that it works perfect for iOS6, but not for iOS7.
So it fetches a person's name from an array and I want to populate the cells with those names. I was able to originally accomplish this using:
cell.textLabel.text = [self.entriesArray objectAtIndex:indexPath.row];
if([class isEqualToString:#"CHILD"])
{
cell.textLabel.textColor = [UIColor colorWithRed:25.0f/255.0f green:111.0f/255.0f blue:45.0f/255.0f alpha:1.0f];
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:17.0];
If I use that instead of the "name" UILabel actions from the first code block, then it displays the names perfectly with no text overlay, but what becomes an issue is the text color. If they are labeled as a CHILD then they should be green text and bold. However, after scrolling down, every person becomes green when they shouldn't be.
Sorry for the lengthy question. I've been working on this and racking my brain around it and I just can't seem to figure it out. Any help would be greatly appreciated.
I'm dynamically adding UI objects to cells. As "caglar" notes, adding and then removing is probs not the best-practice. But, I am doing this also. The way I get around adding loads of UI objects to the cell, each time it's displayed, is by removing all my subviews first. The willDisplayCell delegate then adds them back. Clearly, if you want to remove only certain views, you'll have to tag the view and be more selective with your removal. But you can remove all with the following.
-(void)tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
[[cell.contentView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
}
Just ensure you're adding / removing your labels to the cell's contentView. In iOS7, you'll remove that too, if you're not careful.
Hope it helps you out.
In your code, labels are added to cell whenever cellForRowAtIndexPath: method is called. I mean you add labels many times. You can remove the label which was added before.
//If label was added with tag = 500, remove it
for (UIView *sv in cell.contentView.subviews)
{
if (sv.tag == 500)
{
[sv removeFromSuperview];
}
}
//Create Label for Name inside cell
UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake( 7.0, 5.0, 300.0, 30.0 )];
[name setText:[self.entriesArray objectAtIndex:indexPath.row]];
name.tag = 500;
However, this solution is not a good solution. I think creating a custom UITableViewCell is the right thing to do.
You can try this method, when the cell is nil, you need to creare the UILabel* name, then you set the name label with a tag name.tag = 1000, then you can access this label with this method UILabel* name = (UILabel*)[cell.contentView viewWithTag:1000];.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//Create Label for Name inside cell
UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake( 7.0, 5.0, 300.0, 30.0 )];
name.tag = 1000;
[cell.contentView addSubview:name];
}
UILabel* name = (UILabel*)[cell.contentView viewWithTag:1000];
[name setText:[self.entriesArray objectAtIndex:indexPath.row]];
//Check to see if current person is a Parent or Child
NSString *class = [self.database getCellDataWithMembership:[self.MembershipArray objectAtIndex:indexPath.row] andColIndex:4];
if([class isEqualToString:#"CHILD"])
{
name.textColor = [UIColor colorWithRed:25.0f/255.0f green:111.0f/255.0f blue:45.0f/255.0f alpha:1.0f];
name.font = [UIFont fontWithName:#"Helvetica-Bold" size:17.0];
}
return cell;
}
You can set uilabels by using ios6/7 delta shown is size inspector .firstly set your total view for ios7 by changing values x,y,width,height and then change value in ios6/7 delta to make it for ios6 .Hope you get
you may change the this code.
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; -> UITableViewCell *cell = nil;
I want to have a setting in an iphone app that uses a toggle switch to allow something to be turned on or off. I have seen tutorials, but they only show how to do this in the iPhone's settings place. I want this done inside the application. Any guides, help advice. I'm going for something similar to the picture below.
You can use the UISwitch as accessoryView. This will look (almost?) exactly like in your picture.
Something like this:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UISwitch *mySwitch = [[[UISwitch alloc] init] autorelease];
[mySwitch addTarget:self action:#selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = mySwitch;
}
// configure cell
UISwitch *mySwitch = (UISwitch *)cell.accessoryView;
mySwitch.on = YES; // or NO
cell.textLabel.text = #"Auto Connect";
return cell;
}
- (IBAction)switchToggled:(UISwitch *)sender {
UITableViewCell *cell = (UITableViewCell *)[sender superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSLog(#"Switch %i,%i toggled", indexPath.section, indexPath.row);
}
You can use a UISwitch. Here's the very simple class reference guide.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UISwitch_Class/Reference/Reference.html
Basically you can check its state by checking its "on" property.
if(mySwitch.on) {
//do something here
}
First, make sure you're UITableView style is set to "Grouped"
Then, in your cellForRowAtIndexPath method, do something along these lines:
if (indexPath.section == kSwitchSection) {
if (!randomControl) {
randomControl = [ [ UISwitch alloc ] initWithFrame: CGRectMake(200, 10, 0, 0) ];
[randomControl addTarget:self action:#selector(switchAction:) forControlEvents:UIControlEventValueChanged];
randomLabel = [[UILabel alloc] initWithFrame:CGRectMake(20,8,180,30)];
[randomLabel setFont:[UIFont boldSystemFontOfSize:16]];
[randomLabel setText:#"My Label"];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell addSubview:randomControl];
[cell addSubview:randomLabel];
}
Remember to release the UISwitch object later and to include code for setting it to on or off depending on what state it should be in.
I have a tableview in which I am trying to place a button with an image and a label. I want to change the image of the button once clicked.
Here's the code:
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
checkedImg = [UIImage imageNamed:#"buttonUnChecked1.png"];
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
//Set up the cell...
NSString *cellValue = [suggestions objectAtIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
check = [UIButton buttonWithType:UIButtonTypeCustom];
check.frame=CGRectMake(0,35,20,20);
[check setImage:checkedImg forState:UIControlStateNormal];
[check addTarget:self action:#selector(checkClicked:) forControlEvents: UIControlEventTouchUpInside];
[cell.contentView addSubview:check];
cellContent = [[UILabel alloc]initWithFrame:CGRectMake(40,32,500,25)];
cellContent.text = cellValue;
[cell.contentView addSubview:cellContent];
return cell;
}
-(void)checkClicked:(UIButton *)b
{
checkedImg = [UIImage imageNamed:#"buttonChecked1.png"];
[check setImage:checkedImg forState:UIControlStateNormal];
}
By doing this, the image of the buttons are getting changed but only the last one and not the one clicked. I know the reason behind it, but I don't know how to achieve what I want.
A structured way to get the result you're looking for:
Make a UIView subclass that for your table cells (containing a button and label). You instantiate these custom views and set them as your contentView for each table cell in cellForRowAtIndexPath.
Each of your custom views listens for its own button being pressed. When it was pressed, it toggles its state and tells the main viewcontroller (via a delegate method) that it was toggled. The main view controller calls reloadData on the cell in question to cause it to be reloaded with the correct appearance.
Note that this approach requires you to tell each of the custom views which index path it is rendering for in the table -- that way it can inform the main view controller's delegate method -- this info is needed for triggering a reload of the appropriate cell.
Btw, I presume you want to look at the state of the buttons in your table when the user is done with editing, and your current approach doesn't capture the state stuff very explicitly -- you'd have to iterate over your buttons, or add selected items to a mutable array, or something.
The easy answer to your problem is to change your checkClicked: method to this:
-(void)checkClicked:(UIButton *)b
{
[b setImage:[UIImage imageNamed:#"buttonChecked1.png"] forState:UIControlStateNormal];
}
But you should also adjust your tableView:cellForRowAtIndexPath: method to avoid creating the button repeatedly and to clean up some memory issues like this:
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIButton *checkBtn = [UIButton buttonWithType:UIButtonTypeCustom];
checkBtn.frame = CGRectMake(0,35,20,20);
[checkBtn setImage:[UIImage imageNamed:#"buttonUnChecked1.png"]; forState:UIControlStateNormal];
[checkBtn addTarget:self action:#selector(checkClicked:) forControlEvents: UIControlEventTouchUpInside];
[cell.contentView addSubview:checkBtn];
UILabel *cellContentLbl = [[UILabel alloc]initWithFrame:CGRectMake(40,32,500,25)];
cellContentLbl.tag = 1;
[cell.contentView addSubview:cellContentLbl];
[cellContentLbl release];
}
//Set up the cell...
NSString *cellValue = [suggestions objectAtIndex:indexPath.row];
cellContent = (UILabel *)[cell viewWithTag:1];
cellContent.text = cellValue;
return cell;
}
Little background, the table view is filled by a fetchedResultsController which fills the table view with Strings. Now I'm trying to add a button next to each String in each tableview cell. So far, I've been trying to create a button in my configureCell:atIndexPath method and then add that button as a subview to the table cell's content view, but for some reason the buttons do not show up. Below is the relevant code. If anyone wants more code posted, just ask and I'll put up whatever you want. Any help is greatly appreciated.
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// get object that has the string that will be put in the table cell
Task *task = [fetchedResultsController objectAtIndexPath:indexPath];
//make button
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[button setTitle:#"Check" forState:UIControlStateNormal];
[button setTitle:#"Checked" forState:UIControlStateHighlighted];
//set the table cell text equal to the object's property
cell.textLabel.text = [task taskName];
//addbutton as a subview
[cell.contentView addSubview:button];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
Three comments:
You'll probably need to set the frame of your UIButton. Otherwise it doesn't know how big it's supposed to be and where it's supposed to be placed. It's possible that it comes with a default frame, but I haven't investigated that.
You're leaking your buttons. You don't need to retain after calling [UIButton buttonWithType:...];
You have the possibility of adding multiple buttons to the same cell. If you're reusing a cell, then you should first invoke removeFromSuperview each subview of cell.contentView .
Most of the views in my app are UITableVlews inside a UIViewController. My App feels like it's lagging when trying to scroll through the tables. I was wondering (1.) if it is better to create the cell objects in the table view, or create them at runtime and add them to the cells subview?
examples:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
case 3:
{
NSNumber *tetherState = [[mobIntDict objectForKey:#"State"] intValue];
NSNumber *currValState = [[NSNumber numberWithInt:1023] intValue];
tetherSw = [[UISwitch alloc] initWithFrame:CGRectMake(197, 8, 94, 27)];
tetherSw.tag = kDefaultSwTag;
if(tetherState == currValState){
tetherSw.on = YES;
}else{
tetherSw.on = NO;
}
[tetherSw addTarget:self action:#selector(tetherSwAction:) forControlEvents:UIControlEventValueChanged];
[cell.contentView addSubview:tetherSw];
cell.textLabel.text = #"Tether";
[tetherSw release];
}
break;
}
-OR-
-(void)viewDidLoad{
tetherSw = [[[UISwitch alloc] initWithFrame:CGRectMake(197, 8, 94, 27)] autorelease];
tetherSw.tag = kDefaultSwTag;
[tetherSw addTarget:self action:#selector(tetherSwAction:) forControlEvents:UIControlEventValueChanged];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
case 3:
{
[cell addSubView:tetherSw];
}
}
It doesn't matter. Your cell is complex and therefore your view lags.
If you want performance, avoid the UISwitch. Toggle the cell's checkmark instead. In fact, just avoid any fancy table view cell subclasses or custom backgrounds, to reduce the size of the view hierarchy.
Are you properly de-queuing and reusing cells?
It would optimise things a lot if you reused a cell, say a #"SwitchCell", that would speed up scrolling a lot. Currently a lot of time will be spent adding the switch to the cell's content view (laying out of views etc,) and performing other tasks that only need to happen once in a cells lifetime, instead of every time a new cell appears while scrolling.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Create cell
static NSString *CellIdentifier = #"SwitchCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UISwitch *tetherSw = [[UISwitch alloc] initWithFrame:CGRectMake(197, 8, 94, 27)];
tetherSw.tag = kDefaultSwTag;
[tetherSw addTarget:self action:#selector(tetherSwAction:) forControlEvents:UIControlEventValueChanged];
[cell.contentView addSubview:tetherSw];
[tetherSw release];
}
// Setup for each cell
cell.textLabel.text = #"Tether";
NSNumber *tetherState = [[mobIntDict objectForKey:#"State"] intValue];
NSNumber *currValState = [[NSNumber numberWithInt:1023] intValue];
UISwitch *tetherSw = (UISwitch *)[cell.contentView viewWithTag: kDefaultSwTag];
tetherSw.on = (tetherState == currValState);
// Return
return cell;
}
See the docs for dequeueReusableCellWithIdentifier for more information on dequeuing.
Also, make sure that there is no transparency in any of your cell's subviews. This causes a lot of lagging. Make sure any labels or anything else you add has opaque = YES and a background color set.
Actually, my tables are setup just fine. A solid restore did the trick, and my app runs without the afore-mentioned "LAG"