I have a UITableView of 'people' for my iOS app. I have created an 'add' screen to add new people to my persistent store, which works perfectly.
This add view uses a form created with a UITableView and subclassed UITableViewCells, with a UITextField and UILabel, which although works well, I am very new to iOS programming and feel that this may not be the most efficient way.
I am trying to re-use this add view to be my detail, add, and edit view, and I can successfully set a 'Person' entity as a property in the detail view. I have the following code in my viewDidLoad method, where the adding property is set in the prepareForSegue method in the previous ViewController :
if (self.adding)
{
self.editing = YES;
self.title = #"Add Person";
}
else
{
self.title = self.person.name;
[self setDetail];
}
My problem is that when I try to pre-populate my detail view's fields (my setDetail method), I am unable to set my UITextField text with the name property from my person entity. Here's the code I'm using to retrieve the UITextField and set it's text property with:
form is the UITableView;
UITableViewCell *nameCell = [form cellForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
UITextField *nameField = (UITextField *)[nameCell viewWithTag:100];
nameField.text = self.person.name;
If I NSLog nameCell it returns (null)
I hope that's enough explanation. Any pointers would help a lot!
Instead of setting in viewDidLoad:, do it in your table view data source methods. i.e
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Deque the nameCell and prepare the cell if its not available.
UITextField *nameField = (UITextField *)[nameCell viewWithTag:100];
nameField.text = self.person.name;
return nameCell.
}
I don't really understand your question, but cellForRowAtIndexPath may return nil because
Return Value
An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
Official Document: cellForRowAtIndexPath:
Related
So I have looked around a quite a bit, and nothing on here seems to explain exactly the correct way of doing this. I have 7 UITextFields within a custom UITableViewCell.
My question is this: What is the correct way of managing the delegate of these UITextFields?
Since the custom cells are technically part of the "model" portion of the project, I would rather have the controller that controls the UITableView also control the text fields in the table's cells, but I can not figure out how to set the delegate for the text fields (which are created in the subclass of UITableViewCell) to this view controller.
Would it be bad practice to just make the subclass of UITableViewCell conform to UITextField delegate and manage all of that stuff in there? If so, how else should I go about doing this?
Thanks, any help would be appreciated.
You shouldn't have a problem setting the delegate of the cell's text field to be your view controller.
This is what you need to do:
1) The view controller needs to implement the UITextFieldDelegate protocol
2) Declare a property for the text field in your custom cell
#property (nonatomic, retain) IBOutlet UITextField *textField;
3) Then set the view controller as the text field's delegate in the method cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
// use this if you created your cell with IB
cell = [[[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil] objectAtIndex:0];
// otherwise use this
cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// now set the view controller as the text field delegate
cell.textField.delegate = self;
}
// configure cell...
return cell;
}
In my opinion the cell should manage the keyboard since it is the one that is holding the UITextField. You can set your cell as the UITextField delegate. In my own application, I have done this and then made my cell have it's own delegate. Any of the methods of UITextField or any new methods that should be handled by the controller can be passed along to the controller through the cells delegate.
In this way the cell can still be generic without knowing anything about what the application is actually doing.
My suggestion would be to "tag" (i.e. set the tag) of each textField with a value that encodes the section, row, and one-of-7 text views in the table, then make the UIViewController the delegate.
So you need to bound the size of these - say you will never have more than 100 rows. So you encode this as:
.tag = 1000*section + 100*row +
When you get a message you can have a method/function take the tag and decode it into section, row, tag, and do what you need to have done.
To declare your TableViewController as the delegate include <UITextFieldDelegate> at the end of your #interface in the TableViewController's .h file.
#interface MyTableViewController : UITableViewController <UITextFieldDelegate>
Then, connect the text fields by ctrl-dragging each field under the #interface. Each UITextField is connected to its respective property by an IBOutlet. Finally, in the .m file include the following function to show the delegate which field you want to return....
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[aTextField resignFirstResponder];
return YES;
}
Swift version (Based on Eyal's answer)
class MyViewController: UIViewController, ... , UITextFieldDelegate {
#IBOutlet var activeTextField: UITextField! //doesn't need to connect to the outlet of textfield in storyboard
....
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
....
var cellTextField = self.view.viewWithTag(101) as? UITextField
cellTextField!.delegate = self;
....
}
I unfortunately still have not seen the light when it comes to organising my iphone app nicely into controllers and views. Let me illustrate with an example:
I am working on a sign up page which consists of a table view with a list of custom table cells. Some of these cells have a text field inside them and when the user touches one of those a keyboard slides up from the bottom. The keyboard has a return key in its lower right corner and when the user hits this key I would like the keyboard to slide down again.
Now, where do I put the
- (BOOL) textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
? Currently I have made my custom table cell conform to the text field delegate protocol and have put the method in there, but it does seem a bit wrong to have stuff like that inside a view class? On the other hand I do not find it appropriate in the table view controller either.
you can set your table view controller as the text field's delegate...
just remove the code in the custom cell where you set it as the delegate and instead set the delegate in the table view controller's cellForRowAtIndexPath method where you actually create and return the cell..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
MyCustomCell *myCell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (myCell == nil)
{
myCell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellSelectionStyleNone reuseIdentifier:CellIdentifier] autorelease];
myCell.myTextField.delegate = self;
}
//other cell specific code goes here
return myCell;
}
I have scoured the internet looking for a good tutorial or posting about having a UITableView populated with a UITextField in each cell for data entry.
I want to keep track of each UITextField and the text written within it while scrolling. The tableView will be sectioned. I have been using a custom UITableViewCell but I'm open to any method.
Also, is it possible to use the textFields as ivars?
If any of you could point me in the right direction, it would be greatly appreciated.
Thank you in advance!
To solve your problem you have to maintain an array, with some number (number of textFields you added to all cells) of objects.
While creating that array you need add empty NSString objects to that array. And each time while loading the cell you have to replace the respected object to respected textField.
Check the following code.
- (void)viewDidLoad{
textFieldValuesArray = [[NSMutableArray alloc]init];
for(int i=0; i<numberofRows*numberofSections; i++){
[textFieldValuesArray addObject:#""];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return numberofSections;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return numberofRows;
}
- (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:identifier];
CustomTextField *tf = [[CustomTextField alloc] initWithFrame:CGRectMake(5,5,290,34)];
tf.tag = 1;
[cell.contentView addSubView:tf];
[tf release];
}
CustomTextField *tf = (CustomTextField*)[cell viewWithTag:1];
tf.index = numberofSections*indexPath.section+indexPath.row;
tf.text = [textFieldValuesArray objectAtIndex:tf.index];
return cell;
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
int index = textField.index;
[textFieldValuesArray replaceObjectAtIndex:index withObject:textField.text];
}
First of all, you must understand that UITableViewCell and UITextField are just views, they are not supposed to hold data, they are just supposed to display them and allow the user to interact with them: The data should remain stored in the controller of the table view.
You have to remember that UITableView allows you to reuse UITableViewCell instances for performance purpose: what's displayed on the screen are actually the only subviews UITableView keep there. It means that you'll reuse one cell that already has a text field in it and set the text on that field directly. When the user will tap on the field it will edit it and you'll have to get the value back from it when the user will have finished.
The fastest way, would be to use what Satya proposes, that is building normal UITableViewCell and insert into a UITextField (there's no need for a CustomTextField class...). The tag will allow you to get back to the text field easily... But you'll have to setup your text field so it behaves properly when the table view resizes or if a label in the same cell changes.
The cleanest way to do that is to subclass UITableViewCell and setup the layout of your label and text field, and you can provide the text field as a property of the custom subclass.
I have used Textfields in tableview for data entry.
I have customised the UITextField class in a separate class called Utility :
In Utility.h
#interface CustomUITextField:UITextField{
NSInteger rowNumber;
}
In Utility.m
#implementation CustomUITextField
#synthesize rowNumber;
#end
My tableView cellForRowAtIndexPath method is
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Identifier = #"Cell";
UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:Identifier];
if(cell == nil)
cell = [self reuseTableViewCellWithIdentifier:Identifier withIndexPath:indexPath];
CustomUITextField *itemNameTextField = (CustomUITextField *)[cell.contentView viewWithTag:TEXTFIELD_TAG];//this is the tag I have set in reuseTableViewCellWithIdentifier method for textfield
itemNameTextField.rowNumber = indexPath.row;
itemNameTextField.text = #"";//you can set it for the value you want
if(itemListTable.editing)
itemNameTextField.borderStyle = UITextBorderStyleRoundedRect;
else
itemNameTextField.borderStyle = UITextBorderStyleNone;
return cell;
}
You can customise the delegate methods of UITextField for CustomUITextField & can save the text entered in a particular row's textfield by accessing the CustomTextField's row number.
Just try with this.
I had the same problem here is some code i found that treats this problem . it puts the data enterd in a Array Look at the Debugger console to see the results of the text being typed here's the link TextFieldCell. . Happy Codeing
I'm trying to build a UI so the user can edit the attributes of a core data entity. When the user taps the edit button, selecting a row will push the listDetailViewController, which is just a table view that displays the attributes. It uses a custom table view cell with a label and a UITextField. The listDetailViewController displays the attributes properly, and will accept text as its supposed to, but I can't figure out how to get the user-inputted text to save.
If I'm not explaining clearly, here's an example. I want to change the list's name, so I tap Edit, tap the list, tap the List Name row, the keyboard pops up, I type in the new name, tap Done and it pops me back to the RVC with none of the changes saved. I've been banging my head on this for a few days and would love some help!
Here's the relevant code from ListDetailViewController:
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector(done)];
self.navigationItem.rightBarButtonItem = doneButton;
[doneButton release];
self.tableView.allowsSelection = NO;
self.tableView.allowsSelectionDuringEditing = NO;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 3;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *DetailCellIdentifier = #"DetailCell";
ListDetailCell *cell = (ListDetailCell *)[tableView dequeueReusableCellWithIdentifier:DetailCellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"ListDetailCell" owner:self options:nil];
cell = listDetailCell;
self.listDetailCell = nil;
// Configure the cell...
// list name
if (0 == indexPath.row) {
cell.label.text = #"List Name";
cell.textField.text = self.selectedList.listName;
cell.textField.placeholder = #"Name";
}
// Detail 1
if (1 == indexPath.row) {
cell.label.text = #"Detail 1";
cell.textField.text = selectedList.detail1;
cell.textField.placeholder = #"Detail 1";
}
// Detail 2
if (2 == indexPath.row) {
cell.label.text = #"Detail 2";
cell.textField.text = selectedList.detail2;
cell.textField.placeholder = #"Detail 2";
}
}
return cell;
}
- (void)done {
[self.listDetailCell resignFirstResponder];
[self.navigationController popViewControllerAnimated:YES];
}
The ivars label and textField are declared in ListDetailCell, which is the table cell nib I mentioned earlier.
Not sure if I've got your problem correct or if my answer is the best method, but it's what I did recently. I have a TableViewController and UITableView which display a series of custom cells for editing data. This is basically replicating what I've seen a number of other applications do to create data editing screens.
Each of the custom cells has a UITextField. When the user finishes editing a cell, the UITextField triggers a message to a UITextFieldDelegate. So I added the UITextFieldDelegate protocol to the TableViewController and when setting up the custom cell in cellForRowAtIndexPath, I set the TableViewController as the UITextFields delegate. Then when the user finishes editing the end editing message is sent and I can then get the value from the UITextField and store it back in the managed entity object.
I apologise, but I don't have access to my code right now or I'd cut and paste an example for you.
Anyway, some things to watch out for:
In the code for the delegate message you need to first identify the UITextField that has triggered the call. The best way to do this is to set the Tag property on the UITextField when you create the UITableCell that contains it. Then in the delegate method you can use a switch statement to select which entity field to store the value in.
Getting ride of the keyboard when a user taps on a non-editable area of a UITableView can be tricky. You need to store a list of objects that can have a keyboard, and when a click happens, loop through them and do the resign first responder to remove the keyboard from the display.
tapping a save button on the navigation bar or something else outside of the UITableView will not remove the keyboard or resign the first responder, so the delegate of the field currently being edited does not get called. You need to add code to trigger the save sequence.
If you have any UITextView's they use a different delegate.
To add the delegate you will need to do something like this (taken from your code above):
#interface MyTableViewController : UITableViewController <UITextFieldDelegate>
....
if (0 == indexPath.row) {
cell.label.text = #"List Name";
cell.textField.text = self.selectedList.listName;
cell.textField.placeholder = #"Name";
cell.textField.delegate = self; // Setting controller as text field delegate.
cell.textField.tag = 1; // Really should use an enum here for clarity.
}
....
-(void) textFieldDidEndEditing:(UITextField *) textField {
switch(textField.tag) {
case 1: //Again with the enum.
// Save field 1.
entity.someProperty = textField.text;
....
}
}
This is for dealing with a number of text fields. Another solution I found was to store the changed values in a dictionary and only update the entity when the user taps save. With my solution above, you would also have to reset the entities properties if the user cancels. So it's horse for courses.
I have a UITableView with 15 cells, each with a separate text box in it.
I have implemented UITextViewDelegate and I am able to received changed textview data using textViewDidChange (etc). But I have one big problem still, how do I know WHICH textview sent this, (i.e. in which cell was the textview altered?)
Its interesting to have so much working, yet not know precisely where it comes from.
A whole bunch of code is available if required.
Regards #norskben
Code
// Customize the appearance of table view cells.
- (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] autorelease];
//Big Text Box
UITextView *detailLabel = [[UITextView alloc] initWithFrame:CGRectMake(30, 80, CONST_Cell_width, 150)];
detailLabel.tag = 20;
[cell.contentView addSubview:detailLabel];
}
UITextView * detailLabel = (UITextView *) [cell.contentView viewWithTag:20];
You can assign tags (integers) to the different views and query the tag number to see which view called the method. Look for the tag property on the view:
tag
The receiver’s tag, an integer that you can use to identify view objects in your application.
#property(nonatomic) NSInteger tag
see here
Not at my development machine, but when you create the UITextView you should be able to assign it a tag. I think it is [myTextView setTag:x]; where x is an integer.
Then, in the TextViewDidChange use
if (textview.tag == x) {
//do something
} else if (textview.tag == y) {
//do something else and so on
}
Hope that helps a little.
The text views pass a reference to themselves in every delegate method so you know which one sent it. To make a connection to the cell, I'd set each text view's tag property to a different value that corresponds to the row of the cell they're in.
Here's an important question: Are your text boxes static, or can they change over time? If they won't change (the user can't alter the number of cells or add more later), then you can declare a new textField for each cell. I have something similar in my apps. I have two text boxes, and depending on which textField is currently active, the delegate does something different.
Declare separate text fields in your header
UITextField *textField1;
UITextField *textField2;
UITextField *textField3;
in the delegate method, use if statement blocks to find out which textField is changing:
if (textField == textField1) {
//do something
} else if (textField == myTextField2) {
//something else
}
Note that this really only works if your view is static.
Hope this helps
Have a great day
When you're searching the UITableView's cells for the event source UITextView, only iterate over the cells that the user can currently see. This can be obtained using the following UITableView method:
- (NSArray *)visibleCells