9

I would like to get the view that is the first responder, currently I have a UITableView that contains UITextFields, using a method:

-(UIView*) findFirstResponder
{

}

I would like to be able to get the view that is the firstResponder and then do something with that view.

Any ideas?

Armand
  • 9,847
  • 9
  • 42
  • 75
  • How many textFields you have in tableView? – Aniruddh Mar 26 '12 at 15:07
  • They are dynamically created but I found a way just now – Armand Mar 26 '12 at 15:09
  • 3
    I recommend to sway away from the for loop approach if at all possible, which could add unnecessary processing. A cleaner approach we be to create a variable, local or instance, to hold a reference to the active object, usually set in a delegate method, such as the `UITextFieldDelegate` method - (void)textFieldDidBeginEditing:(UITextField *)textField. – Gobot Mar 26 '12 at 15:18
  • It seems to me that the easiest way to achieve this is to call `UIView* responderView = [[self window] firstResponder];` and then: `if ([responderView isKindOf:[UITextField class]]) { /*do the magic*/ }` – ULazdins Nov 14 '13 at 09:26

5 Answers5

18

All I had to do was

@implementation UIView (FindViewThatIsFirstResponder)
- (UIView *)findViewThatIsFirstResponder
{
    if (self.isFirstResponder) {        
        return self;     
    }

    for (UIView *subView in self.subviews) {
        UIView *firstResponder = [subView findViewThatIsFirstResponder];
        if (firstResponder != nil) {
            return firstResponder;
        }
    }

    return nil;
}
@end
Armand
  • 9,847
  • 9
  • 42
  • 75
  • Don't you need the textField itself? What is the relation between the subview and the table with the textFields? – o15a3d4l11s2 Mar 26 '12 at 15:12
  • To go into more detail of why this will work for me: I have a UITextField that has a UIPickerView as a inputView, now these are all created dynamically so I don't hold any reference to any fields except my table view, so when I select a textfield the UIPickerView will show up and I then select a value in the picker to set the text of the UITextField, I then had to call a delegate method to pass the selected value back to the current firstresponder. see this question http://stackoverflow.com/questions/9874037/uipickerview-and-uitextfield-get-value-from-uipickerview/ – Armand Mar 26 '12 at 15:17
14

Use UIControl as a root reference to different types of control that can become first responder.

UIControl *currentControl;

As Gobot says - whenever a textfield becomes first responder, keep a note of which one it is...

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {   

    currentControl = textField;

    . . .
Damo
  • 12,840
  • 3
  • 51
  • 62
  • What if the first responder is not a `UITextField`? There are `UITextView`s too, and maybe others...? Don't get me wrong this is a fine solution for the question asked, but I'm looking for something a little more universal. – devios1 Oct 07 '14 at 23:06
  • You could use a reference to UIControl. See above – Damo Oct 08 '14 at 08:03
2

There is no simple way to find firstResponder in iOS. The answers above are only tracking UIViews but all subclasses of UIResponder such as UIViewController can be a first responder.

According to the Quick Help of UIResponder:

Many key objects are also responders, including the UIApplication object, UIViewController objects, and all UIView objects (which includes UIWindow). As events occur, UIKit dispatches them to your app's responder objects for handling.

And the only way to follow UIResponder chain will be using UIResponder's next: UIResponder property.

Returns the next responder in the responder chain, or nil if there is no next responder. The UIResponder class does not store or set the next responder automatically, so this method returns nil by default. Subclasses must override this method and return an appropriate next responder. For example, UIView implements this method and returns the UIViewController object that manages it (if it has one) or its superview (if it doesn’t). UIViewController similarly implements the method and returns its view’s superview. UIWindow returns the application object. UIApplication returns nil.

In the most UIKit object superview, UIViewController, UIWindow, UIApplication or Appdelegate will be the next UIResponder.

extension UIResponder {
    func findFirstResponder() -> UIResponder? {
        var responder: UIResponder? = self
        while responder != nil {
            guard let r = responder, r.isFirstResponder else {
                responder = responder?.next
                continue
            }
            return r
        }
        return nil
    }
}

However the above doesn't track responder's siblings. I guess if you really want to track them all, you need to check the type of responder and track its child(subview, child view controller).

Ilias Karim
  • 4,798
  • 3
  • 38
  • 60
Ryan
  • 4,799
  • 1
  • 29
  • 56
0

I would like to shared with you my implementation for find first responder in anywhere of UIView. I hope it helps and sorry for my english. Thanks

+ (UIView *) findFirstResponder:(UIView *) _view {
   if ([subView isFirstResponder])
      return subView;
   if ([subView isKindOfClass:[UIView class]]) {
      UIView *v = subView;

      if ([v.subviews count] > 0) {
         retorno = [self findFirstResponder:v];
         if ([retorno isFirstResponder]) {
             return retorno;
         }
      }
   }
}
0

We can take advantage of the fact that calling UIApplication.shared.sendAction with a target of nil sends the action to the first responder.

Swift

extension UIResponder {
    
    private static weak var _firstResponder: UIResponder?
    
    static var firstResponder: UIResponder? {
        _firstResponder = nil
        // Tell the first responder to record itself in `_firstResponder`
        UIApplication.shared.sendAction(#selector(_recordFirstResponder), to: nil, from: nil, for: nil)
        return _firstResponder
    }
    
    @objc private func _recordFirstResponder() {
        UIResponder._firstResponder = self
    }
    
}

Then simply accessing UIResponder.firstReponder will give us the first responder.

Objective-C

static __weak id foundFirstResponder;

@implementation UIResponder (FindFirstResponder)

+(id)firstResponder {
    foundFirstResponder = nil;
    [UIApplication.sharedApplication sendAction:@selector(findFirstResponder) to:nil from:nil forEvent:nil];
    return foundFirstResponder;
}

-(void)findFirstResponder {
    foundFirstResponder = self;
}
@end
McKinley
  • 1,123
  • 1
  • 8
  • 18