2

I have a UICollectionViewCell which is prototype cell, I have a textfield in it, which by default present in all of them. (which is what I want). I want to be able to when the user inputs a value in these textfields to be able to retrieve the data and the cell which the data was put into(maybe the number of the row). I have seen a few other related questions but none seems to work for me. Any ideas?

@interface ViewController ()
{
NSArray *collections, *numbers;
NSMutableArray *selectedItemsArray;
UICollectionViewCell *cell;
UITextView *text1Field;
NSIndexPath *indexPath1;
}
    - (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    collections = [NSArray arrayWithObjects:@"starbucks_coffee.jpg", @"thai_shrimp_cake.jpg", @"vegetable_curry.jpg", @"white_chocolate_donut.jpg", nil];
    selectedItemsArray = [NSMutableArray array];
    numbers = [NSArray arrayWithObjects:@"1", @"2", @"3", @"4", nil];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return collections.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"Cell";

    cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    UIImageView *imageview = (UIImageView *)[cell viewWithTag:100];
    text1Field = (UITextView *)[cell viewWithTag:50];

    imageview.image = [UIImage imageNamed:[collections objectAtIndex:indexPath.row]];

    collectionView.allowsMultipleSelection = YES;

    collectionView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bg2.png"]];



    return cell;

}

2 Answers2

4

Implement the delegate method (void)textViewDidChange:(UITextView *)textView . You could use the tag property of the textView to distinguish which one was changed.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"MySpecialCell";
    MySpecialCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[MySpecialCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.mySpecialTextView.delegate = self;
    }
    cell.mySpecialTextView.tag = indexPath.row;
    return cell;
}

- (void)textViewDidChange:(UITextView *)textView
    int rowOfTextViewThatJustChanged = textView.tag;
}

Update!
Here's an approach that uses associated objects instead of tags.

static NSString *const kIndexPathKey = @"indexPathKey";

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"MultiSelectCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        UISwitch *switchView = [[UISwitch alloc] init];
        [switchView addTarget:self action:@selector(switchValueChanged:) forControlEvents:UIControlEventValueChanged];
        cell.accessoryView = switchView;
    }

    UISwitch *switchView = (id)cell.accessoryView;
    [self setIndexPath:indexPath onSwitch:switchView];
    switchView.on = [self isIndexPathSelected:indexPath];

    id item = [self itemAtIndexPath:indexPath];
    cell.textLabel.text = safePerformSelector(item, self.itemDescriptionSelector);
    return cell;
}

- (void)switchValueChanged:(UISwitch *)sender {
    NSIndexPath *indexPath = [self indexPathOfSwitch:sender];
    [self setRowSelected:sender.isOn atIndexPath:indexPath];
    [self.delegate didSelectItem:[self itemAtIndexPath:indexPath] atIndexPath:indexPath selected:sender.isOn sender:self];
}

- (void)setIndexPath:(NSIndexPath *)indexPath onSwitch:(UISwitch *)switchView {
    [switchView setAssociatedObject:indexPath forKey:kIndexPathKey];
}

- (NSIndexPath *)indexPathOfSwitch:(UISwitch *)switchView {
    return [switchView associatedObjectForKey:kIndexPathKey];
}


@implementation NSObject (AssociatedObjects)

- (void)setAssociatedObject:(id)object forKey:(NSString *const)key {
    objc_setAssociatedObject(self, (__bridge const void *)(key), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)associatedObjectForKey:(NSString *const)key {
    return objc_getAssociatedObject(self, (__bridge const void *)(key));
}

@end
CrimsonChris
  • 4,651
  • 2
  • 19
  • 30
  • I used to use tags for these kind of things but recently I changed to indexPath. I check the indexPath of the control tapped, get a cell and then the textField. It's basically the same but more generic. – Marcal Apr 09 '14 at 17:32
  • @Marcal Please elaborate. Where do you get the indexPath from? UITextViews do not have an indexPath property. – CrimsonChris Apr 09 '14 at 18:01
  • Here, read this question/answer: http://stackoverflow.com/questions/9274494/how-to-know-the-uitableview-row-number/9274863#9274863 – Marcal Apr 09 '14 at 18:05
  • I asked about UIControls inside custom cells, the answers is worth the reading and has helped me a lot: http://stackoverflow.com/questions/10258082/ios-prototype-cells-with-uicontrols-inside – Marcal Apr 09 '14 at 18:07
  • Sneaky! Those approaches looks awfully fragile... I've found it bad practice to use the superview. The solution I've been using for problems like this is to attach an NSIndexPath to the control as an associated object. However, I think that approach is a bit more advanced than what the OP was asking for. – CrimsonChris Apr 09 '14 at 18:10
  • It takes a bit of solide setup with cell subclasses, delegation, etc, but when everything is set correct, I found it to be quite a solid implementation. I've even created a UITableView subclass with the implementation of all the custom cells I use and it's quite handy. To the question at hand, however using tags is more than suficient. – Marcal Apr 09 '14 at 18:31
  • It does seem much easier indeed. I'll bookmark this question/answer and keep it for future reference. Thanks! – Marcal Apr 09 '14 at 18:41
  • @CrimsonChris Thanks for your work, but I don't really understand how to implement a textfield. Could you show me how to use a tag property of the textView? – user2845489 Apr 09 '14 at 23:11
  • @user2845489 `tag` is a property on all UIViews. Set the tag of your UITextView in your `cellForRowAtIndexPath` and set its delegate to something (probably your UICollectionViewController). Then in the `(void)textViewDidChange:(UITextView *)textView` delegate method, use the tag property of the textView parameter to figure out which UITextView was just edited. – CrimsonChris Apr 09 '14 at 23:30
  • @CrimsonChris could you please put in some code all I got so far is `text1Field = (UITextField *)[cell viewWithTag:50];` text1Field is a public variable, very confused at the moment – user2845489 Apr 09 '14 at 23:43
  • @user2845489 Are you using a custom cell that has a UITextField in it? – CrimsonChris Apr 09 '14 at 23:46
  • @user2845489 You should change it to a UITextView. It has better delegate methods for detecting text changes. – CrimsonChris Apr 09 '14 at 23:47
  • @CrimsonChris, thanks I a little easier for me to userstand however how I am meant to access `cell.mySpecialTextView.tag` without a property of the textView – user2845489 Apr 10 '14 at 00:08
  • @user2845489 You said you were using a custom cell. I assumed that you could expose the text view as a property on your custom cell. – CrimsonChris Apr 10 '14 at 00:19
  • @CrimsonChris, Im using a cell which is a prototype – user2845489 Apr 10 '14 at 00:25
  • @user2845489 Had to look up what you meant by prototype... I've never used storyboards so I don't really know how to expose fields. – CrimsonChris Apr 10 '14 at 00:41
  • @CrimsonChris Ah, doesn't matter Thanks for your help anyway :) – user2845489 Apr 10 '14 at 00:43
  • @user2845489 This seems to explain how to add properties to prototypes. http://stackoverflow.com/questions/10258082/ios-prototype-cells-with-uicontrols-inside – CrimsonChris Apr 10 '14 at 04:51
0

Ok, I have figured out a way for anyone with the same issue. What needs to be done is

  1. Have your collectionView in a dedicated UICollectionViewController.
  2. Add the delegate method <UITextFieldDelegate>
  3. In your textFieldShouldEndEditing:textField method add the following line

    NSIndexPath *indexPath = [self.collectionView indexPathForCell:(UICollectionViewCell*)[[textField superview] superview]];

What is happening here is basically you declaring a NSIndexPath reference which stores the indexPath of the textField. The indexPath will give out a row number by indexpath.row.

Then you can use that data however you want :)

slava
  • 1,901
  • 6
  • 28
  • 32
  • While technically valid I would advise against using this approach. Calling superview.superview on anything is dangerous. What happens if Apple decides to add an extra view in between your UITextView and the cell? – CrimsonChris Apr 10 '14 at 04:34
  • @CrimsonChris, just a temporary fix for the time being because I needed a solution. If I find a better more stable solution I will post it. – user2845489 Apr 10 '14 at 05:19