1

I have a UITableView with a UITextField for each row. When the user touches outside the tableview, deselecting the text field, I would like to invoke a method.

However, I don't want to invoke such method if the user selects another row of the table.

thanks

**Code for alloc_iNit **:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    CMTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[CMTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    [[cell textField] setTag:[indexPath row]];
    [[cell textField] setDelegate:self];

    NSString *tagName = [tagsDisplayName objectAtIndex:[indexPath row]];
    [[cell textField] setText:tagName];

    return cell;
}
aneuryzm
  • 63,052
  • 100
  • 273
  • 488
  • when you deselect the textfield, the keyboard will disappear. then you could trigger it with the UIKeyboardWillHideNotification? – Zoltan Varadi Aug 30 '12 at 11:49
  • This is actually what I want to avoid. I don't want to make it disappear when a UITextfield is deselected, but when the tableview is deselected. In other terms, if the user is selecting the next row, the keyboard shouldn't disappear. But if the user touches somewhere outside the table, then it should disappear indeed. – aneuryzm Aug 30 '12 at 11:52

5 Answers5

2

Something to consider would be using a Tap Gesture recognizer on the view behind the UITableView. You might have to play around with the shouldReceiveTouch event (elaborated on in Gesture recognizer and button actions) to keep the Tap Gesture Recognizer from firing when you click somewhere in the UITableView.

Community
  • 1
  • 1
GeneralMike
  • 2,951
  • 3
  • 28
  • 56
  • I've thought about it, but how do I know which is the current View having the focus ? Is there a way to search it recursively from the parent view ? – aneuryzm Aug 30 '12 at 13:07
  • I believe "having focus" translates to "being the first responder", in which case you can copy the code given in http://lists.apple.com/archives/cocoa-dev/2005/Apr/msg00217.html to check if a certain view has the focus. – GeneralMike Aug 30 '12 at 13:22
  • It has been very easy to implement as you suggest. – aneuryzm Aug 31 '12 at 11:26
0

You have to override hitTest:withEvent: on the actual UITableView itself. Remember that it takes control of the responder chain, so the subviews won’t get a chance to handle it first, unless we explicitly override that behavior

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ 
  return [super hitTest:point withEvent:event];
}

hitTest:withEvent: is responsible for telling the system which view that was hit, by default UITableView assumes itself (or one of its cells), so you have to figure out if the user touches locations, and if user touches outside the tableview so return that view instead.

Modified Code:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ 
    NSLog(@"hitTest");
    UIView *subview = [super hitTest:point withEvent:event];
    if (event.type == UIEventTypeTouches) 
    {        
        // get touches
        NSSet *touches = [event allTouches];

        NSLog(@"subview: %@", subview);
        NSLog(@"touches: %@", touches);
    }
}
alloc_iNit
  • 5,173
  • 2
  • 26
  • 54
  • But hitTest:withEvent is a UIView method. Does this mean that I have necessarily to subclass the UITableView ? I would prefer to not do that. – aneuryzm Aug 30 '12 at 12:03
  • No need to go for subclass. You can code it in your same view and it will respond you. The only thing you have to take care is to identify the view. Check the code that I have edited. – alloc_iNit Aug 30 '12 at 12:23
  • "You can code it in your same view": I don't have such view. My code is all in the view delegate. I still need to subclass a view in the hierarchy to use such method. Correct ? – aneuryzm Aug 30 '12 at 12:27
  • I've added tableView :cellForRowAtIndexPath: so you can see how my table is built. This method and all other code is included in this class which is a subclass of NSObject and delegate of both UITableView and UITextFields – aneuryzm Aug 30 '12 at 12:38
  • No issue. You can put hitTest:withEvent in the same class and manage its response as per your logic. – alloc_iNit Aug 30 '12 at 12:43
0

FIRST SUGGESTION:

I believe the system will help you on this, as it didn't help me (I had to work around it). If you have two textfields, and you have one open, and tap a different one, then the second one gets

textFieldDidShouldBeginEditing:

BEFORE the original one is sent:

textFieldDidEndEditing: // or textFieldShouldEndEditing

Add a couple of log messages to your project to verify this, and if is not so then it may be some other message. It was a year ago I had a problem because of the apparent disordering.

2012-08-30 09:22:40.528 Searcher[22053:f803] SHOULD BEGIN // first tap on first textField
2012-08-30 09:22:40.534 Searcher[22053:f803] BEGIN
2012-08-30 09:22:42.168 Searcher[22053:f803] SHOULD BEGIN // second tap on second TF
2012-08-30 09:22:42.168 Searcher[22053:f803] SHOULD END
2012-08-30 09:22:42.170 Searcher[22053:f803] END

SECOND SUGGESTION:

The user can tap the view anywhere, but if they tap the same textfield (to get copy/paste) you don't want to dismiss.

  • create a new ivar that stores the textField when it gets 'textFieldDidShouldBeginEditing':

    __block UITextField *currTextField;

  • put a transparent view over your view (excluding the keyboard) that detects touch events

  • when the transparent view sees the touch event, if no textField set or the touch is inside the original touchView, do nothing

  • if the touch is anywhere else, and there is a currentTextField, forward the event, then dispatch a block to the mainQueue as follows:

    UITextField *lastTextField = currTextField; dispatch_async(dispatch_get_main_queue(), ^{ if(currTextField && currTextField == lastTextField) { [currTextField resignFirstResponder]; // touch outside the textField, but no new one } } );

David H
  • 40,852
  • 12
  • 92
  • 138
  • Such methods are only invoked when I press the button "Return" or "Done" of my keyboard and not when I touch somewhere else (which is what I need). I was wondering, if I touch outside the tableView, by default, should it be unselected or not ? It currently stays selected. – aneuryzm Aug 30 '12 at 12:31
  • My point was that the difficult part of your task is finding out when another textfield was tapped on. This technique gives you the primitives to detect that case. Detected a touch outside the table should be pretty easy no? – David H Aug 30 '12 at 13:07
0
-(void)textFieldDidBeginEditing:(UITextField *)textField
{   
    if(activeTextField) //declare it in .h file
        [self textFieldDidEndEditing:activeTextField];

    activeTextField = textField;

    CGRect textViewRect = [tableView convertRect:activeTextField.bounds fromView:activeTextField];

    [tableView scrollRectToVisible:textViewRect animated:NO]; //if u want to add scroll to that cell
} 


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeTextField = nil;
}

and then u can use touchesBegan

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [activeTextField resignFirstResponder];
}

hope this one helps. happy coding :)

Anshuk Garg
  • 1,540
  • 12
  • 14
0

I ended up with 2 tap events + added "becomeFirstResponder" on my table so clicking on it won't make it hidden

myTableView.isUserInteractionEnabled = true
myTableView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(searchTableTapEvent(_:))))
myTableView.becomeFirstResponder()

rootView.isUserInteractionEnabled = true
rootView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapOutsideEvent(_:))))
    
}

@objc func searchTableTapEvent(_ sender: UITapGestureRecognizer? = nil) {
    Log.debug("searchTableTapEvent")
}

@objc func tapOutsideEvent(_ sender: UITapGestureRecognizer? = nil) {
    
    Log.debug("rootViewTapEvent")
    myTableView.isHidden = true
}