I have a UICollectionView
which loads my custom UICollectionViewCell
's that have a UITextField
in them and a UILabel
.
In my cellForItemAtIndexPath
I am doing something like this:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
BBCollectionViewTextFieldCell *textFieldCell = [collectionView dequeueReusableCellWithReuseIdentifier:[BBCollectionViewTextFieldCell reuseIdentifier] forIndexPath:indexPath];
NSString *label = @"";
switch (indexPath.item){
case 0:
label = @"Label One";
self.firstTextField = textFieldCell.textField;
textFieldCell.textField.text = self.viewModel.labelOneData;
break;
case 1:
label = @"Label Two";
self.secondTextField = textFieldCell.textField;
textFieldCell.textField.text = self.viewModel.labelTwoData;
break;
case 2:
label = @"Label Three";
self.thirdTextField = textFieldCell.textField;
textFieldCell.textField.text = self.viewModel.labelThreeData;
break;
}
textFieldCell.label.text = label;
textFieldCell.textField.delegate = self;
return textFieldCell;
}
I then use the normal UITextFieldDelegate
methods to handle text input doing something like this:
-(void)textFieldDidEndEditing:(UITextField *)textField{
if (textField == self.firstTextField){
//Do something with it
}
//And so on for the rest...
}
So far so good and all works...
So what's the issue?
The issue is if I reload the UICollectionView
the following happens:
self.firstTextField
will have data in it that belongs to self.thirdTextField
Or any random combination thereof. The UILabel
's are all correct - however it seems the actual UItextField
s have gotten mixed up. the first UICollectionViewCell
's UitextField
will actually have data from another cell's textField.
At first I figured it was a reuse issue - however, since my cells never scroll off screen and the data is pretty static, (There were always X amount of cells, no less or more ) - so it can't be a reuse issue.
I reworked the code so that I have no UitextField
Properties
in my UIViewController
where this code sits - and rely on indexPath
to get the textField. Like this in cellForItemAtIndexPath
switch (indexPath.item){
case 0:
label = @"Label One";
textFieldCell.textField.text = self.viewModel.labeloneData;
break;
and:
-(void)textFieldDidEndEditing:(UITextField *)textField{
NSIndexPath *indexPath = [self.collectionView indexPathForCellContaininView:textField];
if(indexPath.item == 0){
//Do something with it
}
//And so on for the rest...
}
This solves the problem however it is not what I want to do. I need the UItextField
properties in my UIViewController
I declared the UitextField
Properties
in the implementation like so:
@property (strong, nonatomic) UITextField *firsttextField;
I also posted a very similar issue but using UITableView
instead and it was concluded its related to cell Reuse - however I don't believe it is anymore (Again, setup in that question's code is almost identical to this question - same problem though)
UITextField in UITableViewCell - reuse issue
Where the issue is I think
I don't think it is to do with the the way UITableView
or UiCollectionView
is reusing the cells. I think the issue is somewhere in the viewController's
code and the instances of my UItextField
properties.. I may be off base here though.
I know I listed a possible workaround - however I would like to get to the bottom of this issue and find how why it's happening.
Keeping pointers to subviews of UITableView/UICollectionView cells is fraught because the cells are reused when scrolling -- and potentially scrambled on reload. Upon reuse, the subview which you thought was at one index path is now at another.
The most common solution for this is outlets in a custom cell. Others keep a tag up-to-date every time a cell is configured. Others favor a quick search up the view hierarchy (say, upon a button tap) to work out which index path a subview corresponds to.
But since ReactiveCocoa is all about model-view connection it wants a pair of pointers for the duo. Fortunately it also provides and end condition that corresponds to cell reuse. This gist demonstrates how to build reactive connection for table view cell's subview. Copied here ...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:REUSABLE_CELL_ID];
UILabel *label = (UILabel *)[cell viewWithTag:VIEW_TAG];
Model *someModel = [self getModelFromIndexPath:indexPath];
// `takeUntil:` makes the RACObserve() signal complete (and thus breaks the subscription)
// when the cell is recycled.
RAC(label, text) = [RACObserve(someModel, someKey)
takeUntil:cell.rac_prepareForReuseSignal];
return cell;
}