350

I submitted my app a little over a week ago and got the dreaded rejection email today. It tells me that my app cannot be accepted because I'm using a non-public API; specifically, it says,

The non-public API that is included in your application is firstResponder.

Now, the offending API call is actually a solution I found here on SO:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView   *firstResponder = [keyWindow performSelector:@selector(firstResponder)];

How do I get the current first responder on the screen? I'm looking for a way that won't get my app rejected.

Mark Amery
  • 110,735
  • 57
  • 354
  • 402
Justin Kredible
  • 7,970
  • 15
  • 60
  • 88
  • 6
    Rather than iterating over views, you can use this *truly brilliant* bit of magic: http://stackoverflow.com/a/14135456/746890 – Chris Nolet Jul 25 '14 at 01:15

28 Answers28

533

If your ultimate aim is just to resign the first responder, this should work: [self.view endEditing:YES]

cdyson37
  • 7,222
  • 2
  • 21
  • 15
  • Wow, I've always done crazy hacks for this and it's been there since 2.0! (according to the docs). Vote this one up more! – RickDT Jun 13 '11 at 01:09
  • 122
    For all of you saying that this is the answer to the "question", can you take a look at the actual question? The question asks how to get the current first responder. Not how to resign the first responder. – Justin Kredible Jan 04 '12 at 20:02
  • 2
    and where should we call this self.view endEditing? – user4951 Nov 13 '12 at 10:47
  • 8
    True enough that this is not exactly answering the question, but clearly it is a very useful answer. Thanks! – NovaJoe Nov 29 '12 at 15:52
  • 6
    +1 even if it's not an answer to the question. Why? Because many come here with a question in mind that this answer answers :) At least 267 (at the moment) of them... – Rok Jarc May 08 '13 at 17:02
  • 1
    This is not answering the question. – Sasho Mar 07 '14 at 09:12
  • If you try to resign a view outside of "view" (you cant see it), you will get an EXC_BAD_ACCESS Exception... – Frederic Yesid Peña Sánchez Jun 05 '14 at 20:08
  • Great. So, how do you use it and from where do you call it? – Alex Zavatone Apr 04 '16 at 04:14
  • @AlexZavatone - If you don't know where to put it, search for questions related to your *goal* - under what circumstances are you wanting to resign first responder? If you can't find the correct situation, then ask a new question. – ToolmakerSteve Mar 14 '17 at 06:12
350

In one of my applications I often want the first responder to resign if the user taps on the background. For this purpose I wrote a category on UIView, which I call on the UIWindow.

The following is based on that and should return the first responder.

@implementation UIView (FindFirstResponder)
- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;        
    }
    for (UIView *subView in self.subviews) {
        id responder = [subView findFirstResponder];
        if (responder) return responder;
    }
    return nil;
}
@end

iOS 7+

- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;
    }
    for (UIView *subView in self.view.subviews) {
        if ([subView isFirstResponder]) {
            return subView;
        }
    }
    return nil;
}

Swift:

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }

        for subview in subviews {
            if let firstResponder = subview.firstResponder {
                return firstResponder
            }
        }

        return nil
    }
}

Usage example in Swift:

if let firstResponder = view.window?.firstResponder {
    // do something with `firstResponder`
}
Jeehut
  • 15,556
  • 7
  • 54
  • 70
Thomas Müller
  • 15,109
  • 6
  • 40
  • 47
  • 281
    Why is this a better solution than simply calling `[self.view endEditing:YES]`? – Tim Sullivan Aug 20 '10 at 22:25
  • 71
    @Tim I didn't you could do that. It's obviously a much simpler way to resign the first responder. It doesn't help with the original questions, though, which was to identify the first responder. – Thomas Müller Aug 22 '10 at 23:01
  • 1
    @Thomas: Cool... I was mostly curious if I was missing something obvious. – Tim Sullivan Aug 25 '10 at 01:44
  • "if(firstResponder != nil)" is the same thing as writing "BOOL b = ...; if (b != NO)" instead of "BOOL b = ...; if (b)" – Gargo Oct 10 '12 at 09:40
  • @TimSullivan for one thing, calling [self.view endEditing:YES] does not animate the keyboard. – Jay Q. Dec 13 '13 at 01:48
  • @Tim: Because question is not about dismissing - question is about finding the first responser in the first place. – Lukasz Jan 29 '14 at 12:00
  • @Lukasz: My comment was a response to the original wording of the answer, in case people (like me, for example) saw this and thought it was the correct way to dismiss the keyboard. Thomas noted, 3 years ago, the point of the original question, and I responded to him then. – Tim Sullivan Jan 30 '14 at 18:47
  • 19
    Also, this is a better answer because you might want to do something else with the first responder than resigning it... – Erik B Mar 05 '14 at 13:48
  • 5
    It should be noted that this only finds UIViews, not other UIResponders that could also be the first responder (e.g. UIViewController or UIApplication). – Patrick Pijnappel Apr 29 '14 at 13:01
  • 10
    Why the difference for iOS 7? wouldn't you also want to recurse in that case? – Peter DeWeese Jul 31 '14 at 20:04
  • I don't know why Cripto edited the answer for iOS7. Looks like he was trying to use this in a view controller ('self.view')? – Thomas Müller Aug 01 '14 at 00:49
  • 3
    This code is technically correct but should not be used again and agan. Correct way to resign first responder is http://stackoverflow.com/a/11768282/2648673 – Marek H Aug 23 '14 at 13:49
  • In iOS8 have some bugs where the selector of first responder calls parent view controller methods unexpectedly. This answer is "perfect" for this kind of issue. – Daniel Omine Oct 24 '16 at 04:20
  • Just throwing my hat into the ring. Check out my answer below. It works with *any* UIResponder, not just views, and doesn't require any recursion, but like here, you still get back the actual first responder in case you wanted to do something with it. – Mark A. Donohoe Jul 06 '20 at 23:39
  • This should be recursive. What if the subview has subviews of its own. – user9400730 May 09 '21 at 11:27
190

A common way of manipulating the first responder is to use nil targeted actions. This is a way of sending an arbitrary message to the responder chain (starting with the first responder), and continuing down the chain until someone responds to the message (has implemented a method matching the selector).

For the case of dismissing the keyboard, this is the most effective way that will work no matter which window or view is first responder:

[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];

This should be more effective than even [self.view.window endEditing:YES].

(Thanks to BigZaphod for reminding me of the concept)

nevyn
  • 6,829
  • 3
  • 30
  • 41
  • 4
    This is so cool. Possibly the neatest Cocoa Touch trick I've seen on SO yet. Helped me no end! – mluisbrown Jun 01 '13 at 00:28
  • this is BY FAR the best solution, thanks a million ! this is way better than the solution above this one, because it works even of the active textfield is part of the accessory view of the keyboard (in that case it is not part of our view hierarchy) – Pizzaiola Gorgonzola Aug 30 '13 at 14:50
  • This answer it's just awesome – Antonio MG Nov 11 '13 at 14:45
  • Awesome, this is exactly what I needed (I want to send custom actions through the responder chain). The above solutions fail when the first responder is a view, but this is perfect. – Patrick Pijnappel Mar 05 '14 at 04:49
  • 2
    This solution is the best-practice solution. The system already knows who the first responder is, there's no need to find it. – leftspin Mar 07 '14 at 20:35
  • 2
    I want to give this answer more than one upvote. This is fricking genius. – Reid Main Apr 18 '14 at 02:35
  • This doesn't even answer the question asked. How can it be the best solution? See Jakob Egger's answer for the real solution. – devios1 Nov 07 '14 at 00:30
  • Clean, efficient and elegant! – Scott Allen Dec 05 '15 at 17:50
  • 1
    devios1: Jakob's answer is exactly what the question asks for, but it's a hack on top of the responder system, rather than using the responder system the way it was designed to. This is cleaner and accomplishes what most people come to this question for, and in a one-liner too. (You can of course send any target-action style message, and not just resignFirstResponder). – nevyn Jan 26 '17 at 00:59
  • Warning: This doesn't work if you start adding multiple windows. I unfortunately can't point to a cause beyond that yet. – Heath Borders Jun 05 '17 at 21:03
  • Great. That's it. Beatiful. – heiko Nov 17 '19 at 15:20
103

Here's a category that allows you to quickly find the first responder by calling [UIResponder currentFirstResponder]. Just add the following two files to your project:

UIResponder+FirstResponder.h

#import <Cocoa/Cocoa.h>
@interface UIResponder (FirstResponder)
    +(id)currentFirstResponder;
@end

UIResponder+FirstResponder.m

#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

The trick here is that sending an action to nil sends it to the first responder.

(I originally published this answer here: https://stackoverflow.com/a/14135456/322427)

Community
  • 1
  • 1
Jakob Egger
  • 11,393
  • 4
  • 35
  • 47
  • 1
    +1 This is the most elegant solution to the problem (and the actual question asked) I've seen yet. Reusable and efficient! Upvote this answer! – devios1 Nov 06 '14 at 19:19
  • 3
    Bravo. This might just be to most beautiful and clean hack I have ever seen for Objc... – Aviel Gross Dec 22 '14 at 13:26
  • Very nice. Should work really well for then being able to ask the first responder who (if anyone) in the chain will handle a particular action method were it to be sent. You can use this to enable or disable controls based on whether they're able to be handled. I'll answer my [own question](http://stackoverflow.com/q/27863940/2547229) on this and reference your answer. – Benjohn Jan 09 '15 at 16:00
  • In Swift: http://stackoverflow.com/questions/1823317/get-the-current-first-responder-without-using-a-private-api/37492338#37492338 – Valentin Shergin May 27 '16 at 21:13
  • Warning: This doesn't work if you start adding multiple windows. I unfortunately can't point to a cause beyond that yet. – Heath Borders Jun 05 '17 at 21:03
  • This is an actual answer, unlike the several super upvoted answers that don't actually answer the question. And the accepted answer which is super inefficient. – mxcl Sep 19 '17 at 05:00
  • For me, this isn't working when the field in question is occluded by another view. E.g., if the user scrolls the first responder field out of view, under the keyboard in a scroll view. – Chris Prince Jan 31 '18 at 23:42
  • Whoops. My bad. It was another issue. It *is* working when occluded. :). – Chris Prince Feb 01 '18 at 00:09
  • This.is.just.beautiful! Congrats sir! – m_katsifarakis Feb 26 '19 at 19:42
  • 2
    Apple blocked using the method name `findFirstResponder` in a selector passed to `-[UIApplication sendAction:to:from:forEvent:]` in iOS 14. It seems they noticed this answer. But you can just use a different method name instead. – Nick Sep 27 '20 at 11:04
46

Here is a Extension implemented in Swift based on Jakob Egger's most excellent answer:

import UIKit

extension UIResponder {
    // Swift 1.2 finally supports static vars!. If you use 1.1 see: 
    // http://stackoverflow.com/a/24924535/385979
    private weak static var _currentFirstResponder: UIResponder? = nil
    
    public class func currentFirstResponder() -> UIResponder? {
        UIResponder._currentFirstResponder = nil
        UIApplication.sharedApplication().sendAction("findFirstResponder:", to: nil, from: nil, forEvent: nil)
        return UIResponder._currentFirstResponder
    }
    
    internal func findFirstResponder(sender: AnyObject) {
        UIResponder._currentFirstResponder = self
    }
}

Swift 4

import UIKit    

extension UIResponder {
    private weak static var _currentFirstResponder: UIResponder? = nil
    
    public static var current: UIResponder? {
        UIResponder._currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
        return UIResponder._currentFirstResponder
    }
    
    @objc internal func findFirstResponder(sender: AnyObject) {
        UIResponder._currentFirstResponder = self
    }
}
Community
  • 1
  • 1
Code Commander
  • 14,451
  • 5
  • 54
  • 63
29

It's not pretty, but the way I resign the firstResponder when I don't know what that the responder is:

Create an UITextField, either in IB or programmatically. Make it Hidden. Link it up to your code if you made it in IB.

Then, when you want to dismiss the keyboard, you switch the responder to the invisible text field, and immediately resign it:

    [self.invisibleField becomeFirstResponder];
    [self.invisibleField resignFirstResponder];
cannyboy
  • 23,724
  • 40
  • 138
  • 242
  • 62
    This is both horrible and genius at the same time. Nice. – Alex Wayne Jun 02 '10 at 17:54
  • 4
    I find that a bit dangerous - Apple may decide one day that making a hidden control become the first responder is not intended behavior and "fix" iOS not to do this any more, and then your trick may stop working. – Thomas Tempelmann Oct 24 '12 at 13:16
  • 2
    Or you can assign the firstResponder to a UITextField that is currently visible and then call resignFirstResponder on that particular textField. This removes the need to create a hidden view. All the same, +1. – Swifty McSwifterton Jun 16 '13 at 23:51
  • 3
    I rather think it is only horrible... it may change the keyboard layout (or input view) before hiding it – Daniel Oct 23 '14 at 08:25
21

For a Swift 3 & 4 version of nevyn's answer:

UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
Pochi
  • 13,198
  • 2
  • 58
  • 99
11

Here's a solution which reports the correct first responder (many other solutions won't report a UIViewController as the first responder, for example), doesn't require looping over the view hierarchy, and doesn't use private APIs.

It leverages Apple's method sendAction:to:from:forEvent:, which already knows how to access the first responder.

We just need to tweak it in 2 ways:

  • Extend UIResponder so it can execute our own code on the first responder.
  • Subclass UIEvent in order to return the first responder.

Here is the code:

@interface ABCFirstResponderEvent : UIEvent
@property (nonatomic, strong) UIResponder *firstResponder;
@end

@implementation ABCFirstResponderEvent
@end

@implementation UIResponder (ABCFirstResponder)
- (void)abc_findFirstResponder:(id)sender event:(ABCFirstResponderEvent *)event {
    event.firstResponder = self;
}
@end

@implementation ViewController

+ (UIResponder *)firstResponder {
    ABCFirstResponderEvent *event = [ABCFirstResponderEvent new];
    [[UIApplication sharedApplication] sendAction:@selector(abc_findFirstResponder:event:) to:nil from:nil forEvent:event];
    return event.firstResponder;
}

@end
Senseful
  • 73,679
  • 56
  • 267
  • 405
8

The first responder can be any instance of the class UIResponder, so there are other classes that might be the first responder despite the UIViews. For example UIViewController might also be the first responder.

In this gist you will find a recursive way to get the first responder by looping through the hierarchy of controllers starting from the rootViewController of the application's windows.

You can retrieve then the first responder by doing

- (void)foo
{
    // Get the first responder
    id firstResponder = [UIResponder firstResponder];

    // Do whatever you want
    [firstResponder resignFirstResponder];      
}

However, if the first responder is not a subclass of UIView or UIViewController, this approach will fail.

To fix this problem we can do a different approach by creating a category on UIResponder and perform some magic swizzeling to be able to build an array of all living instances of this class. Then, to get the first responder we can simple iterate and ask each object if -isFirstResponder.

This approach can be found implemented in this other gist.

Hope it helps.

vilanovi
  • 2,047
  • 1
  • 21
  • 25
8

Using Swift and with a specific UIView object this might help:

func findFirstResponder(inView view: UIView) -> UIView? {
    for subView in view.subviews as! [UIView] {
        if subView.isFirstResponder() {
            return subView
        }
        
        if let recursiveSubView = self.findFirstResponder(inView: subView) {
            return recursiveSubView
        }
    }
    
    return nil
}

Just place it in your UIViewController and use it like this:

let firstResponder = self.findFirstResponder(inView: self.view)

Take note that the result is an Optional value so it will be nil in case no firstResponder was found in the given views subview hierarchy.

Jeehut
  • 15,556
  • 7
  • 54
  • 70
6

Iterate over the views that could be the first responder and use - (BOOL)isFirstResponder to determine if they currently are.

Johan Kool
  • 15,081
  • 8
  • 59
  • 79
5

Peter Steinberger just tweeted about the private notification UIWindowFirstResponderDidChangeNotification, which you can observe if you want to watch the firstResponder change.

Heath Borders
  • 27,732
  • 16
  • 126
  • 230
  • He get rejected because using a private api, maybe using a private notification also could be a bad idea... – Laszlo Nov 20 '13 at 01:13
5

If you just need to kill the keyboard when the user taps on a background area why not add a gesture recognizer and use it to send the [[self view] endEditing:YES] message?

you can add the Tap gesture recogniser in the xib or storyboard file and connect it to an action,

looks something like this then finished

- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
     [[self view] endEditing:YES];
}
Alladinian
  • 32,266
  • 4
  • 79
  • 85
Arkadi
  • 51
  • 1
  • 5
  • This worked great for me. I chose one connection to a view on the Xib, and I can add additional views for this to invoke in Referencing Outlet Collections – James Perih Sep 02 '13 at 07:57
5

Just it case here is Swift version of awesome Jakob Egger's approach:

import UIKit

private weak var currentFirstResponder: UIResponder?

extension UIResponder {

    static func firstResponder() -> UIResponder? {
        currentFirstResponder = nil
        UIApplication.sharedApplication().sendAction(#selector(self.findFirstResponder(_:)), to: nil, from: nil, forEvent: nil)
        return currentFirstResponder
    }

    func findFirstResponder(sender: AnyObject) {
        currentFirstResponder = self
    }

}
Valentin Shergin
  • 6,630
  • 2
  • 45
  • 50
4

This is what I did to find what UITextField is the firstResponder when the user clicks Save/Cancel in a ModalViewController:

    NSArray *subviews = [self.tableView subviews];

for (id cell in subviews ) 
{
    if ([cell isKindOfClass:[UITableViewCell class]]) 
    {
        UITableViewCell *aCell = cell;
        NSArray *cellContentViews = [[aCell contentView] subviews];
        for (id textField in cellContentViews) 
        {
            if ([textField isKindOfClass:[UITextField class]]) 
            {
                UITextField *theTextField = textField;
                if ([theTextField isFirstResponder]) {
                    [theTextField resignFirstResponder];
                }

            }
        }

    }

}
Romeo
  • 161
  • 1
  • 3
4

I have a slightly different approach than most. Rather than iterate through the collection of views looking for the one that has isFirstResponder set, I too send a message to nil, but I store the receiver of the message so I can return it and do whatever I wish with it.

import UIKit

private var _foundFirstResponder: UIResponder? = nil

extension UIResponder {

    static var first:UIResponder? {

        // Sending an action to 'nil' implicitly sends it to the first responder
        // where we simply capture it and place it in the _foundFirstResponder variable.
        // As such, the variable will contain the current first responder (if any) immediately after this line executes
        UIApplication.shared.sendAction(#selector(UIResponder.storeFirstResponder(_:)), to: nil, from: nil, for: nil)

        // The following 'defer' statement runs *after* this getter returns,
        // thus releasing any strong reference held by the variable immediately thereafter
        defer {
            _foundFirstResponder = nil
        }

        // Return the found first-responder (if any) back to the caller
        return _foundFirstResponder
    }

    @objc func storeFirstResponder(_ sender: AnyObject) {

        // Capture the recipient of this message (self), which is the first responder
        _foundFirstResponder = self
    }
}

With the above, I can resign the first responder by simply doing this...

UIResponder.first?.resignFirstResponder()

But since my API actually hands back whatever the first responder is, I can do whatever I want with it.

Here's an example that checks if the current first responder is a UITextField with a helpMessage property set, and if so, shows it in a help bubble right next to the control. We call this from a 'Quick Help' button on our screen.

func showQuickHelp(){

    if let textField   = UIResponder?.first as? UITextField,
       let helpMessage = textField.helpMessage {
    
        textField.showHelpBubble(with:helpMessage)
    }
}

The support for the above is defined in an extension on UITextField like so...

extension UITextField {
    var helpMessage:String? { ... }
    func showHelpBubble(with message:String) { ... }
}

Now to support this feature, all we have to do is decide which text fields have help messages and the UI takes care of the rest for us.

Mark A. Donohoe
  • 23,825
  • 17
  • 116
  • 235
3

With a category on UIResponder, it is possible to legally ask the UIApplication object to tell you who the first responder is.

See this:

Is there any way of asking an iOS view which of its children has first responder status?

Community
  • 1
  • 1
VJK
  • 564
  • 1
  • 5
  • 5
3

You can choose the following UIView extension to get it (credit by Daniel):

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }
        return subviews.first(where: {$0.firstResponder != nil })
    }
}
Soheil Novinfard
  • 994
  • 9
  • 34
3

This is what I have in my UIViewController Category. Useful for many things, including getting first responder. Blocks are great!

- (UIView*) enumerateAllSubviewsOf: (UIView*) aView UsingBlock: (BOOL (^)( UIView* aView )) aBlock {

 for ( UIView* aSubView in aView.subviews ) {
  if( aBlock( aSubView )) {
   return aSubView;
  } else if( ! [ aSubView isKindOfClass: [ UIControl class ]] ){
   UIView* result = [ self enumerateAllSubviewsOf: aSubView UsingBlock: aBlock ];

   if( result != nil ) {
    return result;
   }
  }
 }    

 return nil;
}

- (UIView*) enumerateAllSubviewsUsingBlock: (BOOL (^)( UIView* aView )) aBlock {
 return [ self enumerateAllSubviewsOf: self.view UsingBlock: aBlock ];
}

- (UIView*) findFirstResponder {
 return [ self enumerateAllSubviewsUsingBlock:^BOOL(UIView *aView) {
  if( [ aView isFirstResponder ] ) {
   return YES;
  }

  return NO;
 }];
}
Andrei Tchijov
  • 549
  • 5
  • 11
2

This is good candidate for recursion! No need to add a category to UIView.

Usage (from your view controller):

UIView *firstResponder = [self findFirstResponder:[self view]];

Code:

// This is a recursive function
- (UIView *)findFirstResponder:(UIView *)view {

    if ([view isFirstResponder]) return view; // Base case

    for (UIView *subView in [view subviews]) {
        if ([self findFirstResponder:subView]) return subView; // Recursion
    }
    return nil;
}
Daniel
  • 17,803
  • 7
  • 74
  • 142
Pétur Ingi Egilsson
  • 4,162
  • 4
  • 40
  • 65
2

you can call privite api like this ,apple ignore:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
SEL sel = NSSelectorFromString(@"firstResponder");
UIView   *firstResponder = [keyWindow performSelector:sel];
ibcker
  • 504
  • 6
  • 11
2

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 {

    UIView *retorno;

    for (id subView in _view.subviews) {

        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;
                }
            }
        }
    }

    return retorno;
}
Keyur
  • 39
  • 5
2

Swift version of @thomas-müller's response

extension UIView {

    func firstResponder() -> UIView? {
        if self.isFirstResponder() {
            return self
        }

        for subview in self.subviews {
            if let firstResponder = subview.firstResponder() {
                return firstResponder
            }
        }

        return nil
    }

}
Community
  • 1
  • 1
Kádi
  • 2,586
  • 14
  • 22
2

You can try also like this:

- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event { 

    for (id textField in self.view.subviews) {

        if ([textField isKindOfClass:[UITextField class]] && [textField isFirstResponder]) {
            [textField resignFirstResponder];
        }
    }
} 

I didn't try it but it seems a good solution

Frade
  • 2,780
  • 4
  • 25
  • 37
1

The solution from romeo https://stackoverflow.com/a/2799675/661022 is cool, but I noticed that the code needs one more loop. I was working with tableViewController. I edited the script and then I checked. Everything worked perfect.

I recommed to try this:

- (void)findFirstResponder
{
    NSArray *subviews = [self.tableView subviews];
    for (id subv in subviews )
    {
        for (id cell in [subv subviews] ) {
            if ([cell isKindOfClass:[UITableViewCell class]])
            {
                UITableViewCell *aCell = cell;
                NSArray *cellContentViews = [[aCell contentView] subviews];
                for (id textField in cellContentViews)
                {
                    if ([textField isKindOfClass:[UITextField class]])
                    {
                        UITextField *theTextField = textField;
                        if ([theTextField isFirstResponder]) {
                            NSLog(@"current textField: %@", theTextField);
                            NSLog(@"current textFields's superview: %@", [theTextField superview]);
                        }
                    }
                }
            }
        }
    }
}
Community
  • 1
  • 1
pedrouan
  • 11,915
  • 2
  • 55
  • 71
  • Thanks! You are correct, the most answers presented here are not a solution when working with uitableview. You could simplify your code a lot, even take out the if `if ([textField isKindOfClass:[UITextField class]])` , allowing the use for uitextview, and could return the firstresponder. – Frade Feb 06 '15 at 12:19
0

Code below work.

- (id)ht_findFirstResponder
{
    //ignore hit test fail view
    if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
        return nil;
    }
    if ([self isKindOfClass:[UIControl class]] && [(UIControl *)self isEnabled] == NO) {
        return nil;
    }

    //ignore bound out screen
    if (CGRectIntersectsRect(self.frame, [UIApplication sharedApplication].keyWindow.bounds) == NO) {
        return nil;
    }

    if ([self isFirstResponder]) {
        return self;
    }

    for (UIView *subView in self.subviews) {
        id result = [subView ht_findFirstResponder];
        if (result) {
            return result;
        }
    }
    return nil;
}   
John Chen
  • 27
  • 6
0

Update: I was wrong. You can indeed use UIApplication.shared.sendAction(_:to:from:for:) to call the first responder demonstrated in this link: http://stackoverflow.com/a/14135456/746890.


Most of the answers here can't really find the current first responder if it is not in the view hierarchy. For example, AppDelegate or UIViewController subclasses.

There is a way to guarantee you to find it even if the first responder object is not a UIView.

First lets implement a reversed version of it, using the next property of UIResponder:

extension UIResponder {
    var nextFirstResponder: UIResponder? {
        return isFirstResponder ? self : next?.nextFirstResponder
    }
}

With this computed property, we can find the current first responder from bottom to top even if it's not UIView. For example, from a view to the UIViewController who's managing it, if the view controller is the first responder.

However, we still need a top-down resolution, a single var to get the current first responder.

First with the view hierarchy:

extension UIView {
    var previousFirstResponder: UIResponder? {
        return nextFirstResponder ?? subviews.compactMap { $0.previousFirstResponder }.first
    }
}

This will search for the first responder backwards, and if it couldn't find it, it would tell its subviews to do the same thing (because its subview's next is not necessarily itself). With this we can find it from any view, including UIWindow.

And finally, we can build this:

extension UIResponder {
    static var first: UIResponder? {
        return UIApplication.shared.windows.compactMap({ $0.previousFirstResponder }).first
    }
}

So when you want to retrieve the first responder, you can call:

let firstResponder = UIResponder.first
yesleon
  • 718
  • 7
  • 12
0

Simplest way to find first responder:

func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool

The default implementation dispatches the action method to the given target object or, if no target is specified, to the first responder.

Next step:

extension UIResponder
{
    private weak static var first: UIResponder? = nil

    @objc
    private func firstResponderWhereYouAre(sender: AnyObject)
    {
        UIResponder.first = self
    }

    static var actualFirst: UIResponder?
    {
        UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
        return UIResponder.first
    }
}

Usage: Just get UIResponder.actualFirst for your own purposes.

Alexey
  • 49
  • 5