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 isFirstResponder
message to each one?
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 isFirstResponder
message to each one?
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.
I really like VJK's solution, but as MattDiPasquale suggests it seems more complex than necessary. So I wrote this simpler version:
UIResponder+FirstResponder.h:
#import <UIKit/UIKit.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
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]
UIResponder *
make more sense than id
? –
Kenny 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. –
Compound 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. –
Portion 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. –
Epigrammatize +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. –
Compound UIKeyboardWillShowNotification
, since its not firstResponder yet when the notification handler called... –
Excess 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. –
Lunkhead abc_currentFirstResponder
). –
Yah override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool
then ensure you return true for the action this solution uses, i.e. if action == #selector(findFirstResponder(sender:)) {return true}
–
Fiske 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.
-[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? –
Maryjanemaryjo 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. –
Thaxton 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."
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.
© 2022 - 2024 — McMap. All rights reserved.