Is there any way of asking an iOS view which of its children has first responder status?
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]
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.
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.