Returning more cells in a UITableView - iphone

I am using MGTwitterEngine and have almost EVERYTHING figured out, but for some reason I cannot figure out how to return more tweets in my timeline! It receives around 20 tweets by the method I am using and if I add a integer for example: return count = 100;, I can tell I now have 100 cells when it loads and by the scroll indicator but when I scroll to that 18- 19 cell I get this error:
SIGABRT
-[NSMutableArray objectAtIndex:]: index 20 beyond bounds [0 .. 19]'
* Call stack at first throw:
I know what it means: theres no more information to receive at cell 20. I'm baffled and went in and out of MGTwitterEngine looking for a default tweet count but I can't find it. I am using the dump to create my NSStrings and the dump only seems to give around 20 tweets per login. Please help, any suggestions are good in my book! Thank you!
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int count = [tweets count];
// Return just enough cells to fill the screen during loading ....
if (count == 0)
count = MyCustomRowCount;
else {
//Here is where I think I need to add a else return integer but dont know how
}
return count;
return [tweets count];
return [authors count];
return [avatarsURL count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
[cell autorelease];
}
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"]autorelease];
/*
//Here it adds a nice shadow to the table view but will crash on rotation and send a
wird dump !!!????
tableView.layer.shadowColor = [[UIColor blackColor] CGColor];
tableView.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
tableView.layer.shadowRadius = 8.0f;
tableView.layer.shadowOpacity = 1.0f;
*/
[cell.textLabel setNumberOfLines:1];
[cell.textLabel setText:[(Tweet*)[authors objectAtIndex:indexPath.row] author]];
[cell.detailTextLabel setText:[(Tweet*)[tweets objectAtIndex:indexPath.row] tweet]];
[cell.detailTextLabel setNumberOfLines:10];
[cell.textLabel setTextColor:[UIColor darkGrayColor]];
[cell.textLabel setShadowColor:[UIColor whiteColor]];
[cell.textLabel setShadowOffset:CGSizeMake(0.5, 0.5)];
[cell.detailTextLabel setTextColor:[UIColor blackColor]];
//[cell.detailTextLabel setText:[(Tweet*)[retweetCount objectAtIndex:indexPath.row]
reTweetCount]];
[cell.textLabel setUserInteractionEnabled:YES];
[cell.contentView setMultipleTouchEnabled:YES];
// cell.text = [[NSString alloc] initWithFormat:#"Cell :%i", indexPath.row];
// Here we use the new provided setImageWithURL: method to load the web image with
SDWebImageManager
[cell.imageView setImageWithURL:[NSURL URLWithString:[(Tweet*)[avatarsURL
objectAtIndex:indexPath.row]avatarURL]]
placeholderImage:[UIImage imageNamed:#"avatar.png"]];
//add gradient to cell
UIImage *gradient = [UIImage imageNamed:#"gradientcell2.png"];
UIImageView *cellimage = [[UIImageView alloc] initWithImage:gradient];
cellimage.contentMode = UIViewContentModeScaleToFill;
cell.backgroundView = cellimage;
[cellimage release];
UIImage *selectedGradient = [UIImage imageNamed:#"selectedcell.png"];
UIImageView *selectedCell = [[UIImageView alloc] initWithImage:selectedGradient];
selectedCell.contentMode = UIViewContentModeScaleToFill;
cell.selectedBackgroundView = selectedCell;
[tableView setBackgroundColor:[UIColor clearColor]];
return cell;
}
//get cell accessory
-(UITableViewCellAccessoryType)tableView:(UITableView *)tableView
accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row < [tweets count]){
}
return UITableViewCellAccessoryDisclosureIndicator;
}
// custom hieght
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
return 80;
}
//select tweet bring to detail view ..... Also bring in the Users information who made the tweet!
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
//Get the selected tweets
NSString *selectedTweet = [(Tweet*)[tweets objectAtIndex:indexPath.row] tweet];
NSString *selectedUser = [(Tweet*)[tweets objectAtIndex:indexPath.row] author];
NSString *selectedUserInfo = [(Tweet*)[tweets objectAtIndex:indexPath.row] user];
NSString *retweetCount = [(Tweet*)[tweets objectAtIndex:indexPath.row] reTweetCount ];
// NSString *selectedUserFriendsCount = [(Tweet*)[tweets objectAtIndex:indexPath.row] userFriendsCount];------HUH? NO soup for ME!
NSString *selectedUserFollowersCount = [(Tweet*)[tweets objectAtIndex:indexPath.row] userFollowersCount];
//Initialize the detail view controller and display it.
TwitterDetailViewController*dvController = [[TwitterDetailViewController alloc]
initWithNibName:#"TwitterDetailViewController" bundle:[NSBundle mainBundle]];
dvController.selectedTweet = selectedTweet;
dvController.selectedUser = selectedUser;
dvController.selectedUserInfo = selectedUserInfo;
// dvController.selectedUserFriendsCount = selectedUserFriendsCount;------Doesnt reconize the call for some odd reason!
dvController.selectedUserFollowersCount = selectedUserFollowersCount;
dvController.retweetCount = retweetCount;
dvController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:dvController animated:YES];
//[self.navigationController pushViewController:webController animated:YES];-------would rather use navagation controller for several obvious reasons
[dvController release];
dvController = nil;
}

UITableView doesn't it like it when you gave it something inconsistant. If you change the model, you need to call [tableView reloadData] or if you want to animate the changes call [tableView beingUpdate][tableView endUpdate] with all the insert/remove cell operations in the middle.
On another note, I'm not sure why you doing here:
NSString *identifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
[cell autorelease];
}
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:#"cell"];
[cell autorelease];
First if (cell == nil), calling auto release on it won't do anything.
The reuse identifier thing is a flyweight pattern. If dequeueReusableCellWithIdentifier: returns nil, it means it doesn't have a cell object in the pool for you to use and you should alloc a new one for it. dequeueReusableCellWithIdentifier also already returns an autorelease object.
Instead try this:
NSString *identifier = #"mytweetcell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier] autorelease];
}

Related

Mixing UITextView and plain Title/Right Detail cells in same iOS iPhone app tableview

I've researched many StackOverflow.com questions, and there seems to be a lot of
confusion over how to get a UITextView displayed into a cell of a Table(View). Here's what
I want: a table where cell rows 0 and 3, for example, are simple Title/Right Detail text cells, and where cells (rows) 1 & 2 have embedded UITextViews that display a clickable phone number and a clickable URL for a website, respectively.
My first attempt set up and allocated UITextView subViews outside of the "if (cell == nil)"
code block:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize infoTableData;
#synthesize infoDetailTableData;
#synthesize cellImages;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.infoTableData = [[NSMutableArray alloc] initWithObjects: #"Address", #"Telephone", #"Website", #"Hours", #"Reservations", #"Parking", #"Takeout", #"Cash only?", nil];
self.cellImages = [[NSMutableArray alloc] initWithObjects:
#"compass_24x24.png",
#"mobilephone2_24x24.png",
#"web_24x24.png",
#"calendar_24x24.png",
#"clock2_24x24.png",
#"parking_meter_24x24.png",
#"shopping_bag_24x24.png",
#"credit_cards_24x24.png", nil];
self.infoDetailTableData = [[NSMutableArray alloc] initWithObjects:
#"123 Main St.",
#"555-555-5555",
#"www.genericrestaurant.com",
#"11:30-2:30,Su:11;5:30-10;F-Sa:10:30,Su:9",
#"Recommended",
#"Parking lot (in rear); on-street",
#"No",
#"Credit cards accepted", nil];
}
#pragma mark Table Source, Delegate Protocol code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.infoTableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ATTEMPT #1: place subView allocate code outside of 'if (cell == nil)' block...
// Causes cell corruption issues upon table scrolling...
static NSString *cellIdentifier = #"DetailInfoCell"; // same ID as used in storyBoard cell setup...
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
cell.textLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:14.0f];
cell.textLabel.text = [self.infoTableData objectAtIndex: indexPath.row];
UIImage *cellImage = [UIImage imageNamed:[self.cellImages objectAtIndex:indexPath.row]]; // ditto: cellImages = array of strings
cell.imageView.image = cellImage;
// Add UITextView to cell for phone, website rows
if (indexPath.row == 1 || indexPath.row == 2)
{
cell.detailTextLabel.text = #"";
// Using constants = embarrassing hack -- figure out proper way
UITextView *tv = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x + (cell.frame.size.width)/2.1f,
cell.frame.origin.y + 2.0f,
(cell.frame.size.width)/2.0f,
cell.frame.size.height)];
if (indexPath.row == 1)
tv.dataDetectorTypes = UIDataDetectorTypePhoneNumber;
else
tv.dataDetectorTypes = UIDataDetectorTypeLink;
tv.editable = NO;
tv.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
tv.textAlignment = NSTextAlignmentRight;
//tv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // or use this instead?
tv.contentMode = UIViewContentModeRight;
tv.backgroundColor = [UIColor clearColor];
tv.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
tv.text=[self.infoDetailTableData objectAtIndex: indexPath.row];
[cell.contentView addSubview:tv];
}
else
{
cell.detailTextLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
cell.detailTextLabel.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
}
return cell;
}
#end
But when I scrolled the table, cell contents overwrote each other. After further research on this website, I learned that it's supposedly a big no-no to set up and allocate the UITextView subViews outside of the "if (cell == nil)" block, because when a cell is dequeued for re-use (using "dequeueReusableCellWithIdentifier"), it may already have had a UITextView subView added to it, so UITextView subViews will be added to cells that already have them, which causes cell corruption issues during scrolling.
So my second attempt tried to get around that problem by using tags to keep track of cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ATTEMPT #2: put subView allocate block INSIDE 'if (cell == nil)' block
// Now, subViews are blank -- they do not appear in the cell rows (even during scrolling)
static NSString *cellIdentifier = #"DetailInfoCell"; // same ID as used in storyBoard cell setup...
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
UITextView *tv1 = nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
// Using constants = embarrassing hack -- figure out proper way
UITextView *tv = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x + (cell.frame.size.width)/2.1f,
cell.frame.origin.y + 2.0f,
(cell.frame.size.width)/2.0f,
cell.frame.size.height)];
tv.tag = indexPath.row; // keep track of this cell, for later re-use...
if (indexPath.row == 1)
tv.dataDetectorTypes = UIDataDetectorTypePhoneNumber;
else
tv.dataDetectorTypes = UIDataDetectorTypeLink;
tv.editable = NO;
tv.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
tv.textAlignment = NSTextAlignmentRight;
//tv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // or use this instead?
tv.contentMode = UIViewContentModeRight;
tv.backgroundColor = [UIColor clearColor];
tv.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
tv.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
[cell.contentView addSubview:tv];
}
cell.textLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:14.0f];
cell.textLabel.text = [self.infoTableData objectAtIndex: indexPath.row]; // infoTableData = data source array of strings...
if (indexPath.row == 1 || indexPath.row == 2)
{
cell.detailTextLabel.text = #"";
tv1 = (UITextView *)[cell.contentView viewWithTag: indexPath.row]; // get pointer to 'correct' re-usable cell...
tv1.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
}
else
{
cell.detailTextLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
cell.detailTextLabel.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
}
return cell;
}
This second attempt failed. The UITextViews weren't showing up at all. The cell
"detail text areas" were blank.
Yet more research on this site revealed postings which claimed that you had to clear any
subViews on returned cells from the call to [tableView dequeueReusableCellWithIdentifier...],
so that adding a new UITextView subView to the cell wouldn't cause problems. So my third
attempt tried this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ATTEMPT #3: keep subView allocate code INSIDE 'if (cell == nil)' block; try to clear subViews from cells
// Still does not work: subViews still blank
static NSString *cellIdentifier = #"DetailInfoCell"; // same ID as used in storyBoard cell setup...
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
UITextView *tv1 = nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
// Using constants = embarrassing hack -- figure out proper way
UITextView *tv = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x + (cell.frame.size.width)/2.1f,
cell.frame.origin.y + 2.0f,
(cell.frame.size.width)/2.0f,
cell.frame.size.height)];
tv.tag = indexPath.row; // keep track of this cell, for later re-use...
if (indexPath.row == 1)
tv.dataDetectorTypes = UIDataDetectorTypePhoneNumber;
else
tv.dataDetectorTypes = UIDataDetectorTypeLink;
tv.editable = NO;
tv.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
tv.textAlignment = NSTextAlignmentRight;
//tv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // or use this instead?
tv.contentMode = UIViewContentModeRight;
tv.backgroundColor = [UIColor clearColor];
tv.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
tv.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
[cell.contentView addSubview:tv];
}
else
{
// Clear subviews, if any?
// Tried this: did not work: subViews still blank
[[[cell contentView] subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
// Tried this: did not work: subViews still blank
for (UIView *view in cell.contentView.subviews)
{
if ([view isKindOfClass:[UIView class]])
{
[view removeFromSuperview];
}
}
}
cell.textLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:14.0f];
cell.textLabel.text = [self.infoTableData objectAtIndex: indexPath.row]; // infoTableData = data source array of strings...
if (indexPath.row == 1 || indexPath.row == 2)
{
cell.detailTextLabel.text = #"";
tv1 = (UITextView *)[cell.contentView viewWithTag: indexPath.row]; // get pointer to 'correct' re-usable cell...
tv1.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
}
else
{
cell.detailTextLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
cell.detailTextLabel.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
}
return cell;
}
This had no affect whatsoever. UITextView subViews were still not appearing in the cells.
Yet even more research. Yet another posting said that:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
will always return a cell in iOS6, so that code within the "if (cell == nil)" block
will never even execute -- the returned cell will never be nil. Sure enough, that's
what I found out: no breakpoint was ever being reached inside my "if (cell == nil)"
code block -- the code was never being executed.
My fourth attempt was just to add a "magic" line of code that yet another posting
suggested:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
which I'm guessing roughly translates to: "always return a new cell, not a used
one, and don't worry about performance or memory issues" (I sense the internet-sphere rolling its eyes at about this point), and sure enough, that did the trick, once I removed the UITextView allocate/setup code from within the "if (cell == nil)" and put it back where it was originally (outside the block, so that it always executes).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ATTEMPT #4:
// According to one particular posting on StackOverflow.com, 'dequeueReusableCellWithIdentifier' will
// *always* return a cell in iOS6 and above. Go figure... Use 'nil' for cell id on
// dequeueReusableCellWithIdentifier
// For some reason, this works...
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
}
cell.textLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:14.0f];
cell.textLabel.text = [self.infoTableData objectAtIndex: indexPath.row];
UIImage *cellImage = [UIImage imageNamed:[self.cellImages objectAtIndex:indexPath.row]];
cell.imageView.image = cellImage;
if (indexPath.row == 1 || indexPath.row == 2)
{
cell.detailTextLabel.text = #"";
UITextView *tv = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x + (cell.frame.size.width)/2.1f,
cell.frame.origin.y + 2.0f,
(cell.frame.size.width)/2.0f,
cell.frame.size.height)];
if (indexPath.row == 1)
tv.dataDetectorTypes = UIDataDetectorTypePhoneNumber;
else if (indexPath.row == 2)
tv.dataDetectorTypes = UIDataDetectorTypeLink;
tv.editable = NO;
tv.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
tv.textAlignment = NSTextAlignmentRight;
//tv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // or use this instead?
tv.contentMode = UIViewContentModeRight;
tv.backgroundColor = [UIColor clearColor];
tv.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
tv.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
[cell.contentView addSubview:tv];
}
else
{
cell.detailTextLabel.font = [UIFont fontWithName:#"Trebuchet MS" size:12.0f];
cell.detailTextLabel.text = [self.infoDetailTableData objectAtIndex: indexPath.row];
}
return cell;
}
But this seems to be a hack. Can someone post some very simple, basic iPhone
app code (I guess for iOS6/4S or equivalent, but does it really matter?) that
represents the canonical, proper way to fill a TableView so that some rows
are simple Title/Right Detail "text-only" cells, and some cells are UITextView
subView cells with phone numbers/website links in the "details" section? Am
I required to subClass a UITableViewCell or use some "prepareForReuse"
method?
Note: I'm using a storyBoard to set up one, Dynamic Prototype cell, with an
identifier 'string'.

How can i dynamically change the UISwitch of UITableView?

I have a UItableView where every UITableViewCell is containing a UISwitch .Now my question is when i will click in one switch then how can i OFF other switches of the UITableViewCell
In my code i have already made the view and i can ON/OFF the switches.But i want to OFF all other switches except my selected switch.
Please some help me by giving an example or source code example.
With Best Regards
Edit
My Code:
- (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];
switchview = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchview;
switchCondition = NO;
[switchview setOn:NO animated:YES];
[switchview addTarget:self action:#selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventValueChanged];
[switchview release];
}
if(switchCondition == YES){
[switchview setOn:YES animated:YES];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.contentView.backgroundColor = [UIColor clearColor];
cell.textLabel.text = [NSString stringWithFormat:#"%#",[cellValueArray objectAtIndex:indexPath.row]];
return cell;
}
- (void)updateSwitchAtIndexPath:(UISwitch*)sender {
if(sender.on){
switchCondition = YES;
[table reloadData];
}
}
Update your data model used by the table's data source, then reload the table (or at least the visible rows). This will cause each row to reload and each switch will get updated with the latest data.
Edit: Here's an updated version of your code:
You need an instance variable to track the state of each switch. Create an array to hold the YES and NO values. In the code below I will assume there is an instance variable named switchConditions of type NSMutableArray that has been setup with NSNumber objects representing the YES and NO values for each row. This is similar to your cellValueArray. You should also get rid of your switchView and switchCondition instance variables.
- (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];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UISwitchView *switch = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switch;
[switchview addTarget:self action:#selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventValueChanged];
[switch release];
}
UISwitchView *switch = (UISwitchView *)cell.accessoryView;
switch.tag = indexPath.row; // This only works if you can't insert or delete rows without a call to reloadData
BOOL switchState = [switchConditions[indexPath.row] boolValue];
switch.on = switchState; // this shouldn't be animated
cell.contentView.backgroundColor = [UIColor clearColor];
cell.textLabel.text = cellValueArray[indexPath.row];
return cell;
}
- (void)updateSwitchAtIndexPath:(UISwitch*)switch {
NSInteger row = switch.tag;
if (switch.on){
// This switch is on, turn all of the rest off
for (NSUInteger i = 0; i < switchConditions.count; i++) {
switchConditions[i] = #NO;
}
switchConditions[row] = #YES;
[self.tableView reloadData];
} else {
switchConditions[row] = #YES;
}
}

Data lose On UITableViewCell When UITableView Scrolling?

I am trying to implement UITableview based application. In my tableView their is 10 Section and each section having one row.
I want implement each section have Different type of ContentView(1-8 same ContentView 9th section Different ContentView). I did this code For that.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 10;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier1 = #"Cell1";
static NSString *CellIdentifier2 = #"Cell2";
UITextField *textField;
UITextView *textView;
NSUInteger section=[indexPath section];
if(section == 9){
UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath];
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if(cell==nil){
cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1]autorelease];
textView=[[UITextView alloc]initWithFrame:CGRectMake(5, 5, 290, 110)];
[textView setBackgroundColor:[UIColor scrollViewTexturedBackgroundColor
]];
[textView setTag:([indexPath section]+100)];
[cell.contentView addSubview:textView];
}else{
textView=(UITextView*)[cell.contentView viewWithTag:([indexPath section]+100)];
}
return cell;
}else {
UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath];
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if(cell==nil){
cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier2]autorelease];
textField=[[UITextField alloc]initWithFrame:CGRectMake(5, 5, 290, 50)];
[textField setBackgroundColor:[UIColor scrollViewTexturedBackgroundColor]];
[textField setTag:([indexPath section]+100)];
[cell.contentView addSubview:textField];
}else{
textField=(UITextField*)[cell.contentView viewWithTag:([indexPath section]+100)];
}
return cell;
}
return nil;
}
My problem are:
1. After type some thing in the UITextField/UITextView i am scrolling in the UITableView. that time all data in the UITableViewCell(UITextField/UITextView) was lose, except last cell data.
2. If i create cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Instead of
UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath];
Data will repeating . How can i over come this problem?
This line:
UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath];
Should never appear in your data source cellForRowAtIndexPath method.
Apart from that, your code is OK, except that you are not setting the text field value anywhere. You need a model (such as an array of strings for the 10 textfield values). This model should be updated when the textfields are edited, and in your method above you copy the value back out of the model and into the textfield's text property:
textfield.text = [self.modelArray objectAtIndex:indexPath.section];
The table pools and reuses cells in an unpredictable fashion, so that subview of a cell that just scrolled off the bottom might reappear next at the top, or might be disposed of.
This is why you saw it partially work. The cell's subviews work okay until their cell gets reused or unloaded, then things move to the wrong place or data disappears.
The solution is that your table's datasource needs to hold onto it's own data. This is usually an array representing your model. Your case is a little unusual because you are using the text controls in your table as inputs, rather than display, which is more typical.
I suggest doing it like this:
// in #interface
#property (nonatomic, retain) NSMutableArray *sections;
// in #implementation
#synthesize sections=_sections;
// at some point before the view appears
self.sections = [NSMutableArray array];
for (int i=0; i<10; i++) {
UIControl *textControl;
if (i<9) {
textControl=[[UITextView alloc]initWithFrame:CGRectMake(5, 5, 290, 110)];
} else {
textControl=[[UITextField alloc]initWithFrame:CGRectMake(5, 5, 290, 50)];
}
[textControl setBackgroundColor:[UIColor scrollViewTexturedBackgroundColor]];
[textControl setTag:i+100];
[sections addObject:textControl];
[textControl release];
}
Now your cellForRowAtIndexPath is a little simpler:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier1 = #"Cell1";
static NSString *CellIdentifier2 = #"Cell2";
NSUInteger section=[indexPath section];
if(section == 9) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if(cell==nil) {
cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1]autorelease];
}
} else {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if(cell==nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier2]autorelease];
}
}
// who knows what subview this cell has? it might not have one, or it might have the wrong one
// just clean it up to be certain
for (UIView *view in cell.subviews) {
[view removeFromSuperView];
}
// get the textControl we set up for _this_ section/cell
UIControl *textControl = [self.sections objectAtIndex:section];
// now we have a fresh cell and the right textControl. drop it in
[cell addSubview:textControl];
return cell;
}
hey the reason is you are doing this things when cell is nil ? but you are not writing any code when cell is not nil.
look at this example , in this example i am adding image view in tableview cell , hence you can add textviews or any other views like this
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UIImageView *imgView;
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(100,0,100,62)];
[imgView setImage:[UIImage imageNamed:#"img.png"]];
imgView.tag = 55;
[cell.contentView addSubview:imgView];
[imgView release];
}
else
{
imgView = (id)[cell.contentView viewWithTag:55];
}
so as showin here imgView = (id)[cell.contentView viewWithTag:55]; you have to give tag to you and write code showing above in else..
Let you try to make the cell labels and textviews by using following code. It works for me.
if (tagvalue ==3) {
static NSString *CellIdentifier = #"Cell3";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier] autorelease];
}
lbl7 = [[UILabel alloc] init];
[lbl7 setFont:[UIFont boldSystemFontOfSize:20]];
[cell.contentView addSubview:lbl7];
lbl7.backgroundColor = [UIColor clearColor];
lbl7.frame = CGRectMake(120, 5, 0, 40);
lbl7.tag = 70;
[lbl7 release];
lbl8 = [[UILabel alloc] init];
[lbl8 setFont:[UIFont boldSystemFontOfSize:18]];
[cell.contentView addSubview:lbl8];
lbl8.backgroundColor = [UIColor clearColor];
lbl8.textColor = [UIColor grayColor];
lbl8.frame = CGRectMake(120, 50, 0, 40);
lbl8.tag = 80;
[lbl8 release];
lbl7 = (UILabel*)[cell.contentView viewWithTag:70];
lbl8 = (UILabel*)[cell.contentView viewWithTag:80];
lbl7.text = [[rowsarray objectAtIndex:row]objectForKey:#"name"];
lbl8.text = [[rowsarray objectAtIndex:row]objectForKey:#"flavour"];
[lbl7 sizeToFit];
[lbl8 sizeToFit];
return cell;
}
if (tagvalue ==4) {
static NSString *CellIdentifier = #"Cell4";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
lbl9 = [[UILabel alloc] init];
[lbl9 setFont:[UIFont boldSystemFontOfSize:20]];
[cell.contentView addSubview:lbl9];
lbl9.backgroundColor = [UIColor clearColor];
lbl9.frame = CGRectMake(120, 5, 0, 40);
lbl9.tag = 90;
[lbl9 release];
lbl10 = [[UILabel alloc] init];
[lbl10 setFont:[UIFont boldSystemFontOfSize:18]];
[cell.contentView addSubview:lbl10];
lbl10.backgroundColor = [UIColor clearColor];
lbl10.textColor = [UIColor grayColor];
lbl10.frame = CGRectMake(120, 50, 0, 40);
lbl10.tag = 100;
[lbl10 release];
lbl9 = (UILabel*)[cell.contentView viewWithTag:90];
lbl10 = (UILabel*)[cell.contentView viewWithTag:100];
lbl9.text = [[rowsarray objectAtIndex:row]objectForKey:#"name"];
lbl10.text = [[rowsarray objectAtIndex:row]objectForKey:#"flavour"];
[lbl9 sizeToFit];
[lbl10 sizeToFit];
return cell;
}
I had same issue. Its not the problem with table class. The issue is at the place where you are calling this tableviewcontroller. First make the object of this call in .h and then allocate in .m, thats it..
When I was declaring in viewdidload like tbl *t = [self.storyboard...];, I was also facing the same problem. But when I put tbl *t; in .h problem solved.

Objective-C: How to retain inputs and also tag names when UItextFields scrolled off?

My App is placing questions and according to the question, placing either UITextField or UISwitch.
When a user input texts it automatically detects which textField and placing the texts accordingly.
It works well but when the items are scrolled off, it removes the user inputs and tag names as well, and when displayes the area placing a new items on top of that.
So when a user input texts it stores it into the old textField.
I would like to know how to prevent it from this issue.
Is there any suggestion? Thanks in advance.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
NSLog(#"-------------cellForRowAtIndexPath---------------");
cell_id = [qid objectAtIndex:[indexPath row] ];
static NSString *CellIdentifier = #"Cell";
label = nil;
cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[self configureLabel];
[[cell contentView] addSubview:label];
}
dict = [qtext objectAtIndex:[indexPath row]];
celltext = [NSString stringWithFormat:#"%#\n\n\n",[[dict allKeys] objectAtIndex:0]];
dict = [qtype objectAtIndex:[indexPath row]];
type = [[dict allKeys] objectAtIndex:0];
//place the question
cell.textLabel.text = celltext;
NSLog(#"celltext=%#",celltext);
if([type isEqualToString:#"devider"]){
[self configureDevider];
}else{
[self configureCell];
}
if([cell_id intValue] == ([qid count])){
tabledone = #"Yes";
}
tableView.backgroundColor=[UIColor clearColor];
tableView.opaque=NO;
tableView.backgroundView=nil;
NSString *a = [arrAllheight objectAtIndex:[indexPath row]];
allheight +=thisheight;
thisheight =[a intValue];
if([type isEqualToString:#"YN"]){
DCRoundSwitch *ynSwitch = [[DCRoundSwitch alloc] initWithFrame:CGRectMake(220,thisheight-40,80,27)] ;
ynSwitch.onText=#"Yes";
ynSwitch.offText=#"No";
[answers addObject:ynSwitch];
[cell addSubview:ynSwitch];
[ynSwitch setTag:[cell_id intValue]];
[ynSwitch addTarget:self action:#selector(setAnswersForRoundSwitches:) forControlEvents:UIControlEventValueChanged];
i++;
}else if([type isEqualToString:#"freetext"]){
//When the done button was clicked, remove the keybords from the screen
[self makeTextField];
[rtxtfield addTarget:self action:#selector(setAnswersfortextFields:) forControlEvents:UIControlEventEditingDidEndOnExit];
// [rtxtfield value];
}else if([type isEqualToString:#"dropdown"]){
picc = [picker_array objectForKey:[[NSString alloc] initWithFormat:#"%d",cell_id]];
//Choose an array for this textField
// [self getPickerArray];
[self makeTextField];
//[rtxtfield addTarget:self action:#selector(setAnswersfortextFields:) forControlEvents:UIControlEventEditingDidEndOnExit];
//When the done button was clicked, remove the keybords from the screen
[rtxtfield addTarget:self action:#selector(textFieldReturn:) forControlEvents:UIControlEventEditingDidEndOnExit];
//Get the tag for picker
[rtxtfield addTarget:self action:#selector(getTag:) forControlEvents:UIControlEventTouchDown];
//Display picker
[rtxtfield addTarget:self action:#selector(acsheet:) forControlEvents:UIControlEventTouchDown];
//set Tag for the textField
[rtxtfield setTag:[cell_id intValue]];
NSLog(#"rtxtfield tag=%d",rtxtfield.tag);
}
if([type isEqualToString:#"devider"]){
[self caliculateHeightofCell];
}else{
[self caliculateHeightofCell];
}
return cell;
}
Save the state of your controls in your data model as soon as they change. So, maybe your model is an array of questions, and each question has an instance variable that can hold the answer. Your view controller is probably both the table data source and table delegate, and you should make it the target of any controls in the cells, too. That is, when you set up a new cell in your -tableView:cellForRowAtIndexPath:, make the view controller the target of the UITextField or UISwitch in the cell. When the user changes either of those controls, then, the change will trigger an action in the view controller, and the view controller can retrieve the new value of the control and store it in the corresponding question in the data model.
If you take this approach, you never have to worry about questions scrolling out of view. As soon as the question scrolls back into view, -tableView:cellForRowAtIndexPath: will be called for that row again, and you'll have all the information you need to reconstitute that cell.
Save the text to the dataSource. UITableViewCells must not contain any state information.
Implement something similar to this:
- (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];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(2, 2, 200, 40)];
textField.tag = 999;
textField.delegate = self;
textField.placeholder = #"Enter text here";
[cell.contentView addSubview:textField];
}
UITextField *textField = (UITextField *)[cell.contentView viewWithTag:999];
textField.text = [self.dataSource objectAtIndex:indexPath.row];
/* configure cell */
return cell;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
UIView *contentView = [textField superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.dataSource replaceObjectAtIndex:indexPath.row withObject:textField.text];
}
Do not add views outside of if (cell == nil)!
If you have different type of cells use a different CellIdentifier! Like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SwitchCellIdentifier = #"SwitchCell";
static NSString *TextFieldCellIdentifier = #"TextFieldCell";
UITableViewCell *cell = nil;
if (/* current cell is a text field cell */) {
cell = [tableView dequeueReusableCellWithIdentifier:TextFieldCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextFieldCellIdentifier];
// add textField
}
// configure cell...
}
else if (/* current cell is a switch cell */) {
cell = [tableView dequeueReusableCellWithIdentifier:SwitchCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SwitchCellIdentifier];
// add switch
}
// configure cell...
}
return cell;
}
I took a day to fix the problem, but I've finally got it right:
Sorry it's a long code cause it's using textField, DCRoundSwitch, and pickerView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"-----------------cellForRowAtIndexPath---------------");
Questions *q = [qtext objectAtIndex:indexPath.row];
NSLog(#"question=%#",q.question);
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
static NSString *SwitchCellIdentifier = #"SwitchCell";
static NSString *TextFieldCellIdentifier = #"TextFieldCell";
static NSString *DropDownFieldCellIdentifier = #"DropDownCell";
cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:TextFieldCellIdentifier];
if ([q.question_type isEqualToString:#"freetext"]) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextFieldCellIdentifier];
if (cell == nil) {
[self makeTextField];
[cell.contentView addSubview:rtxtfield];
cell.textLabel.text = q.question;
}
[self makeTextField];
[self configureCell];
[self configureLabel];
cell.textLabel.text = q.question;
[cell.contentView addSubview:rtxtfield];
rtxtfield.text = [appDelegate.dataSource objectAtIndex:indexPath.row];
rtxtfield.tag=[indexPath row];
}else if([q.question_type isEqualToString:#"YN"]){
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SwitchCellIdentifier];
if (cell == nil) {
[self makeDCRoundSwitch];
[ynSwitch setTag:[indexPath row]];
cell.textLabel.text = q.question;
[self configureLabel];
[self configureCell];
}
[self makeDCRoundSwitch];
[ynSwitch setTag:[indexPath row]];
cell.textLabel.text = q.question;
[self configureLabel];
[self configureCell];
[ynSwitch addTarget:self action:#selector(ynSwitchDidEndEditing:) forControlEvents:UIControlEventValueChanged];
if([[appDelegate.dataSource objectAtIndex:indexPath.row] isEqualToString:#"Yes"]){
[ynSwitch setOn:YES animated:YES];
} else{
[ynSwitch setOn:NO animated:YES];
}
rtxtfield.tag=[indexPath row];
} else if([q.question_type isEqualToString:#"dropdown"]){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:DropDownFieldCellIdentifier];
if (cell == nil) {
[self makeTextField];
[cell.contentView addSubview:rtxtfield];
cell.textLabel.text = q.question;
}
[self makeTextField];
[self configureCell];
[self configureLabel];
cell.textLabel.text = q.question;
[cell.contentView addSubview:rtxtfield];
picc=q.question_array;
//When the done button was clicked, remove the keybords from the screen
[rtxtfield addTarget:self action:#selector(textFieldReturn:) forControlEvents:UIControlEventEditingDidEndOnExit];
//Get the tag for picker
[rtxtfield addTarget:self action:#selector(getTag:) forControlEvents:UIControlEventTouchDown];
//Display picker
[rtxtfield addTarget:self action:#selector(acsheet:) forControlEvents:UIControlEventTouchDown];
//set Tag for the textField
[rtxtfield setTag:[indexPath row]];
// AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
rtxtfield.text = [appDelegate.dataSource objectAtIndex:indexPath.row];
rtxtfield.tag=[indexPath row];
}
[self makeTextField];
rtxtfield.tag=[indexPath row];
tableView.backgroundColor=[UIColor clearColor];
tableView.opaque=NO;
tableView.backgroundView=nil;
return cell;
}
try this
label = nil;
//cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[self configureLabel];
[[cell contentView] addSubview:label];
//}
comment your code like that and tell me what happens

Asynchronous data in UITableView

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"celluleConnecte";
celluleConnecte *cell = (celluleConnecte *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
UIViewController *vue = [[UIViewController alloc] initWithNibName:#"celluleConnecte" bundle:nil];
cell = (celluleConnecte *)vue.view;
[vue release];
}
else {
asyncImageView* oldImage = (asyncImageView*)
[cell.contentView viewWithTag:999];
[oldImage removeFromSuperview];
}
// Configure the cell.
NSDictionary *dico = [self.pseudoOnline objectAtIndex:indexPath.row];
cell.pseudo.text = [dico objectForKey:#"name"];
cell.sexe.text = [dico objectForKey:#"sexe"];
CGRect frame;
frame.size.width=70; frame.size.height=70;
frame.origin.x=5; frame.origin.y=10;
asyncImageView *asyncImage = [[[asyncImageView alloc] initWithFrame:frame] autorelease];
asyncImage.tag =999;
[asyncImage loadImageFromURL:[NSURL URLWithString:[dico objectForKey:#"photo"]]];
[cell.contentView addSubview:asyncImage];
return cell;
}
it's because you downloading image right in cell, that is wrong. Create a new class of UIImageView and downloading photos there using NSData. There are many examples over the Internet. Here is first i found markj.net/wp/wp-content/uploads/2009/02/asyncimageview.m