Custom UITableViewCells UITextField strong or weak reference? - objective-c

I'm pragmatically setting up a tableview with uitextfields, uisegmentedcontrols, etc.
Here's an example
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//NSLog(#"creating a new %#", CellIdentifier);
if([CellIdentifier isEqualToString:#"ID"]) {
UITextField *newTextField = [[UITextField alloc] initWithFrame:CGRectMake(80, 5, 215, 34)];
self.idField = newTextField;
[cell addSubview:self.idField];
}
}
I am creating properties for all of these text fields and assigning them to the newly created fields as you can see.
My question is should I be using (nonatomic, strong) or (nonatomic, weak) ?
#property(nonatomic, weak) UITextField *idField;
//Or
#property(nonatomic, strong) UITextField *idField;

You should not create member variables for those textFields at all.. For what do you need a second textField? Perhaps another Cell Style is enough for your purpose? E.g. UITableViewCellStyleValue1, UITableViewCellStyleValue2 or UITableViewCellStyleSubtitle?
But if you need a custom one, just assign it a tag (e.g. the row of the tableViewCell) and retrieve it afterwards via viewWithTag:. If you want direct access think of a custom UITableViewCell subclass.
To give a direct answer to your question: Probably a weak reference would be enough here, since the textField is added on a cell, which will not get deallocated at any time (since it will be reused).

Related

UITextField in UITableViewCell - reuse issue

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

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;

custom uitableviewcell will not display label texts

And the value of the labels are null as well.
I'm not really sure what's going on.
These are my classes/codes
#interface CustomEventCell : UITableViewCell{
}
#property (nonatomic, weak) IBOutlet UILabel *participant1label;
#property (nonatomic, weak) IBOutlet UILabel *participant2label;
#property (nonatomic, weak) IBOutlet UILabel *status;
#end
Event model is
#interface Event : NSObject{
}
#property NSNumber *matchId;
#property NSString *participant1, *participant2,
-(id)init;
#end
this is the tableview that fills up the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"EventCell";
CustomEventCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[CustomEventCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// events is a NSMutableArray with Event instances
Event *event = [events objectAtIndex:indexPath.row];
cell.participant1label.text = event.participant1;
return cell;
}
Here's my setup
I must be missing something as I have another uitableview and it populatees the custom without problems. I compared them and they're identical. I even tried going back to the regular label and it would fill that up but not this custom one.
EDIT:
Modified the wrong copied code.
I wrote a simple little test app attempting to replicate what you want to see working. I have made it available here using Storyboards. If you cannot figure it out from this, then perhaps you can take the test app and modify it so that it replicates the bad behavior you are seeing.
My best guess as to what might be going on is that when you initialize your cell, it is not connected to a view in a xib.
Try setting a breakpoint at:
cell.participant1label.text = event.participant1;
and verify that cell.participant1label is not nil by doing:
NSLog( #"cell.participant1label: %#", cell.participant1label );
I had a small bug in which none of my custom labels were showing up and cell.participant1label was nil. The cause was that I had not set the Identifier for the custom table view cell to 'EventCell'. So, I might suggest rechecking that and making sure the identifier really does match between your code and the XIB.

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];

Storyboard - Multiple UITableViewControllers with the same custom cell

I occasionally find myself copy/pasting custom cells between UITableViews in my storyboard which is slowly becoming a maintenance nightmare (i.e., a simple change must now be made to each custom cell across several UITableViews).
Is it possible to create one custom cell in a storyboard or xib file and then reference it from multiple UITableViews? I'm thinking there's got to be a way to specify a NIB name for a static/prototype cell similar to how you can specify a NIB name for a UIViewController when editing a normal xib.
BTW... I know how to do this via code. What I'm looking for is a way to do this from within the storyboard editor itself.
I know of a way, but it does require a little code in a custom class. Make a subclass of UITableViewCell that loads itself from a nib. Then just set the class of your cells to this subclass. For a good method of having a view replace itself with a different version loaded from a nib see this blog post.
Actually, you don't have to create a custom class. If you're coding for version 4.0 and up, you can use a UINib to create it. You can also cache it to improve performance.
UIViewController.h
#property (nonatomic, retain) UINib *cellNib;
#property (nonatomic, retain) IBOutlet MyCustomTableViewCell *customCell;
UIViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Cache the tableviewcell nib
self.cellNib = [UINib nibWithNibName:#"MyCustomTableViewCell" bundle:nil];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCustomTableViewCell";
MyCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[self.cellNib instantiateWithOwner:self options:nil];
cell = customCell;
self.customCell = nil;
}
// Set title and such
}
Don't forget about releasing it in dealloc if not using ARC