Editable table view - ios

I have a tableview for entering the ingredients of a recipe in a recipe app. I want to be able to type in the ingredients in the tableview cell and then save it to display in another view controller - UILabel (dynamically created).
I found code to make an editable tableview where data can be inserted and deleted, but no code to be able to type in a tableview cell.
Is there a way to be able to type in a tableview cell? What is the best way to do it?
Additionally, all data entered needs to stored in the database using core data.
Here is a screenshot of my storyboard:
The tableview in the AddRecipeViewController is where I want to enter data. When I click save, the data should be displayed in RecipeDetailViewController Ingredients UIView.
Update:
I have created IngrdientsTableViewCell class for custom cell with a UITextField for entering text. But when I compile, the tableview does not display any textfield to type in. I have no idea what I am doing wrong. Please help!
Updated code:
IngrdientsTableViewCell.h
#interface IngredientsTableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UITextField *ingredientTextField;
#end
IngrdientsTableViewCell.m
- (void)awakeFromNib
{
// Initialization code
self.ingredientTextField = [[UITextField alloc]initWithFrame:CGRectMake(5, 5, 320, 39)];
self.ingredientTextField.autoresizingMask=UIViewAutoresizingFlexibleHeight;
self.ingredientTextField.autoresizesSubviews=YES;
self.ingredientTextField.layer.cornerRadius=10.0;
[self.ingredientTextField setBorderStyle:UITextBorderStyleRoundedRect];
[self.ingredientTextField setPlaceholder:#"Type Data Here"];
}
AddRecipeViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.ingredientItems.count;
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Ingredient Cell";
IngredientsTableViewCell *ingredientCell = [self.ingredientsTableView dequeueReusableCellWithIdentifier:cellIdentifier];
// NSManagedObjectContext *managedObject = [self.ingredientItems objectAtIndex:indexPath.row];
if (ingredientCell == nil)
{
ingredientCell = [[IngredientsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
ingredientCell.accessoryType = UITableViewCellAccessoryNone;
}
[ingredientCell addSubview:ingredientCell.ingredientTextField];
return ingredientCell;
}
Thanks for any help.

You should have a custom UITableViewCell with a UITextView in it.

You can add a textField/View to your tableViewCell and just handle the text events like you normally would. I do something similar with TextFields.
UITextField *textField;
cell.textLabel.text = NSLocalizedString(#"Name", #"Name");
textField = self.name = [self makeTextField:self.selectedUser.name];
textField.delegate = self;
self.name.autocapitalizationType = UITextAutocapitalizationTypeWords;
self.name.keyboardType = UIKeyboardTypeDefault;
self.name.returnKeyType = UIReturnKeyNext;
self.name.tag = indexPath.row;
[cell addSubview:self.name];
You will have to format the text field with constraints depending on your setup, but ultimately you will want to do this.

Related

UILabel appears multiple times in UITableViewCell

I'm currently creating my UITableViewCells programmatically like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
UILabel *newLabel = [[UILabel alloc] initwithframe:cell.frame];
[newLabel setText:self.data[indexPath.row]];
[cell addSubview:newLabel];
return cell;
}
This seems to create a new UILabel each time the cell is reused though, which I definitely don't want. I tried doing the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
if (cell == nil) {
UILabel *newLabel = [[UILabel alloc] initwithframe:cell.frame];
[cell addSubview:newLabel];
}
[newLabel setText:self.data[indexPath.row]];
return cell;
}
but then the UILabel seems to never be created. Perhaps this is because I'm using prototype cells with Storyboard and thus the cells are never nil?
You have two solutions.
Create a custom table view cell that already has the label.
If you want to add the label in code, don't register a class for the cell. Then the dequeued cell can be nil and you can add the label at that time (like in your 2nd set of code). This also requires using the dequeueReusableCellWithIdentifier method that doesn't also take an indexPath.
You should create a UITableViewCell subclass and add "newLabel" as a property.
The cell is never nil because the method you use to dequeue the table view cell always returns a cell, creating one if it doesn't already exist in the reuse queue.
A better solution would be to create the label in the cell prototype in the storyboard.
This implementation is against MVC architecture where controller managers stuff and do not deal with view. Here, you are trying to add stuff in view from controller. It is suggested to subclass UITableViewCell as below and add your custom UI controls in there
MyTableViewCell.h
#interface MyTableViewCell : UITableViewCell {
}
#property (nonatomic, strong) UILabel *myLabel;
#end
Then you can implement layoutSubviews in your MyTableViewCell.m file to define the look and feel of your cell.
MyTableViewCell.m
- (id)initWithStyle:(UITableViewCellStyle)iStyle reuseIdentifier:(NSString *)iReuseIdentifier {
if ((self = [super initWithStyle:iStyle reuseIdentifier:iReuseIdentifier])) {
self.myLabel = [[UILabel alloc] initWithFrame:<Your_Frame>];
// Set more Label Properties
[self.contentView addSubview:self.myLabel];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
// Override run time properties
}
Finally use your custom cell like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Home-Cell"];
}
cell.myLabel.text = self.data[indexPath.row];
return cell;
}
As a side note, I hope you know that you get textLabel and detailTextLabel free from default UITableViewCell implementation.

How to Change selected Cell data permanently?

I want to change the selected cell data permanently as i have done in my didSelectRowAtIndexPath method but the problem is that when I select a row the cell data is change but when i select any other row the previous become as it was, and I also want to save rows in an array, those been selected in an array. here is my code right now.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
#try {
static NSString *cellidentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellidentifier];
if(cell == nil)
{
NSArray *cellObjects = [[NSBundle mainBundle] loadNibNamed:#"Cell" owner:self options:nil];
cell = (UITableViewCell*) [cellObjects objectAtIndex:0];
}
UILabel *label;
long row = [indexPath row];
label = (UILabel *)[cell viewWithTag:10];
label.text =time[row];
label.textColor = [UIColor blackColor];
cell.imageView.image = [img_clock_blue objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor yellowColor];
[tableView setSeparatorInset:UIEdgeInsetsZero];
//int hecValue;
return cell;
}
#catch (NSException *exception)
{
NSLog(#"%#",exception);
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView reloadData];
UITableViewCell *cell1 = (UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
cell1.imageView.image = [UIImage imageNamed:#"1_red.png"];
cell1.backgroundColor = [UIColor clearColor];
}
You're modifying the cell, which is a bad idea. You need to modify the place where it's getting its data.
in your didSelectRowAtIndexPathjust find the objectAtIndex:in the array, modify it to your will, then reload the table.
If you only have, for example, titles (NSStrings), then an array of strings will suffice. But most of the time it won't, because you're displaying something custom.
it looks like you don't have a custom class here, so I'll just make an example that you can translate easily. Let's say you're tryign to display a list of Animal objects.
Create your Animal class inheriting from NSObject. (New file, class, and so on).
Add the properties you will need in the Animal.h file, for example
#property (weak, nonatomic) NSString *name;
#property (nonatomic) int size;
#property (nonatomic) int weight;
#property (weak, nonatomic) NSString *countryOfOrigin;
You'll also technically need a class to create/manage/fetch/save these Animal objects but let's keep it simple and do it in the viewDidLoad of your controller.
- (void)viewDidLoad{
[super viewDidLoad];
Animal *myAnimal = [[Animal alloc]init];
myAnimal.name = #"Lion";
myAnimal.size = 13;
myAnimal.weight = 100;
myAnimal.countryOfOrigin = #"NoIdeaHahahah";
// You can hardcode a couple like that, and add them to your array used for your tableview data. Basically we just want some of your custom objects in an array, for your tableview.
}
Ok so now we have an array of Animal (our data) for your tableview. You can use that to create your rows.
When creating the cell in the cellForRow, simply start with :
Animal *animal = [myArray objectAtIndex:indexPath.row];
and then feed your cells with the properties of that animal
cell.titleLabel.text = animal.name;
for example.
And in the didSelect you can modify that specific animal, like I said at the very beginning of this answer :)
Animal *animal = [myArray objectAtIndex:indexPath.row];
animal.name = #"IJustChangedTheName";
[self.tableView reloadData];
All this is common practice, except what we did in the viewDidLoad that is very brutal, but I'm sure you'll be able to adapt that to your code :)
Try this,
create a NSMutableArray #property in view controller. lets say selectedIndexArray
initialize the array in viewDidLoad by self.selectedIndexArray = [[NSMutableArray alloc] init];
in cellForRowAtIndexPath method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//other codes
if ([self.selectedIndexArray containsObject:indexPath]) {
cell.imageView.image = [UIImage imageNamed:#"1_red.png"]; //assumes all selected cells have same image
} else {
cell.imageView.image = [img_clock_blue objectAtIndex:indexPath.row];
}
.....//other code
}
in didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.selectedIndexArray addObject:indexPath];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
The code for setting up cell contents should all be in cellForRowAtIndexPath:.
You should create a real data model to represent the contents of your cells instead of the time array. Create an array of custom objects (or dictionaries) with properties such as "time" and "selected". Use indexPath.row to find the correct object and then use its "selected" property to decide which kind of image to give it.
didSelectRowAtIndexPath: sets "selected" YES or NO and doesn't need to change the cell at all.

Disabling a UIButton in a UITableViewCell with cell reusability

I have a UIButton in each of my UITableViewCells. When the button is pressed I am disabling it so that the user cannot press it again (it is a like button). However, when the user scrolls passed a cell and the scrolls back to the cell the button is selectable once again. I'm guessing this is because the cell is redrawn when the user comes back to it, resetting the button. Is there a way I can avoid this? My code is below
Code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifer = #"cellName";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
UIButton *ilikeit = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[ilikeit addTarget:self action:#selector(like:) forControlEvents:UIControlEventTouchUpInside];
ilikeit.frame = CGRectMake(55, h+70, 45, 25);
[cell addSubview:ilikeit];
return cell;
}
-(void) like:(id) sender {
((UIButton *)sender).enabled = NO;
}
You can store the button state in your data model for the tableview class.
Say the TableView is loading data from a class MyData which looks like this:
#interface MyData: NSObject
// Your other data here such as strings etc
NSString *otherData;
// Here add a selected flag
BOOL selected;
#end
in the TableViewDelegate, read and write to this model.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyData *thisCellData = [yourGlobalMyDataArray objectAtIndex:indexPath.row];
// set other data
if (thisCellData.selected){
// Hide the button
// Do other stuff as needed for this button
}
else {
// Show the button
// Do other stuff as needed for this button
}
}
The same data model should be updated when user selects the button.
Hope this helps.
Problem- 1 You are creating your button every time cellForRowAtIndexPath gets called.
Problem- 2 You have no code to keep the state of your button with cell reusability, that's why it is refreshed when cell is going for reuse.
Solution- For this you have to keep your selected button state. Make a global NSInteger variable. And set its value as sender.tag value in like: method. And use -
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
// here create your UIButton
// Set its tag to indexPath.row
// add this to cell as subview
}
// find button of current cell using tag
UIButton *btn = (UIButton*)[cell.contentView viewWithTag:indexPath.row];
// place your check here if button tag matches with selected tag then disable btn here.
// Else use setEnable with YES parameter.
}

Managing UITableViewCell selected states

I have UITableViewController with a static table view and 2 sections. The first section has two cells, with a layout Table View Cell > Content View > Text Field.
The second section of table view cells has a layout that is Table View Cell > Content View > Label - and the cells expand when they're selected by using beginUpdates and endUpdates and reporting cell heights via tableView:heightForRowAtIndexPath:.
Right now, when I touch one of the first sections' cells, the keyboard comes up and the cursor is placed in the text field, but the cell is not selected - which means that if I had previously selected one of the cells in the second section, it would remain selected (and therefore expanded).
Also, if I have selected a cell in the first section with the text field given first responder, then I select a cell in the second section, the cell expands, but the first cell does not resign first responder and dismiss the keyboard.
Did I layout my table view incorrectly such that the cell selected states are not managed automatically and if not: what is the proper way to manage the selected states of the UITableViewCells?
I dont know how you have written the code. But from above information I have created one sample application. The code is as below and it is working as you want.
I have considered the table with 2 section.
First section has the textfield on Cell.
Second section has the UIlabel on the cell.
txtFieldCopy is of type UITextField declared in .h of class.
Please check the way I have assigned the Tag to the view as we need it to get the indexpath.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellStyle style =UITableViewCellStyleSubtitle;
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
int iTag=[indexPath row]+1;
txtFieldCopy=nil;
if(indexPath.section==1)
{
UILabel *lblFirst=nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
lblFirst = [[UILabel alloc] initWithFrame:CGRectMake(45, 0, 100,
40)] ;
lblFirst.tag=iTag;
[cell.contentView addSubview:lblFirst];
lblFirst=nil;
}
lblFirst = (UILabel *)[cell.contentView viewWithTag:iTag];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
lblFirst.text =#"Label 1";
}
else
{
UITextField *txtFirst=nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
txtFirst = [[UITextField alloc] initWithFrame:CGRectMake(40, 0, 50,
40)] ;
txtFirst.tag=iTag;
txtFirst.delegate=(id)self;
[cell.contentView addSubview:txtFirst];
txtFirst=nil;
}
txtFirst = (UITextField *)[cell.contentView viewWithTag:iTag];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
txtFirst.text =#"text 1";
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(txtFieldCopy)
{
[txtFieldCopy resignFirstResponder];
txtFieldCopy=nil;
}
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
NSLog(#"%d",textField.tag);
NSIndexPath *iRowId =[NSIndexPath indexPathForRow:(textField.tag-1) inSection:0];
[tblSample selectRowAtIndexPath:iRowId animated:NO scrollPosition:UITableViewScrollPositionMiddle];
txtFieldCopy=textField;
}
Make sure if suppose in between you are reloading the table view then txtFieldCopy=nil; in the cellForRowAtIndexPath get called and set to null. so you can ignore this line. But before please check whether you are calling it again and again.
Let me know if it worked for you or not. I am sure it definitely work for you

Couldn't get cell title from another view

My Situation: i have a "homeviewcontroller", and here I'm wanting to acces a cell title of a tableview of another view. So the title of the cell apears in the homeviewcontroller when its appear.
So, i have a tableviewcontroller. Then i have my homeviewcontroller with a label what want to change the text to the cell title text.
PROBLEM: This is my code but it isnt working, the label apears empty.
Home View Controller Code:
Homeviewcontroller.h
...
#property (weak, nonatomic) IBOutlet UILabel *celltitle;
...
Homeviewcontroller.m
...
#import "tableviewcontroller"
#synthesize celltitle;
...
- (void)viewDidAppear:(BOOL)animated
{
Tableviewcontroller *hourscell = [tableviewcontroller alloc];
diahoy.text = hourscell.daylabel.text;
}
and my tableViewController (where i set the cel) method:
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Hours Cell";
CellVC *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CellVC alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Hours *hour = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.daylabel.text = hour.dayselect;
return cell;
}
I want to make clear that my table is working correct, user can add cells without any problem.
Also, it would be great if you know how to get the titlelabel of a certain cell row.
Thanks a lot.
Think you forgot to initialise your tableViewController. You did only allocate it in your HomeViewController.m. And the label should be cellTitle instead of diahoy I guess:
- (void)viewDidAppear:(BOOL)animated
{
Tableviewcontroller *hourscell = [[Tableviewcontroller alloc] init];
celltitle.text = hourscell.daylabel.text;
}

Resources