I'm a beginner in iOS and I try to get the value of my uiTextField that I created programmatically.
So my problem is that I create a form in an UITableViewController with a .json file. I have created my form, but I don't know how I can get the different values.
In my cellForRowAtIndexPath I created my different elements (I have textFields, textFields with picker and uiswitch). When the user have finished to fill out the form, he click on a "save" button and it is here that I want to get my values.
If someone can help me and put some code for explain .... :) Thanks.
some of 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:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSString *cellValue = [self getItemName:indexPath.section and:indexPath.row];
cell.textLabel.text = cellValue;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSString *itemType=[self getItemType:indexPath.section and:indexPath.row];
NSLog(#"%#",itemType);
if ([itemType isEqualToString:#"text"]) {
UITextField *Reference = [self createTextField];
[cell.contentView addSubview:Reference];
}else if ([itemType isEqualToString:#"checkbox"]) {
UIView *checkbox = [self createCheckbox:indexPath.section and:indexPath.row];
[cell.contentView addSubview:checkbox];
}else if ([itemType isEqualToString:#"picker"]) {
UITextField *Reference = [self createPicker:indexPath.section and:indexPath.row];
[cell.contentView addSubview:Reference];
}else if ([itemType isEqualToString:#"datePicker"]) {
UITextField *Reference = [self createDatePicker];
[cell.contentView addSubview:Reference];
}
return cell;
}
it is here that i create dynamically the elements. For example an uixtField :
-(UITextField*)createTextField{
UITextField *Reference = [[UITextField alloc] initWithFrame:CGRectMake(200 , 10, 200, 40)];
Reference.layer.borderColor=[[UIColor blackColor]CGColor];
Reference.layer.borderWidth=1.0f;
Reference.textAlignment=NSTextAlignmentCenter;
Reference.tag=_tag;
_tag++;
return Reference;
}
http://hpics.li/04b98ca
here it is an example of that i obtained with my dynamical generation.
Use:
[textField setTag:(integer value)]
when you creating fields in cellForRow... method.
Then use in saveMethod this:
[[tableView cellForIndexPath:indexPath] viewWithTag:(integer value)]
for get concrete field of concrete cell.
if ([itemType isEqualToString:#"text"]) {
UITextField *Reference = [self createTextField];
[Reference setTag:1];
[cell.contentView addSubview:Reference];
}
-(void)saveMethod
{
NSString *string = [[[self.tableView cellForRowAtIndexPath:indexPath] viewwithTag:1] text];
}
You can create elements in the method cellForRowAtIndexPath for your cells if after you want to get info from them, because it will be very difficult to get it.
The best way is creating your custom UITableViewCell with all elements you want and after, in the method didSelectRowAtIndexPath to get the info of your elements.
The way I get an indexPath from a view is by checking the superview until I get the parent cell, then using -indexPathForCell:.
- (UITableViewCell *)cellForView:(UIView *)view
{
while (view != nil && ![view isKindOfClass:[UITableViewCell class]]) {
view = view.superview;
}
return (UITableViewCell *)view;
}
- (NSIndexPath *)indexPathForView:(UIView *)view
{
return [self.tableView indexPathForCell:[self cellForView:view]];
}
Overall, this solution will run into problems. Cells do not persist in a table view. As soon as the cell is off the screen it is recycled by the table view.
This means you should not attempt to store data in a cell. A cell is used to present data and get user input, but never store data. Data is meant to be stored in the table data source.
In addition, it means you need to be careful when adding subviews to a cell. As the cell get recycled, the views you added in it's previous use will still be in the cell. You will need to have a cleanup section to -tableView:cellForRowAtIndexPath: to remove all the views you previously added.
My recommendation is to subclass UITableViewCell. You can either have 4 subclasses, one for each JSON type you have, or have 1 subclass which can be instantiated with in the 4 different ways. In my example, I have 1 subclass that can be instantiated 4 different ways.
Keep the different types of cells differentiated by using different reuse identifiers.
Use target/actions and delegates to get input from the controls on the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = nil;
NSString *itemType=[self getItemType:indexPath.section and:indexPath.row];
NSLog(#"%#",itemType);
if ([itemType isEqualToString:#"text"]) {
cellIdentifier = #"TextCell";
} else if ([itemType isEqualToString:#"checkbox"]) {
cellIdentifier = #"CheckboxCell";
} else if ([itemType isEqualToString:#"picker"]) {
cellIdentifier = #"PickerCell";
} else if ([itemType isEqualToString:#"datePicker"]) {
cellIdentifier = #"DatePickerCell";
}
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
if ([cellIdentifier isEqualToString:#"TextCell"]) {
cell = [MyTableViewCell textCellWithIdentifier:cellIdentifier];
} else if ([cellIdentifier isEqualToString:#"CheckboxCell"]) {
cell = [MyTableViewCell checkboxCellWithIdentifier:cellIdentifier];
} else if ([cellIdentifier isEqualToString:#"PickerCell"]) {
cell = [MyTableViewCell pickerCellWithIdentifier:cellIdentifier];
} else if ([cellIdentifier isEqualToString:#"DatePickerCell"]) {
cell = [MyTableViewCell datePickerCellWithIdentifier:cellIdentifier];
}
}
NSString *itemName = [self getItemName:indexPath.section and:indexPath.row];
cell.textLabel.text = itemName;
if ([itemType isEqualToString:#"text"]) {
NSString *itemValue = [self getItemValue:indexPath.section and:indexPath.row];
cell.textField.text = itemValue;
} else if ([itemType isEqualToString:#"checkbox"]) {
BOOL itemChecked = [self getItemChecked:indexPath.section and:indexPath.row];
cell.checkbox.selected = itemChecked;
} else if ([itemType isEqualToString:#"picker"]) {
NSString *itemValue = [self getItemValue:indexPath.section and:indexPath.row];
cell.pickerLabel = itemValue;
} else if ([itemType isEqualToString:#"datePicker"]) {
NSDate *itemDate = [self getItemDate:indexPath.section and:indexPath.row];
cell.datePickerLabel = itemDate;
}
return cell;
}
For my problem, I have create a NSMutableDictionaryin which i put my element like that :
key : "name"
value : "UITextField"
Like that i can get the textField everywhere in my class.
in .h file
create the dictionary
in .m
just put the element and when you want to go the text value do :
UITextField* myTextField= [dictionnary objectForKey:key];
Related
I have a tableviewcontroller that has dynamic controls created in cells. If it's a dropdown type, I take the user to a different tableviewcontroller to select the value. Once selected, I pop back and reload the data, but when I do that it overwrites the cells on top of one another. I know this is because I'm reusing the cells, but I cannot seem to figure out how to prevent it.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
EWHInboundCustomAttribute *ca = [visibleCustomAttributes objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.tag=indexPath.row;
if (ca.CustomControlType == 1) {
cell.detailTextLabel.hidden=true;
cell.textLabel.hidden=true;
UITextField *caTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)];
caTextField.adjustsFontSizeToFitWidth = YES;
caTextField.textColor = [UIColor blackColor];
caTextField.placeholder = ca.LabelCaption;
if (ca.ReadOnly) {
[caTextField setEnabled: NO];
} else {
[caTextField setEnabled: YES];
}
caTextField.text=nil;
caTextField.text=ca.Value;
caTextField.tag=indexPath.row;
caTextField.delegate=self;
[cell.contentView addSubview:caTextField];
} else if (ca.CustomControlType == 4) {
cell.detailTextLabel.text=ca.Value;
cell.textLabel.text=ca.LabelCaption;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.detailTextLabel.hidden=true;
cell.textLabel.hidden=true;
UITextField *caTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)];
caTextField.adjustsFontSizeToFitWidth = YES;
caTextField.textColor = [UIColor grayColor];
caTextField.placeholder = ca.LabelCaption;
[caTextField setEnabled: NO];
caTextField.text = ca.Value;
caTextField.tag=indexPath.row;
caTextField.delegate=self;
[cell.contentView addSubview:caTextField];
}
return cell;
}
Instead of creating the UITextfield each time I would suggest at least using [UIView viewWithTag:tag] to capture the same UITextField object.
I'd suggest you to create custom UITableViewCell subclass and put all subviews related logic there.
Next, in order to reset/clear cell before reuse - you should override prepeareForReuse function.
Swift:
override func prepareForReuse() {
super.prepareForReuse()
//set cell to initial state here
}
First,I suggest you to use custom cells.If not and your cells are not so many,maybe you can try unique cell identifier to avoid cell reuse:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// unique reuseID
NSString *cellReuseID = [NSString stringWithFormat:#"%ld_%ld", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellReuseID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellReuseID];
// do something
}
return cell;
}
Hope it's helpful.
I am trying to keep the selected state of multiple cells on a didSelectRowAtIndexPath method. I have an edit button that I've set up that loops through every cell to select each field on my UITableView.
Here is the code for the edit button on tap that selects all my rows.
- (IBAction)editButtonTapped:(id)sender {
for (int i = 0; i < self.caseDataTableView.numberOfSections; i++) {
for (NSInteger r = 0; r < [self.caseDataTableView numberOfRowsInSection:i]; r++) {
[self tableView:caseDataTableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:i]];
}
}
}
When calling the didSelectRowAtIndexPath method, it does the following code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
OKOperatieNoteTableViewCell *cell = (OKOperatieNoteTableViewCell *)[self.caseDataTableView cellForRowAtIndexPath:indexPath];
cell.cellIndexPath = indexPath;
[cell hideLabelAndShowButtons];}
Incase you were wondering here is the hideLabelAndShowButtons method.
- (void)hideLabelAndShowButtons {
self.caseDataKeyLabel.hidden = NO;
if (!self.disabled) {
self.caseDataValueLabel.hidden = YES;
self.textField.hidden = NO;
if ([self.inputType isEqualToString:#"switcher"] || [self.inputType isEqualToString:#"multiselect"] || [self.inputType isEqualToString:#"picker"] || [self.inputType isEqualToString:#"DatePicker"] || [self.inputType isEqualToString:#"selectContact"]) {
self.button.hidden = NO;
}else {
self.button.hidden = YES;
}
}
self.caseDataDescriptionTextView.hidden = YES;}
Now at this point, I have all my rows selected. If I scroll down and then back up the selection of these rows is not there anymore. Now I'm aware when you go in and out of the view, the cellForRowAtIndexPath method recreates these cells. The following is my cellForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"caseData";
OKOperatieNoteTableViewCell * cell = [[OKOperatieNoteTableViewCell alloc]init];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (indexPath.row < _procedureVariables.count) {
if ([[[_caseDataArray objectAtIndex:indexPath.row] valueForKey:#"key"] isEqualToString:#"Procedure"]) {
[cell setLabelsWithKey:[[_caseDataArray objectAtIndex:indexPath.row] valueForKey:#"key"] AndValue:[self.model valueForKey:#"var_procedureName"]];
}else {
[cell setLabelsWithKey:[[_caseDataArray objectAtIndex:indexPath.row] valueForKey:#"key"] AndValue:[[_caseDataArray objectAtIndex:indexPath.row] valueForKey:#"value"]];
}
OKProcedureTemplateVariablesModel *variableModel = _procedureVariables[indexPath.row];
cell.variable = variableModel.value;
[cell showLabelAndHideButtons];
cell.delegate = self;
[cell setUpCellType];
} else if (indexPath.row == _procedureVariables.count) {
NSString *text = [NSString stringWithFormat:#"%# \n\n %#", [_templateDictionary objectForKey:#"indicationText"], [_templateDictionary objectForKey:#"procedureText"] ];
[cell showDescription:text];
NSLog(#"cell.caseDataDescriptionTextView.font.fontName = %#", cell.caseDataDescriptionTextView.font.fontName);
}
cell.procedureID = _procedureID;
[tableView setContentInset:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)];
return cell;
}
I'm just trying to figure out how to keep the selected state of these cells once the cellForRowAtIndexPath method is called. Any suggestions are welcomed.
i tried to simulate your situation, created a customCell and saved the indexpaths of selectedRows in my custom selectedPaths mutable array(initialized in viewDidLoad).
After every click i removed or added related indexpath to my array.
it worked for my case. Hope it helps.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"caseData";
NOTableViewCell *cell = (NOTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSLog(#"new cell created for row %d", (int)indexPath.row);
cell = [[[NSBundle mainBundle] loadNibNamed:#"NOTableViewCell" owner:self options:nil] objectAtIndex:0];
}
if ([selectedPaths indexOfObject:indexPath] != NSNotFound) // this cell is in selected state.
{
[cell.textLabel setText:#"This cell selected"];//selected state job.
return cell;
}
[cell.textLabel setText:[NSString stringWithFormat:#"%d", (int)indexPath.row]];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([selectedPaths indexOfObject:indexPath] != NSNotFound) {
[selectedPaths removeObject:indexPath];
}
else{
[selectedPaths addObject:indexPath];
}
//[tableView reloadData];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];//instead of reloading all just reload clicked cell.
}
You need to update the cell to selected and not selected explicitly in both directions in cellForRowAtIndexPath.
If not, the recycled cells will just show the value of the cell the cell was last used for until you change it.
While you are invoking the delegate method in order to call hideLabelAndShowButtons, you aren't telling the table view that you have selected the row;
- (IBAction)editButtonTapped:(id)sender {
for (int i = 0; i < self.caseDataTableView.numberOfSections; i++) {
for (NSInteger r = 0; r < [self.caseDataTableView numberOfRowsInSection:i]; r++) {
NSIndexPath *path=[NSIndexPath indexPathForRow:r inSection:i];
[caseDataTableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
[self tableView:caseDataTableView didSelectRowAtIndexPath:path];
}
}
}
Also, you aren't using the cell selection state in cellForRowAtIndexPath, so you probably need to change some code there too, but I am not sure what the relationship is between selected state and how you want to render the cell.
I am using a array containing objects from two dictionaries and displaying objects of first dictionary on first cell having different identifier and same with second dictionary objects.
check out my code in cellforrowatindexpath
static NSString *cellIdentifier = #"cellIdentifier1";
static NSString *customCellIdentifier = #"cellIdentifiercustom";
if (indexPath.row == 0) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// if (cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
ExcName = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 2.0, 250.0, 36.0)];
ExcName.font = [UIFont systemFontOfSize:12.0];
ExcName.textColor = [UIColor blackColor];
ExcName.textAlignment = NSTextAlignmentLeft;
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"activityname"];
ExcName.numberOfLines=3;
[cell.contentView addSubview:ExcName];
return cell;
}
else{
UITableViewCell *customcell = [tableView dequeueReusableCellWithIdentifier:customCellIdentifier];
// if (cell==nil)
{
customcell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:customCellIdentifier];
}
customcell.selectionStyle = UITableViewCellSelectionStyleNone;
ExcName = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 2.0, 250.0, 36.0)];
ExcName.font = [UIFont systemFontOfSize:12.0];
ExcName.textColor = [UIColor blackColor];
ExcName.textAlignment = NSTextAlignmentLeft;
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
// ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
ExcName.numberOfLines=3;
[customcell.contentView addSubview:ExcName];
return customcell;
}
return nil;
}
now I want if any dictionary is null, cell corresponding to that dictionary should not be visible which is visible now.
A few things:
Don't return nil from -tableView:cellForRowAtIndexPath:. That method gets called exactly once for each (visible) row, and the table assumes it has as many rows as you told throught the return value of -tableView:numberOfRowsInSection:. Defaulting to nil reveals an inconsistency in your code.
By that token, cell dequeueing should never fail. I recommend that you switch to the newer method: -dequeueReusableCellWithIdentifier:forRowAtIndexPath:. It takes care of allocation of new cells for you when there is no cell in the reuse pool.
Cells are being reused. A single cell may be reuse multiple times: If you add a new label each time, they will pile up. Instead, add the label to your cells in interface builder and set up an outlet to reference them when configuring the cell.
The Solution:
Instead of "skipping rows", make your data model consistent with what you are going to display. If you have an array with objects some of which you want to skip, first filter your array and then used the filtered array as data source.
// Call this method once before reloading the table view
- (void) filterDictionaries
{
// (filteredArray is an ivar defined as NSMutableArray*)
for (NSDictionary* dictionary in exerciseAr) {
if ([dictionary objectForKey:#"exercisename"] != nil) {
[filteredArray addObject: dictionary];
}
}
// Now you can use filteredArray as the data source
// instead of exerciseAr, without worrying about skipping
// entries.
}
- (NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger) section
{
return [filteredArray count];
}
- (UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
static NSString *cellIdentifier = #"cellIdentifier1";
static NSString *customCellIdentifier = #"cellIdentifiercustom";
UITableViewCell* cell;
if (indexPath.row == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forRowAtIndexPath:indexPath];
}
else{
cell = [tableView dequeueReusableCellWithIdentifier:customCellIdentifier forRowAtIndexPath:indexPath];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.excLabel.text = [[filteredArray objectAtIndex:indexPath.row]
return cell;
}
It actually depends what you meant by your dictionary being null. It may be nil, or it just doesn't have any values for the keys e.g the values for the key is an empty string. So, to add to NibolasMiari's answer, I would say, check both of the cases.
- (void) filterDictionaries:(NSArray*) exerciseAr{
filteredArray = [[NSMutableArray alloc]init];
for (NSDictionary* myDict in exerciseAr) {
if ([myDict isKindOfClass:[NSNull class]]) {
continue;
}
else if ([myDict count] == 0) {
continue;
}
else{
[filteredArray addObject: myDict];
}
}
}
- (NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger) section
{
return [filteredArray count];
}
Now in your cellforIndexPath method everything will be same just changed the lines following way-
In the if block, you had
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"activityname"];
Make it -
ExcName.text=[[filteredArray objectAtIndex:indexPath.row] objectForKey:#"activityname"];
In the else block, you had
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
Make it -
ExcName.text=[[filteredArray objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
I have a UIButton inside a UITableViewCell. When the app is first launched, it works as expected, where I created it's frame.
When I scroll pass the cell which holds the button, it creates a second instance of the button slightly below the button.
Here's a video to illustrate my problem: http://pixori.al/DJ1k
Here's the code for the UITableViewCell and also how I populate the cells.
Not sure why it's behaving like this.
#pragma mark - UITableViewDataSource
// 3 sections, (1 = mistarOverview) (2 = hourlyForecast) (3 = dailyForecast)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0) {
return MAX(6,6) + 1; //TODO add getNumberOfClasses for people with 7 or 8 classes
} else if (section == 1) {
return MIN([[MAManager sharedManager].hourlyForecast count], 6) + 1;
} else {
return MIN([[MAManager sharedManager].dailyForecast count], 6) + 1;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Redefine layout variables in method from `viewDidLoad`
CGFloat inset = 20; // For padding
if (! cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
// Sets up attributes of each cell
cell.selectionStyle = UITableViewCellSelectionStyleNone; //TODO none
cell.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2];
cell.textLabel.textColor = [UIColor whiteColor];
cell.detailTextLabel.textColor = [UIColor whiteColor];
QBFlatButton* loginButton = nil;
if (indexPath.section == 0) {
if (indexPath.row == 0) {
[self configureHeaderCell:cell title:#"Grades"];
if ([cell.textLabel.text isEqual: #"Grades"] && (!loginButton) && (indexPath.row == 0) && (indexPath.section == 0)) {
UIView *cellView = cell.contentView;
CGRect loginButtonFrame = CGRectMake((cellView.frame.size.width - (80 + inset)), 18, 80, (cellView.frame.size.height));
loginButton = [[QBFlatButton alloc] initWithFrame:loginButtonFrame];
[loginButton addTarget:self action:#selector(loginButtonWasPressed)forControlEvents:UIControlEventTouchUpInside];
loginButton.faceColor = [UIColor grayColor];
loginButton.sideColor = [UIColor clearColor];
loginButton.radius = 6.0;
loginButton.margin = 4.0;
loginButton.depth = 3.0;
loginButton.alpha = 0.3;
loginButton.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20];
[loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[loginButton setTitle:#"Login" forState:UIControlStateNormal];
[cellView addSubview:loginButton];
}
} else {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.textLabel.text = [NSString stringWithFormat:#"Period %ld A+", (long)indexPath.row];
cell.detailTextLabel.text = #"Class name";
//TODO get grades and config using method (TB Created)
}
} else if (indexPath.section == 1) {
if (indexPath.row == 0) {
[self configureHeaderCell:cell title:#"Hourly Forecast"];
}
else {
// Get hourly weather and configure using method
MACondition *weather = [MAManager sharedManager].hourlyForecast[indexPath.row - 1];
[self configureHourlyCell:cell weather:weather];
}
}
else if (indexPath.section == 2) {
if (indexPath.row == 0) {
[self configureHeaderCell:cell title:#"Daily Forecast"];
}
else if (indexPath.section == 2) {
// Get daily weather and configure using method
MACondition *weather = [MAManager sharedManager].dailyForecast[indexPath.row - 1];
[self configureDailyCell:cell weather:weather];
}
}
return cell;
}
Implement the following UITableView Delegate Method
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
//In here, check the index path. When you have the cell that contains the button, pop it out from there by using [button removeFromSuperView];
}
Your problem occurs when you dequeue the cell. Since the cell is being reused, it already has the button and you're simply re-adding it again. This will solve your issue. However, I'd recommend you create a subclass for the UITableViewCell, and in it's prepareForReuse method, pop the button out. Up to you. Both will work.
Table view cells are not just deallocated then they move out of visible area. They are stored for reusing and then returned in tableView dequeueReusableCellWithIdentifier:CellIdentifier];
So you need to clean your cells after using or before reusing. There are several ways:
1.Add tag to your button when you create it
loginButton.tag = SOME_TAG;
just after
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
search for view with this tag
loginButton = [cell viewWithTag:SOME_TAG];
if loginButton != nil you can reuse it or remove from cell and then create a new one.
2.Implement UITableViewDelegate method
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
and erase login button inside it.
3.Create custom UITableViewCellclass and implement prepareForReuse method.
You're adding the button every time you return a cell in this method. If you scroll the cell off the screen and back on, this method is called again for the same index path, and you will add the button again.
You declare the variable, do nothing with it, then check if it is nil. It will always be nil, so you always add the button.
A quick and dirty solution is to give the button a tag, then check for its existence using viewWithTag:.
A better solution is to make a custom cell subclass, and set one-time properties like this in the init method. Your cell contents seem very different for each section as well, so use different reuse identifiers for each section, and possibly a different cell subclass. Clearing out sub views is expensive and could hurt your scrolling performance.
When you run your project first time then cellForRowAtIndexPath is called.....
Then whenever you scroll tableView it again calls cellForRowAtIndexPath and reload data automatically..
So you have to take CellIdentifier as unique for each cell.
you have to remove static keyword from
static NSString *CellIdentifier = #"CellIdentifier";
now you have
NSString *CellIdentifier = #"CellIdentifier";
only this things
Now you have to write like below
NSString *CellIdentifier = [NSString stringWithFormat:#"%#",indexPath];
Now Enjoy.....
I am trying to populate cells in table view ( I have two custom types of cells with with different elements created in storyboard, with identifiers "info_cell" and "person_cell", on segmented control above UITableView I decide what to load [tableView reload]). When I try to access UILabels inside cell I get that labels are null.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = (viewType == INFO_VIEW) ? #"info_cell" :#"person_cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(viewType == INFO_VIEW){
NSLog(#"INFO = %#", info_text_some_string);
UILabel *lblInfo = (UILabel *)[cell viewWithTag:200];
[lblInfo setText:info_text_some_string];
}
else{
// there is part for person
}
return cell;
}
Same code works when I have just one prototype cell inside table (UITableView is inside UIVewController). What can be problem here, I have checked 100 times: cell identifiers are OK, label tag is 200.
This is action for UISegmentControl
- (IBAction)changeView:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *) sender;
NSInteger selectedSegment = segmentedControl.selectedSegmentIndex;
if (selectedSegment == 0) {
viewType = INFO_VIEW;
}
else{
viewType = PERSON_VIEW;
}
[tableView reloadData];
}
I have added and necessary methods for tableView and connect delegate i datasource.
Does anyone have any idea why it is null ?
Try this usually i follow this process whenever i go with custom cell in CellRorRowAtIndexPath
if(!cell)
{
cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
//customcell is your UITableViewCell created by you in ur xib
NSData *archivedData =[NSKeyedArchiver archivedDataWithRootObject:customcell];
cell =[NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
}
if(viewType ==INFO_VIEW)
{
[(UILabel *)[cell viewWithTag:200] setText:#"you text"];
}
else{
// person view....
}
This way your collecting all your elements of your cell and setting value for it. share your results please
Assuming that you have subclass your UITableViewCells correctly (I use InfoCell and PersonCell for example), you can try this:
if(viewType == INFO_VIEW)
{
InfoCell *cell = (InfoCell *)[tableView dequeueReusableCellWithIdentifier:#"info_cell"];
// do your stuff for info here
}
else if(viewType == PERSON_VIEW)
{
PersonCell *cell = (PersonCell *)[tableView dequeueReusableCellWithIdentifier:#"person_cell"];
// do your stuff for person here
}
Why not do something like this?
[[cell textLabel] setText: #"text goes here"];
And skip the UILabel part?