UITextField in UITableViewCell - reuse issue - objective-c

I have a UITableView which has a custom UITableViewCell that has a UITextField inside it.
Each UITextField displays some text from my viewModel and I am using Reactive-Cocoa to bind the textfields to the viewModel.
When my UITableView loads for the first time, everything works just fine. However when I reload the UiTableView for the next 'page' - the first UiTextField on reloaded (page two) tableView has the exact same memory address as the first UITextField in the first 'page' - cell is not the same as other UI Elements are correct - just the textfields are the same instance.
So, I declared the UITextFields in my VC like so:
#property (weak, nonatomic) UITextField *textFieldOne; //One the first 'page'
#property (weak, nonatomic) UITextField *textFieldTwo; //After reload on second 'page'
Then setup then up like so in a method invoked by cellForRowAtIndexPath
-(void)configureTextFieldCell:(BBTextFieldLabelTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.textField.delegate = self;
if (self.selectedSegmentIndex == SegmentedControlStep1){
if (indexPath.section == 0){
cell.label.text = #"Name";
self.textFieldOne = cell.textField;
}
/* Code for setting up other cells / textfields ommited -
but the same syntax as above with checks for indexPath */
}
if (self.selectedSegmentIndex == SegmentedControlStep2){
cell.label.text = #"Username";
self.textFieldTwo = cell.textField;
[self bindUsernameAndPasswordToViewModel]; /* Binding for this textfield as its nil when VC loads for the first time,
so this is the first chance I get to bind it on second page */
}
}
Inside BBTextFieldLabelTableViewCell the UITextField is declared like so:
#property (strong, nonatomic) IBOutlet UITextField *textField;
I also tried doing this inside the cell's implementation file:
-(void)prepareForReuse
{
self.textField = [[UITextField alloc] init];
}
As I thought my issue is possibly a cell-reuse issue of some sort.
However this code made no difference.
So textFieldOne and textFieldTwo both have the exact same memory address and I cannot figure out why.
Inside cellForRowAtIndexPath I create the cell like so:
BBTextFieldLabelTableViewCell *textFieldCell = [tableView dequeueReusableCellWithIdentifier:textFieldCellidentifier];

In your prepareForReuse you are creating a new text-field but you are neither removing the old one nor adding the new one.
I suggest using prepareForReuse to reset the current text field rather than creating a new one
Reading your question a bit more carefully: The fact that textfield one and two both have the same value, indicates that prepare for reuse is not being called between the two calls to configureTextFieldCell. Without more code, it is difficult to understand why

Related

Passing Data Between View Controllers Not Working

I am attempting to pass the label for a given selected cell to a different view controller...
ListViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedIndexPath];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle: nil];
DetailViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"DetailVC"];
viewController.venueName = cell.textLabel;
[self.navigationController pushViewController:viewController animated:YES];
}
Here is my DetailViewController:
.h
#interface DetailViewController : UIViewController {
IBOutlet UILabel *venueName;
}
#property (nonatomic, strong) IBOutlet UILabel *venueName;
venueName never gets the data. In my .m I also synthesize the venueName.
This happens because your UILabel outlet is not connected at the time you set a string for it. I recommend you to create a string property to store the value and, in viewDidLoad in DetailViewController, you set the string to venueName.
Cheers!!
You need to modify the below like that for setting and getting the text:-
viewController.venueName.text = cell.textLabel.text;
So venueName, which is a pointer to a label, is made to point to an existing label that's part of a table cell. This is obviously wrong -- you don't mean to use the actual label from the table cell, but rather the text stored in it. Both the other answers here are correct: you surely mean to assign the text property of one label to that of the other, and the label that outlet viewController.venueName is connected to hasn't been created at the time this code executes.
The immediate thing you should do to fix your code is to assign the data in question (i.e. cell.textLabel.text) to a string property in the destination view controller. Have the -viewDidLoad or -viewWillAppear method in that controller set the text property of the venueLabel label. Either of those methods will be called after the view hierarchy is instantiated.

UITableViewCell - registerNib subclass not finding nib as subview

In the viewDidLoad in my UITableView I am calling:
[self registerNib:[UINib nibWithNibName:#"MyCell" bundle:nil] forCellReuseIdentifier:#"MyCellRI"];
Then in the cellForRowAtIndexPath I have the following:
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellRI"];
[cell setup:[items objectAtIndex:indexPath.row]];
Inside the setup method I have a look on [self subviews] to go through the subviews and assign content based on key/value but in this loop I find UITableViewCellContentView's but I do not see the my custom view defined in the NIB.
In the NIB I have assigned Files Owner & The custom class of my cell. The cell is showing I just can't find the NIB view in the subviews.
Please advise.
James
Iterating through subviews is hard work) Try to avoid it. it is not very good practice because it is not reliable. For instance if you decided to change your custom cell UI you will have to change method where you're iterating through subviews.
Try to do following:
Let's say that there is custom cell with UIImage and 3 strings (e.g. person's name, age and cell phone number)
Implement 3 functions in your custom cell to set this values as follows:
MyCustomCell.h
... .h file standart code and then at the end ...
#propert(nonatomic, strong) IBOutlet UIImageView *myImage;
#propert(nonatomic, strong) IBOutlet UILabel *myFirstLabel;
#propert(nonatomic, strong) IBOutlet UILabel *mySecondLabel;
#propert(nonatomic, strong) IBOutlet UILabel *myThirdLabel;
- (void)setUIimage:(UIImage *)_image;
- (void)setFirstString:(NSString *)_string;
- (void)setSecondString:(NSString *)_string;
- (void)setThirdString:(NSString *)_string;
MyCustomCell.m:
#synthesize myImageView, myFirstLabel, mySecondLabel, myThirdLabel;
- (void)setUIimage:(UIImage *)_image {
self.myImageView.image = _image;
}
- (void)setFirstString:(NSString *)_string {
self.myFirstLabel.text = _string;
}
- (void)setSecondString:(NSString *)_string {
self.mySecondLabel.text = _string;
}
- (void)setThirdString:(NSString *)_string {
self.myThirdLabel.text = _string;
}
and finaly in your tableView controller cellForRowAtIndexPath
[myCustomCell setUiImage:someImage];
[myCustomCell setFirstString:oneString];
[myCustomCell setSecondString:twoString];
[myCustomCell setThirdString:threeString];
Hope this approach will help you. Feel free to ask if you have some questions

UITextField inside UITableViewCell won't become first responder

I use a UITableViewController to display the details of KoreanFood. The first cell is a custom UITableViewCell (OverviewCell) with an Image and two UITextFields, which I created and layout in Storyboard (AutoLayout).
I subclassed UITableviewCell like this:
// OverviewCell.h
#interface OverviewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UITextField *englishTitleTF;
#property (strong, nonatomic) IBOutlet UITextField *koreanTitleTF;
#property (strong, nonatomic) IBOutlet UIImageView *myImageView;
#property (strong, nonatomic) UIImage *thumbnail;
My textfields in Storyboard are set to enabled/UserInteractionenabled and the delegate is my TVC. When I create the cells I also do this in code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == GENERAL_SECTION) {
static NSString *overviewCellID = #"overviewCell";
OverviewCell *overviewCell = [tableView dequeueReusableCellWithIdentifier:overviewCellID forIndexPath:indexPath];
overviewCell.englishTitleTF.text = self.generalInfo.titleEnglish;
overviewCell.koreanTitleTF.text = self.generalInfo.titleKorean;
overviewCell.englishTitleTF.enabled = YES;
overviewCell.koreanTitleTF.enabled = NO;
//BOOL test = [overviewCell.englishTitleTF becomeFirstResponder];
overviewCell.koreanTitleTF.delegate = self;
overviewCell.englishTitleTF.delegate = self;
UIImageView *imageView = [[UIImageView alloc] initWithImage:self.generalInfo.thumbnail];
overviewCell.myImageView = imageView;
overviewCell.myImageView.frame = CGRectMake(25, 25, 95, 95);
[overviewCell addSubview:overviewCell.myImageView];
return overviewCell;
}
The comment with the BOOL is NO, and I just don't know why... As I set the text and it's displayed correctly, I know the Outlets are set and the Cell isn't nil (I checked that).
Why does this not become first responder?
I also tried some suggestions inside the OverviewCell subclass like the hit test or implementing the setSelected: / setEditing: methods. But a becomeFirstResponder to the textField here doesn't change anything as well.
A view can't become first responder until it's been added to the responder chain, which happens automatically when the view gets added as a subview of a view that's in a window in the application object's windows list. In other words, your cell hasn't been added to the table view yet, so it's not connected to anything, and hence can't become first responder.
Try sending becomeFirstResponder from a method that gets called after the table view has finished loading its cells. And of course, don't do this:
overviewCell.koreanTitleTF.enabled = NO;

UILabel text not changing, however xx.title is working

I have two view controller. In first view controller I have list of names. When I click on that, I want the same name to be displayed in second view controller.
I have below code.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// PropDetailViewController is second view controller
PropDetailViewController *prop = [self.storyboard instantiateViewControllerWithIdentifier:#"prop"];
ListOfProperty *propList = [propListFinal objectAtIndex:indexPath.row];
NSString *myText = [NSString stringWithFormat:#"%#", propList.addressOfFlat];
prop.detailLabel.text = myText;
prop.title = myText;
[self.navigationController pushViewController:prop animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
and in PropDetailViewController, I have #property (retain, nonatomic) IBOutlet UILabel *detailLabel;.
What I was expecting is when I click say Name 1, I will see Name 1 as text in UILabel and on the UINavigationBar too. However I only see Name 1 on navigation bar and not on UILabel.
It is not advisable to access an UIView item at that point in the program flow. When setting the value of prop.detailLabel.text the view may not have been loaded. When the view is loaded later then the UIView is updated with the default settings given in the XIB file in IB.
You should rather set an NSString property, lets say
#property (retain, nonatomic) NSString *propName;
assign it before pushing the view controller as you do. But use this property and not the UILable. And in PropDetailViewController in viewDidLoad do the following:
(void) viewDidLoad {
// call super viewDidLoad and all the works ...
self.detailLabel.text = propName;
}
Instead of viewDidLoad you could use viewWillAppear. Because viewDidLoad COULD be executed already when you assign the property's value.
If you want to be on the save side then invent a new init method where you hand over all the values that you want to be set upfront.
But I never did that in combination with storyboard (where you may use instantiate... rather than init...) and therefore I cannot give any advise out of the top of my head.
Another clean approach would be to stick with the propName property but to implement a custom setter -(void)setPropName:(NSString)propName; where you set the property (probably _propName if you autosynthesize) AND set the UILable text plus setting the UILable text within viewDidLoad.
Try this:
in .h
#property (retain, nonatomic) NSString *detailText;
in PropDetailViewController.m
Change line of code with
NSString *myText = [NSString stringWithFormat:#"%#", propList.addressOfFlat];
prop.detailText = myText;
prop.title = myText;
in ViewDidLoad:
[self.detailLabel setText:self.detailText];

editButtonItem Not Showing Up

The Problem:
The built-in editButtonItem that Xcode automatically comments out when a new UITableViewController class is created does not work when I delete the comment slashes (//). By does not work I mean that the edit button does not appear at all. Swiping a cell does not work either.
Attempted Solutions:
I have tried to follow the various workarounds that have been posted on other stackoverflow threads to no avail. Most of the posts that I have found talk about various aspects of the edit button not working (e.g., no minus signs showing up, etc…) but very few that I have found in which the edit button does not show up at all.
Hunch:
I have a hunch that it might have something to do with the UITableViewController not being properly implemented. I am very new to object-oriented programming as well as objective-c, so I apologize if the answer is something very basic—but hey, it's part of the learning process. Any help is much appreciated.
Code:
____.h
#import <UIKit/UIKit.h>
#import "IndividualRecipeViewController.h"
#class BrowsePrivateRecipeViewController;
#protocol BrowsePrivateRecipeViewControllerDelegate
- (void)browsePrivateRecipeViewControllerDidFinish:(BrowsePrivateRecipeViewController *)controller;
#end
#interface BrowsePrivateRecipeViewController : UITableViewController
#property (weak, nonatomic) id <BrowsePrivateRecipeViewControllerDelegate> delegate;
#property (assign, nonatomic) NSUInteger listLength;
#property (strong, nonatomic) NSDictionary *dictionaryOfRecipes;
#property (strong, nonatomic) NSMutableArray *arrayOfRecipeNames;
// ... methods
#end
____.m
#interface BrowsePrivateRecipeViewController ()
#end
#implementation BrowsePrivateRecipeViewController
#synthesize delegate = _delegate;
#synthesize listLength = _listLength;
#synthesize dictionaryOfRecipes = _dictionaryOfRecipes;
#synthesize arrayOfRecipeNames = _arrayOfRecipeNames;
- (void)viewDidLoad
{
// ... code here
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
// ... other methods
UPDATE:
LINK TO SOURCE CODE
So I have decided to post the source code to my whole project. I am having this problem with multiple files, but if I get it fixed in one, I am pretty sure that the rest will fall into place.
Please focus on the files BrowsePrivateRecipeViewController.m/.h. This is the most straightforward instance of the problem.
Once again thank you for your patience and help.
Sincerely,
Jason
First of all, I would definately not use a custom button for editing the table. It's unnecessary simply because there's already one built in.
Just use UIViewControllers editButtonItem.
If you have to perform additional stuff on button press, override -setEditing:animated: and call super first.
The error you mentioned above is caused because you're trying to access the navigationBars navigationItem, which does not exist. You should access your view controller's navigationItem.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
You need to make a button first. This will make an Edit button then add it to the rightBarButtonItem spot.
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
self.navigationItem.rightBarButtonItem = editButton;
You then need to set up a method to turn on the table's edit mode.
- (void)editTable
{
[self.tableView setEditing:YES animated:YES];
}
Update:
Just read your question again and noticed you want swipe to delete as well. You need to added these methods in order to add that to your tableview.
// 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;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
Update 2
__.h
#property (weak, nonatomic) IBOutlet UINavigationBar *navigationBar;
__.m
#synthesize navigationBar;
- (void)viewDidLoad {
//...
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
self.navigationBar.navigationItem.rightBarButtonItem = editButton;
}
You aren't alloc'ing or init'ing the editButtonItem object, so how can you expect to retain it (equals sign), let alone have it show up? You're basically sending a message to nil.