Datasource for UITableViews in UIScrollView - iphone

I'm working on the project using several UITableViews being put vertically next to each other. I got to show those tables, but got stuck when I tried to show different table contents for each table.
This is the code:
-(void)setTheTable{
int numberOfTables = 5;
//height of the table
CGFloat height = self.view.bounds.size.height;
CGRect tableBounds = CGRectMake(0.0f, 0.0f, self.widthOfTheScreen, height- 30);
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, 30.0f, self.widthOfTheScreen, height - 30.0f)];
self.scrollView.contentSize = CGSizeMake(self.widthOfTheScreen * numberOfTables, height-30);
self.scrollView.pagingEnabled = YES;
self.scrollView.bounds = tableBounds;
[self.view addSubview:self.scrollView];
//put five tables
CGRect tableFrame = tableBounds;
tableFrame.origin.x = 0;
for (int i = 0; i < numberOfTables; i++) {
UITableView *tableView = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
switch (i) {
case 0:
tableView.backgroundColor = [UIColor yellowColor];
tableView.tag = 0;
cell.textLabel.text = #"section = 0 行0";
break;
case 1:
tableView.backgroundColor = [UIColor blueColor];
tableView.tag = 1;
break;
case 2:
tableView.backgroundColor = [UIColor brownColor];
tableView.tag = 2;
break;
case 3:
tableView.backgroundColor = [UIColor greenColor];
break;
case 4:
tableView.backgroundColor = [UIColor cyanColor];
break;
default:
break;
}
[self.scrollView addSubview:tableView];
tableFrame.origin.x += self.widthOfTheScreen;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
int num = tableView.tag;
if(num == 1) {
if(indexPath.row == 0) {
cell.textLabel.text = #"section = 0 行0";
} else if(indexPath.row == 1){
cell.textLabel.text = #"section = 0 行1";
} else {
cell.textLabel.text = #"section = 0 行2";
}
} else {
NSString *str = [[NSString alloc] initWithFormat:#"section = %d col %d",indexPath.section,indexPath.row];
cell.textLabel.text = str;
}
return cell;
}
I thought tableview.tag would help but gave me 5 same tables.
Could you guys give me some ideas to solve this problem?
Thank you.

First of all, see this answer about adding a UITableView to a UIScrollView, Apple specifically warns against it (though they did not include the warning in the UITableView docs, not your fault). Add your tables as subviews to a UIView NOT a UIScrollView (I recommend getting rid of that entriely).
Second, you only create a UITableViewCell in the tableView:cellForRowAtIndexPath: method. Remove all references to any UITableCell from your for-loop entirely (those are not even being used, it is work for nothing). After you have set the table's data source and delegate, call [tableView reloadData] on each able, the data source methods will be called automatically.
Since you are working with multiple tables, things get a bit more tricky. You will need to keep a reference to all your table views in your class (either in an array, but ideally properties for each table view).
You will also need to know what table is being loaded at any given time, e.g.:
-(UITableViewCell*)tablView:tableView cellForRowAtIndexPath:indexPath {
UITableViewCell *cell;
if ( tableView == self.tablView1 ) {
// build your cells for table view 1
}
else if ( tableView == self.tableView2 ) {
// build cells for table view 2
}
return cell;
}
With multiple tables, you would probably want to consider a specific delegate for each table, especially if the data models behind each table is vastly different, and/or each the UI for each table are different.
I do not know your exact needs, but also consider a table with multiple sections instead of multiple tables.
I also encourage you to read the iOS TableView Programming Guide, and this on Delegates and Data Sources.

Related

UIProgressview in subclassed UITableViewCell need to be used in other UIViewController

I have a subclassed custom UITableViewCell, in which I am inserting a progress view in it.
here is how:
-(void)layoutSubviews {
applicationDelegate = [[UIApplication sharedApplication] delegate];
[super layoutSubviews];
self.imageView.frame = CGRectMake(0, 0, self.frame.size.height, self.frame.size.height);
self.textLabel.frame = CGRectMake( self.imageView.frame.size.width + 20, self.textLabel.frame.origin.y, self.textLabel.frame.size.width, self.textLabel.frame.size.height);
progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
progressView.frame = CGRectMake(self.textLabel.frame.origin.x, 50, 150, 100);
progressView.progress = 0.0f;
progressView.trackTintColor = [UIColor clearColor];
progressView.progressTintColor = [UIColor redColor];
progressView.tag = 101;
[progressView setHidden:YES];
[self.contentView addSubview:progressView];
}
Now when in other view controller I want to show this elements only in first cell, so I am trying to hide these elements in other cells. Here is how I am trying to do this in cellForRowAtIndexPath: method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * cellIdentifier = #"cellId";
CustomCell * cell = [self.tableview dequeueReusableHeaderFooterViewWithIdentifier:cellIdentifier];
if (cell == Nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
if (indexPath.row == 0) {
imageData = UIImageJPEGRepresentation(cell.imageView.image, 100);
filename = cell.textLabel.text;
[(UIProgressView*)[cell.contentView viewWithTag:101] setHidden:NO];<==Not Working
}
return cell;
}
How to use this UIElements in custom UITableView subclass in my ViewController ?
I have not used xib file, everything is done programmatically. I have beed playing around this for whole day now, any one with any idea here ?
If only the 1st row needs to show the progress view, why not only use the custom cell for row 0 and then use a standard UITableViewCell for rows >1?
I would also recommend adding progressView to self.contentView in an init method and not in layoutSubviews. layoutSubviews is intended to position the views you already have (not add new ones) because it can be called many times over the lifetime of the cell.
I would add a BOOL property to the custom cell that determines if you should show the progress indicator (and removes it when set to NO). In cellForRowAtIndexPath: you could set it appropriately for the given row.
In CustomCell.h:
#property(nonatomic, assign) BOOL shouldShowProgressView;
In CustomCell.m:
- (void)setShouldShowProgressView:shouldShowProgressView {
_shouldShowProgressView = shouldShowProgressView;
if (shouldShowProgressView) {
if (self.someProgressView == nil) {
// add your progress view from scratch
}
[self.contentView addSubview:self.someProgressView];
} else {
[self.someProgressView removeFromSuperview];
}
}
In your table controller:
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *cellIdentifier = #"cellId";
CustomCell *cell = [self.tableview dequeueReusableHeaderFooterViewWithIdentifier:cellIdentifier];
if (cell == Nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
if (indexPath.row == 0) {
cell.shouldShowProgressView = YES;
} else {
cell.shouldShowProgressView = NO;
}
return cell;
}

Duplicates of text in UITextField when inside UITableView

I have created a UITextField in a UITableView. I type the data in and close the keyboard. However when I scroll down and hide the UITextField and then scroll back up again, the 'UITextField' data is duplicated as seen below:
Original Load of View:
Typed in Data:
After hidden textfield and then started editing again:
- (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];
}
if ([indexPath section] == 0) { // Email & Password Section
cell.textLabel.text = #"Subject";
} else {
cell.textLabel.text = #"Task";
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if ([indexPath section] == 0) {
UITextField *subject = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
subject.adjustsFontSizeToFitWidth = YES;
subject.textColor = [UIColor blackColor];
if ([indexPath row] == 0) {
subject.placeholder = #"Maths";
subject.keyboardType = UIKeyboardTypeEmailAddress;
subject.returnKeyType = UIReturnKeyNext;
}
subject.backgroundColor = [UIColor clearColor];
subject.autocorrectionType = UITextAutocorrectionTypeNo;
subject.autocapitalizationType = UITextAutocapitalizationTypeWords;
subject.tag = 0;
subject.clearButtonMode = UITextFieldViewModeNever;
[cell.contentView addSubview:subject];
} else {
UITextView *task = [[UITextView alloc] initWithFrame:CGRectMake(102, 0, 185, 40)];
task.text = #"fasfashfjasfhasfasdjhasgdgasdhjagshjdgashjdgahjsdghjasgasdashgdgjasd";
task.editable = NO;
task.scrollEnabled = NO;
task.userInteractionEnabled = NO;
task.textColor = [UIColor colorWithRed: 62.0/255.0 green: 85.0/255.0 blue:132.0/255.0 alpha:1.0];
task.backgroundColor = [UIColor clearColor];
}
return cell;
}
Like Richard said, cells are reused (that's what the identifier purpose is), and that's why you test in your tableView:cellForRowAtIndexPath: for a nil value returned by dequeueReusableCellWithIdentifier:.
If a cell already exists (ie. was allocated earlier) and is not displayed anymore, dequeueReusableCellWithIdentifier: will use this cell to display the content of the newly appearing cell.
What you are doing is adding your UITextView every time your cells are displayed and not created. So each time a cell is gets scrolled out of the screen and a new cell pops in, you append a new UITextView in the cell. You should add subviews only in the if (cell == nil) part of your method. As the content of your cells are rather different, I'd recommend using two distinct identifiers.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifierForSection0 = #"Cell0";
static NSString *CellIdentifierForSection1 = #"Cell1";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: [indexPath section] == 0 ? CellIdentifierForSection0 : CellIdentifierForSection1];
if (cell == nil) {
if ([indexPath section] == 0) { // Email & Password Section
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifierForSection0];
cell.textLabel.text = #"Subject";
UITextField *subject = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
subject.adjustsFontSizeToFitWidth = YES;
subject.textColor = [UIColor blackColor];
if ([indexPath row] == 0) {
subject.placeholder = #"Maths";
subject.keyboardType = UIKeyboardTypeEmailAddress;
subject.returnKeyType = UIReturnKeyNext;
}
subject.backgroundColor = [UIColor clearColor];
subject.autocorrectionType = UITextAutocorrectionTypeNo;
subject.autocapitalizationType = UITextAutocapitalizationTypeWords;
subject.tag = 0;
subject.clearButtonMode = UITextFieldViewModeNever;
[cell.contentView addSubview:subject];
} else {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifierForSection1];
cell.textLabel.text = #"Task";
UITextView *task = [[UITextView alloc] initWithFrame:CGRectMake(102, 0, 185, 40)];
task.text = #"fasfashfjasfhasfasdjhasgdgasdhjagshjdgashjdgahjsdghjasgasdashgdgjasd";
task.editable = NO;
task.scrollEnabled = NO;
task.userInteractionEnabled = NO;
task.textColor = [UIColor colorWithRed: 62.0/255.0 green: 85.0/255.0 blue:132.0/255.0 alpha:1.0];
task.backgroundColor = [UIColor clearColor];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
Note that this code is mainly for example purpose, and could be greatly reducted. Moreover, you should use subclass(es) of UITableViewCell like Richard suggested, as it will help organizing your code and make it more reusable.
BUT do NOT use drawRect: to add subviews. This is unnecessary and will impact performances. drawRect: should only be used if you intend to make real drawing like with CoreAnimation or CoreGraphics. Adding subview should be done in initWithFrame: or initWithCoder: depending of your use of Interface Builder or not.
Remember cells get reused, therefore the subviews are added each time it's reused. If you're going to add subviews to a cell you're best off creating a subclass of UITableViewCell and adding the subviews in the drawRect: method of that subclass. That way the modifications are part of the cell and aren't added each time the cell is reused.

Load all cells in UITableView before scrolling

I have a UITableView with 8 cells(sections) in total in which 6 of them contain textFields as subviews and out of 2 one containing button and the other containing text view.Now I have got a problem here,as we are all aware that when ever the view appears only,the visible cells loads.So when my app gets loaded,I am able to view 5 cells,the first 4 containing text fields and the 5th cell with button.I can see the array count is 4 when i displayed it through logging in console i.e.
there are 4 objects in the array
Now after scrolling the table view,I can observe the following:
there are 6 objects in the array
So,what's happening is If I am to save the data entered in the table view,or say edit to make changes to existing data,I am forced to scroll the table view and enter the values in last cell too.So that cannot be the case always with the user because we can't expect him/her to scroll the table view and enter the complete form/table view cell entries.What ever changes he/she makes they will and just click done/save what ever it is..!
More over I am having a picker view as input view for last cell containing text field as subview.Hence in my picker view did select row method I am facing crash problem with last cell/section(textField as subview) containing a picker view here is my code snippet for the method:
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
switch (tagValues)
{
case 105:
{
self.textField = [self.fields objectAtIndex:3];
self.textField.text = [self.reminder objectAtIndex:row];
}
break;
case 107:
{
//Crash occurring in this case,sometimes its working wonder why
self.textField = [self.fields objectAtIndex:5];
self.textField.text = [self.group objectAtIndex:row];
}
break;
}
}
**Note**:I am using the same textField for adding as subview to all 6 cells,but each one with unique tags and own purpose,i.e. one for editing text using key board,one with date picker,one with general picker etc...
I wonder why some times after selecting values from picker view(last section cell) crashes some times and some times works fine.Some times it crashes at the above line commented in case 107,some times in main autorelease pool.Some time thread as shown in snap below:
So is there any way that we can load all cells at once so that all the text fields get added to array before scrolling.There cannot be any problem I believe
More Detailed EDITED CODE for understanding:
- (UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSString *identifier = #"UITableViewCell";
NSString *cellIdentifierF = nil;
static NSString *firstCellIdentifier = #"FirstCell";
static NSString *secondCellIdentifier = #"SecondCell";
static NSString *thirdCellIdentifier = #"ThirdCell";
static NSString *fourthCellIdentifier = #"FourthCell";
static NSString *fifthCellIdentifier = #"FifthCell";
static NSString *sixthCellIdentifier = #"SixthCell";
static NSString *seventhCellIdentifier = #"SeventhCell";
static NSString *eightCellIdentifier = #"EightCell";
if(indexPath.section == 0 && indexPath.row == 0)
{
cellIdentifierF = firstCellIdentifier;
}
else if(indexPath.section == 1 && indexPath.row == 0)
{
cellIdentifierF = secondCellIdentifier;
}
else if(indexPath.section == 2 && indexPath.row == 0)
{
cellIdentifierF = thirdCellIdentifier;
}
else if(indexPath.section == 3 && indexPath.row == 0)
{
cellIdentifierF = fourthCellIdentifier;
}
else if(indexPath.section == 4 && indexPath.row == 0)
{
cellIdentifierF = fifthCellIdentifier;
}
else if(indexPath.section == 5 && indexPath.row == 0)
{
cellIdentifierF = sixthCellIdentifier;
}
else if(indexPath.section == 6 && indexPath.row == 0)
{
cellIdentifierF = seventhCellIdentifier;
}
else if(indexPath.section == 7 && indexPath.row == 0)
{
cellIdentifierF = eightCellIdentifier;
}
UITableViewCell *cell = (UITableViewCell *)[atableView dequeueReusableCellWithIdentifier:cellIdentifierF];
atableView.backgroundColor = [UIColor clearColor];
textField = [[[UITextField alloc]initWithFrame:CGRectMake(15, 12, 300, 24)]autorelease];
textField.textColor = [UIColor whiteColor];
textField.delegate = self;
tagValues = textField.tag;
switch (indexPath.section)
{
case 0:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: firstCellIdentifier])
{
textField.placeholder = #"Enter name";
[self.textField setValue:[UIColor purpleColor]
forKeyPath:#"_placeholderLabel.textColor"];
textField.tag = 101;
textField.text = reminderInstance.Name;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
}
}
break;
case 1:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: secondCellIdentifier])
{
textField.tag = 102;
textField.text = reminderInstance.Event;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
}
}
break;
case 2:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: thirdCellIdentifier])
{
textField.placeholder = #"Click here to set date and time";
[self.textField setValue:[UIColor purpleColor]
forKeyPath:#"_placeholderLabel.textColor"];
textField.inputView = self.datePicker;
textField.text = reminderInstance.Date;
textField.tag = 103;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
}
}
break;
case 3:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: fourthCellIdentifier])
{
textField.tag = 105;
textField.text = reminderInstance.numDays;
textField.inputView = self.reminderPicker;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
}
}
break;
case 4:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: fifthCellIdentifier])
{
checkboxButton = [[[UIButton alloc] initWithFrame:CGRectMake(16,1,120, 44)]autorelease];
[checkboxButton setImage:[UIImage imageNamed:#"ewee.png"] forState:UIControlStateNormal];
[checkboxButton addTarget:self action:#selector(toggleButton:) forControlEvents:UIControlEventTouchUpInside];
NSString *one = reminderInstance.selString;
NSNumber* i = [NSNumber numberWithInt:[one intValue]];
BOOL isOn = [i boolValue];
if(isOn)
{
[checkboxButton setImage:[UIImage imageNamed:#"checkarrow.png"] forState:UIControlStateNormal];
}
else
{
[checkboxButton setImage:[UIImage imageNamed:#"ewee.png"] forState:UIControlStateNormal];
}
NSLog(#"String Val = %#",one);
[checkboxButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[checkboxButton setImageEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)];
[cell addSubview:checkboxButton];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(55, 10, 225, 24)];
label.text = #"Every Year";
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
[cell addSubview:label];
cell.textLabel.textColor = [UIColor whiteColor];
[label release];
}
}
}
break;
case 5:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: sixthCellIdentifier])
{
textField.placeholder = #"Enter the number here";
[self.textField setValue:[UIColor purpleColor]
forKeyPath:#"_placeholderLabel.textColor"];
textField.text = num;
textField.text = reminderInstance.number;
textField.tag = 106;
textField.userInteractionEnabled = YES;
textField.keyboardType = UIKeyboardTypeNumberPad;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
}
}
break;
case 6:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"reminderbuttonxl.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: seventhCellIdentifier])
{
cell.backgroundColor = [UIColor clearColor];
textView.clipsToBounds = YES;
textView = [[UITextView alloc]initWithFrame: CGRectMake(-2, -3, 307, 154)];
UIImageView *imgView = [[[UIImageView alloc]initWithFrame: textView.frame]autorelease];
imgView.image = [UIImage imageNamed: #"buttonbg_text.png"];
[textView addSubview: imgView];
[textView sendSubviewToBack: imgView];
textView.backgroundColor = [UIColor clearColor];
textView.delegate = self;
textView.tag = 11;
tagValues = textView.tag;
textView.textColor = [UIColor purpleColor];
[cell.contentView addSubview:textView];
textView.text = #"Your birthday wishes/other reminder body here";
NSLog(#"Value = %#",textView.text);
}
}
}
break;
case 7:
{
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
//Only add content to cell if it is new
if([cellIdentifierF isEqualToString: eightCellIdentifier])
{
textField.tag = 107;
textField.inputView = self.groupPicker;
tagValues = textField.tag;
textField.text = reminderInstance.remGroup;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
}
}
break;
default:
break;
}
int size = [fields count];
NSLog(#"there are %d objects in the array", size);
return cell;
}
I have made several attempts to find out what exactly is going wrong also I checked some questions here and there.But couldn't find any efficient/appr. answer or that solves my issue.
So anyone please help me with valuable suggestions or sample code snippets.
Thanks all in advance :)
It would make sense that there will be a lot of issues coming from the way you are building the form (which is actually the case)
what I've been doing myself when I need to build a such a form is to use build a normal view with all the controls whatever long it is and have that form in a scroll view and set its content size with the size of the view in it. that would let you have more control on how you want that form to look.
and I don't think I've seen an app using a tableview for a form entry.
you can use interface builder to build your form (just cheat on Xcode and make the main view and the scroll vew large enough so u can see everything on the builder, then resize it to the appropriate size when you are done)
It sounds like you might be better off not using a table view at all, but using a scroll view instead. If you really do have a fixed amount of things that you are going to display, you can just lay these out on a scroll view as views (instead of table cells) and your code will get a lot simpler.
If what you want out of the table view is just the appearance, you can simply use a background image that looks the way you want, and overlay the views and controls over that background. You should be able to do all of this in Interface Builder.
If you take this approach, the code in your view controller will now be just what you need to handle the text input and button presses, and you don't need to worry about a table view delegate or data source.
Although this seems to be already solved, what could have done as a "dirty hack" is to make the entire table scroll till bottom before performing any operation. Something like [myTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; This way it would be forced to load all the UITableCellView. Remember, this is not preferred way, but can work in case minimum code change is required.
What happens is that UITableView loads only the visible cells, and all the time that the UITableView is scrolled, the callback cellForRowAtIndexPath: is called. But let me get this straight. Why do you need a UITableView to make a form? And as I can see, you are loading one cell per section... And, you are only initializing the cell. What you should do. Subclass UITableViewCell and create your objects there.
then call like this:
cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
Then you can call exactly the controller you want. And you have to control the behavior of your UITableView whenever it is scrolled. like this:
case 7:
{
if (cell == nil)
{
cell = [[[MyCustomCellEigth alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierF] autorelease];
cell.backgroundColor = [[UIColor alloc]initWithPatternImage:[UIImage imageNamed:#"buttonbg-1.png"]];
/*this should be done in the Custom Cell init method
if([cellIdentifierF isEqualToString: eightCellIdentifier])
{
textField.tag = 107;
textField.inputView = self.groupPicker;
tagValues = textField.tag;
textField.text = reminderInstance.remGroup;
NSLog(#"Value = %#",textField.text);
[cell.contentView addSubview:textField];
[self.fields addObject:textField];
}
*/
}
else
{
//Control content
((MyCustomCell*)cell).myTextField.text = #"SOME TEXT";
}
}
break;
About the picker. It probably only works once you have scrolled all table, because it references a textField that will only be added to the Array once the correspondent cell become visible.
There isn't much more to do. But follow this and you should be fine. First, Subclass UITableViewCell. You can create the controls yourself programmatically or with a xib. This will give you more control over the cells itself. About the picker, instead of creating a array with controls, create it with the values it should have. Once someone has typed something, save it using the UITextFieldDelegate.
Hope it helps.
EDIT:
You must create a Subclass of the UITableViewCell. On Xcode, create a new file. Select objective-c Class. On the next screen, on the field subclass of, select UITableViewCell and name your cell accordingly. This will create a class that will override the initWithStyle:reuseIdentifier: method. Inside this method, you should put your init code.
like this:
#import <UIKit/UIKit.h>
#interface MyCustomCellOne : UITableViewCell
{
UITextField *textField;
}
#end
#import "MyCustomCellOne.h"
#implementation MyCustomCellOne
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
textField = [[UITextField alloc] initWithFrame:"YOUR FRAME"];
textField.tag = 107;
textField.text = #"SOME TEXT";
[self addSubview:textField];
}
return self;
}

Reuse Cell with Subview (programmatically)

I'm having an issue programmatically adding a subview to a UITableView cell. I am attempting to add a UITextFiled to 2 cells and a UILabel to another cell in one section of my table. The table has 3 sections and most of it is manually created. One of the cells, when selected calls an action that inserts four more cells. If the same cells is selected again, the four cells will be removed. When this action occurs, the subviews get mixed up and are on the wrong cells. The keyboard also does not respond to the done key.
I try to create the subviews inside the if (cell == nil) conditional statement. Here is my code that creates the cells (sorry for it being so messy, I've been trying a ton of things):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSLog(#"cell created");
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
if (indexPath.section == 0 && indexPath.row == 0 && ([cellText objectAtIndex:indexPath.row] == #"Weight")) {
exerciseWeight = [[[UITextField alloc] initWithFrame:CGRectMake(0, 12, 295, 30)] autorelease];
exerciseWeight.textAlignment = UITextAlignmentRight;
exerciseWeight.adjustsFontSizeToFitWidth = YES;
exerciseWeight.textColor = [UIColor blackColor];
exerciseWeight.backgroundColor = [UIColor clearColor];
exerciseWeight.tag = 89899;
exerciseWeight.keyboardType = UIKeyboardTypeNumberPad;
[exerciseWeight setReturnKeyType:UIReturnKeyDone];
exerciseWeight.clearButtonMode = UITextFieldViewModeNever;
[exerciseWeight setEnabled: YES];
[exerciseWeight setDelegate:self];
[cell addSubview:exerciseWeight];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryNone;
} else if (indexPath.section == 0 && indexPath.row == 1) {
NSLog(#"Created metric subview");
metric = [[[UILabel alloc] initWithFrame:CGRectMake(100, 5, 180, 30)] autorelease];
metric.textAlignment = UITextAlignmentRight;
metric.textColor = [UIColor grayColor];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
metric.tag = 89898;
[cell addSubview:metric];
} else if (([cellText count] - 1) == indexPath.row){
exerciseReps = [[[UITextField alloc] initWithFrame:CGRectMake(0, 12, 295, 30)] autorelease];
exerciseReps.textAlignment = UITextAlignmentRight;
exerciseReps.adjustsFontSizeToFitWidth = YES;
exerciseReps.backgroundColor = [UIColor clearColor];
exerciseReps.tag = 89890;
exerciseReps.keyboardType = UIKeyboardTypeNumberPad;
[exerciseReps setReturnKeyType:UIReturnKeyDone];
exerciseReps.clearButtonMode = UITextFieldViewModeNever;
[exerciseReps setEnabled:YES];
[exerciseReps setDelegate:self];
[cell addSubview:exerciseReps];
} else if (indexPath.section == 2) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
} else {
exerciseWeight = (UITextField *)[cell viewWithTag:89899];
metric = (UILabel *)[cell viewWithTag:89898];
exerciseReps = (UITextField *)[cell viewWithTag:89890];
}
if (indexPath.section == 0) {
cell.textLabel.text = [cellText objectAtIndex:indexPath.row];
}
// Configure the cell...
if (indexPath.section == 0 && indexPath.row == 0) {
cell.textLabel.textAlignment = UITextAlignmentLeft;
exerciseWeight.placeholder = #"160";
} else if (indexPath.section == 0 && indexPath.row == 1) {
metric.text = metricUnit;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else if (cell.textLabel.text == #"Repetitions"){
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryNone;
exerciseReps.placeholder = #"10";
} else if (indexPath.section == 1) {
cell.textLabel.text = #"Log New Set";
cell.textLabel.textAlignment = UITextAlignmentCenter;
cell.accessoryType = UITableViewCellAccessoryNone;
} else if (indexPath.section == 2) {
cell.textLabel.text = #"History";
} else if (cell.textLabel.text == #" Pounds") {
cell.textLabel.textColor = [UIColor grayColor];
cell.textLabel.font = [UIFont systemFontOfSize:14];
} else if (cell.textLabel.text == #" Kilograms") {
cell.textLabel.textColor = [UIColor grayColor];
cell.textLabel.font = [UIFont systemFontOfSize:14];
} else if (cell.textLabel.text == #" Miles") {
cell.textLabel.textColor = [UIColor grayColor];
cell.textLabel.font = [UIFont systemFontOfSize:14];
} else if (cell.textLabel.text == #" Kilometers") {
cell.textLabel.textColor = [UIColor grayColor];
cell.textLabel.font = [UIFont systemFontOfSize:14];
}
return cell;
// [exerciseWeight release];
// [metric release];
// [exerciseReps release];
}
looks like you're going to have some fun here. But it will be satisfying once you're done :-)
I'm not sure the code you posted is the bit you need to be worrying about. Defining what you see in your table (primarily) are methods like heightForRowAtIndexPath, numberOfSectionsInTableView and numberOfRowsInSection. If you get those right you will get the display you want. So when you tap a cell you will be modifying your underlying data structures, then calling reloadData - causing the table view to call those methods all over again.
Regarding laying out those cells I don't see you using the cell's contentView at all. Have you taken a look at the Cocoa With Love blog, specifically Easy custom UITableView drawing and UITableView construction, drawing and management (revisited)? I have also used the xib method of defining table view cells as described in the Apple docs, Table View Programming Guide for iOS and given the way Xcode just keeps improving interface builder's interaction with code would probably continue to do so. I was very much against IB but now see the value in using it since Xcode 4 took away the pain of switching apps to use it, there are a lot fewer opportunities to make mistakes.
What I'm thinking is that when you "add a view" to a cell you should just switch to another cell that you have defined and load the cell, after reloadData, as required. When you want to add cells you change your underlying data structures and let the table display itself.
On the question of cell identifiers - you can either use one cell identifier and save only the cost of creating a cell, factoring out everything common that you do to all your cells, OR you can create cells with a unique identifier per type of cell and reuse cell variant TK421 when your section/row indicates a TK421 is required. I tend to do the second. I don't actually know which one performs better, but I'm sure that either is better than no reuse at all.

Align cells to bottom and top of UITableView, leaving stretchable gap in middle

I have a table view with five cells displaying the settings of my app. I want the first four cells to appear at the top. The last cell isn't actually a setting, it says "Legal" and takes you to the EULA, so I want it to appear at the bottom.
Now I know I could use – tableView:viewForHeaderInSection: and– tableView:heightForHeaderInSection: to create some padding, but I really don't like hardcoding in dimensions this way. I also don't want to use UIButton, because I want it to be exactly the same style as the rest of the cells.
Does anyone know the best practice for going about this?
I'm assuming you're using a grouped table view, like the settings app. What I've done is created a group section between the top and bottom group sections, to which I've add a 'transparent' row cell, that I size dynamically.
I've created a simple demo for you using hardcoded values, but hopefully you'll get the idea. Obviously you would get the number of sections and the number of rows from your data structure:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int rtn = 0;
if (section == 0)
rtn = 4;
else if (section == 1)
rtn = 1;
else if (section == 2)
rtn = 1;
return rtn;
}
/*
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 44.0f;
}
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int TOTALCELLS = 6;
float CONSTANT = 0.5f;
if (indexPath.section == 1)
return self.view.frame.size.height - (44.0f * (CONSTANT + TOTALCELLS));
return 44.0f;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
if (indexPath.section == 0)
{
if (indexPath.row == 0) {
cell.textLabel.text = #"My Switch";
UISwitch *boolSwitch = [[UISwitch alloc]initWithFrame:CGRectZero];
boolSwitch.on = YES;
[cell setAccessoryView:boolSwitch];
[boolSwitch release];
} else if (indexPath.row == 1) {
cell.textLabel.text = #"My TxtField";
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
UITextField *txtField = [[UITextField alloc]initWithFrame:CGRectMake(0,0,175,20)];
txtField.text = #"Default Value";
txtField.textColor = [UIColor darkGrayColor];
[txtField setClearButtonMode:UITextFieldViewModeAlways];
[cell setAccessoryView:txtField];
[txtField release];
} else if (indexPath.row == 2) {
cell.selectionStyle=UITableViewCellSelectionStyleNone;
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(0,0,280,22)];
slider.minimumValue = 0.0f;
slider.maximumValue = 10.0f;
slider.value = 0.7f;
slider.minimumValueImage = [UIImage imageNamed:#"minus.png"];
slider.maximumValueImage = [UIImage imageNamed:#"plus.png"];
[cell setAccessoryView:slider];
[slider addTarget:self action:#selector(sliderChange:forEvent:) forControlEvents:UIControlEventValueChanged];
[slider release];
} else if (indexPath.row == 3) {
cell.textLabel.text = #"My MultiValue";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = #"Default selection";
}
} else if (indexPath.section == 1) {
UIView *transCell = [[UIView alloc] initWithFrame:CGRectZero];
transCell.backgroundColor = [UIColor clearColor];
cell.backgroundView = transCell;
[transCell release];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
} else if (indexPath.section == 2) {
cell.textLabel.text = #"Legal";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
The critical parts are in cellForRowAtIndexPath, where the transparent row is created. The trick here is to add the transparent view to the cell's backgroundView, and set the cell selection style to None.
UIView *transCell = [[UIView alloc] initWithFrame:CGRectZero];
transCell.backgroundColor = [UIColor clearColor];
cell.backgroundView = transCell;
[transCell release];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
and in heightForRowAtIndexPath, the dynamic sizing of the transparent row.
self.view.frame.size.height - (44.0f * (CONSTANT + TOTALCELLS));
TOTALCELLS is the total of all of the cells, including the transparent one. You may need to do additional work on this algorithm, as it doesn't take into consideration what happens when number of cells grows beyond what is visible, and I haven't tested it with various combinations of navigation and toolbars.
I hope this works for you.