45

In Mac OS X, you can find the first responder like this:

[[self window] firstResponder]

Is there any way of doing it in iOS? Or do you need to enumerate the child controls and send an isFirstRespondermessage to each one?

Honey
  • 24,125
  • 14
  • 123
  • 212
Tommy Herbert
  • 18,568
  • 14
  • 43
  • 56

4 Answers4

245

I really like VJK's solution, but as MattDiPasquale suggests it seems more complex than necessary. So I wrote this simpler version:

Objective-C

#import "UIResponder+FirstResponder.h"

static __weak id currentFirstResponder;

@implementation UIResponder (FirstResponder)

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

-(void)findFirstResponder:(id)sender {
   currentFirstResponder = self;
}

@end

Swift 4

import UIKit

extension UIResponder {

    private static weak var _currentFirstResponder: UIResponder?

    static var currentFirstResponder: UIResponder? {
        _currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(UIResponder.findFirstResponder(_:)), to: nil, from: nil, for: nil)

        return _currentFirstResponder
    }

    @objc func findFirstResponder(_ sender: Any) {
        UIResponder._currentFirstResponder = self
    }
}

I also made it a class method since that seemed to make more sense. You can now find the first responder like so: [UIResponder currentFirstResponder]


Nicolas Miari
  • 15,044
  • 6
  • 72
  • 173
Jakob Egger
  • 11,393
  • 4
  • 35
  • 47
  • 17
    This is brilliantly simple, well done. – Steven Fisher Feb 22 '13 at 07:00
  • 1
    Wouldn't using `UIResponder *` make more sense than `id`? – Steven Fisher Feb 22 '13 at 17:46
  • 1
    @StevenFisher I'm pretty sure that the first responder is always a view, so maybe `UIView*` would make even more sense. (eg. a view controller would be in the responder chain, but it can't be the first responder -- the first responder would be the view it controls) But to be honest, I didn't think too deeply about the return type. Using `id` avoids having to use explicit casts. – Jakob Egger Feb 25 '13 at 13:08
  • 2
    instancetype would fit well :) – Arcank Nov 29 '13 at 16:18
  • Ok, gonna use this in all of my projects from now on, neat snippet! – Rick van der Linde Jan 23 '14 at 20:24
  • @Arcank I think that `instancetype` is not a good idea here. Imagine calling `[UITextView currentFirstResponder]`, there is no guarantee that the first responder is actually a `UITextView`. `UIResponder *` as return value should be fine. – Tammo Freese Feb 03 '14 at 15:12
  • 4
    fwiw: View controllers **can** become first responders. All you need to do is to return `YES` from `-canBecomeFirstResponder`. This is great for input accessory views that are always on screen or handling shortcuts even when no view is focussed, etc. As such, using `UIView *` would be wrong here. – Max Seelemann May 27 '14 at 12:54
  • Great solution! I came up with [something similar](http://stackoverflow.com/a/24639472/35690), but instead of using a static variable, I subclassed UIEvent. This makes it safer (e.g. if you have multiple threads, or if you have nested function calls, it'll still work correctly). – Senseful Jul 09 '14 at 00:14
  • 2
    @Senseful Making `+currentFirstResponder` reentrant is pointless if you call it only from the main thread. And you really shouldn't call it from a background thread. UIKit is not thread safe. – Jakob Egger Jul 15 '14 at 19:33
  • wish I could upvote this answer several times. – Can Poyrazoğlu Oct 31 '14 at 14:00
  • 3
    Here is a Swift implementation: http://stackoverflow.com/a/27140764/385979 – Code Commander Nov 26 '14 at 03:22
  • 1
    Doesn't work in `UIKeyboardWillShowNotification`, since its not firstResponder yet when the notification handler called... – Gary Lyn Apr 20 '15 at 05:07
  • 3
    Doesn't work at least on iOS9. I put focus into `TextField` which is in the `scrollview`. After I check `firstResponder` in the way described and get ScrollView as the first responder. At the same time `[[[[UIApplication sharedApplication] windows] firstObject] valueForKey:@"firstResponder"]` gives TextField. – malex Sep 17 '15 at 19:52
  • @malex I have some text fields on a uitableview inside a cell, the method above works even on iOS 9, i haven't tried a scrollview, but since the tableview inherits from scrollview, I think it should also work?. Have you found other instances where this solution doesn't work? – Oscar Gomez Nov 09 '15 at 21:13
  • It's won't work at least if responder is UITableViewCell (http://stackoverflow.com/a/24343989/95060). – Fanruten Nov 10 '15 at 18:02
  • 1
    Highly recommended to prefix objc category methods with your class prefix (for ex `abc_currentFirstResponder`). – hhanesand Jan 01 '16 at 04:26
  • anyone tell me how can i use this code in non ARC project? – duong khang Nov 08 '17 at 09:32
25

I wrote a category on UIResponder to find the first responder

@interface UIResponder (firstResponder)
- (id) currentFirstResponder;
@end

and

#import <objc/runtime.h>
#import "UIResponder+firstResponder.h"

static char const * const aKey = "first";

@implementation UIResponder (firstResponder)

- (id) currentFirstResponder {
    [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:self forEvent:nil];
    id obj = objc_getAssociatedObject (self, aKey);
    objc_setAssociatedObject (self, aKey, nil, OBJC_ASSOCIATION_ASSIGN);
    return obj;
}

- (void) setCurrentFirstResponder:(id) aResponder {
    objc_setAssociatedObject (self, aKey, aResponder, OBJC_ASSOCIATION_ASSIGN);
}

- (void) findFirstResponder:(id) sender {
    [sender setCurrentFirstResponder:self];
}

@end

Then in any class that derives from a UIResponder you can get the first responder by calling

UIResponder* aFirstResponder = [self currentFirstResponder];

but remember to import the UIResponder category interface file first!

This uses documented API's so there should be no app store rejection issues.

VJK
  • 564
  • 1
  • 5
  • 5
  • 4
    Using `-[UIApplication sendAction:to:from:forEvent:]` is neat! :) But, instead of using an associated object to reference `firstResponder`, why not just use a static variable since there'll only be one `firstResponder` at a time? – ma11hew28 Jul 01 '12 at 21:26
  • Oh that's clever! I couldn't make send of your `sendAction:` until I read the docs. That must be the only part of iOS's APIs that actually gives direct access to the first responder. – sobri Oct 21 '12 at 03:53
  • just returns the view that asked the currentFirstResponder. When i check the responder if it's first responder as expected it say no – AppHandwerker Jun 04 '14 at 13:42
11

If you need first responder just so you can ask it to resign its status, here is an approach to get any to resign. UIView has a method that will iterate through all of UIViews subviews and ask any that are first responder to resign.

[[self view] endEditing:YES];

Here is a link to Apple's UIView Docs "This method looks at the current view and its subview hierarchy for the text field that is currently the first responder. If it finds one, it asks that text field to resign as first responder. If the force parameter is set to YES, the text field is never even asked; it is forced to resign."

Praxiteles
  • 5,074
  • 5
  • 37
  • 62
11

You would need to iterate over all of the child controls and test the isFirstResponder property. When you encounter TRUE, break out of the loop.

UIView *firstResponder;
for (UIView *view in self.view.subviews) //: caused error
{
    if (view.isFirstResponder)
    {
        firstResponder = view;
        break;
    }
}

BETTER SOLUTION

See Jakob's answer.

Community
  • 1
  • 1
Evan Mulawski
  • 51,888
  • 11
  • 110
  • 142
  • 7
    This snippet only searches direct descendants of a view, but wont find the first responder when it is nested deeper. – Jakob Egger Jan 03 '13 at 08:32