How to determine which textfield has focus in swift [duplicate] - swift
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.
If your ultimate aim is just to resign the first responder, this should work: [self.view endEditing:YES]
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`
}
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)
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)
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
}
}
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];
For a Swift 3 & 4 version of nevyn's answer:
UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
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
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.
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.
Iterate over the views that could be the first responder and use - (BOOL)isFirstResponder to determine if they currently are.
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.
Additionally, I zero out the optional that holds the found responder in a defer statement from within the call itself. This ensures no references remain--even weak ones--at the end of the call.
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
}
// Make sure to mark this with '#objc' since it has to be reachable as a selector for `sendAction`
#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.
Peter Steinberger just tweeted about the private notification UIWindowFirstResponderDidChangeNotification, which you can observe if you want to watch the firstResponder change.
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];
}
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
}
}
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];
}
}
}
}
}
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;
}];
}
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?
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 })
}
}
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
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;
}
you can call privite api like this ,apple ignore:
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
SEL sel = NSSelectorFromString(#"firstResponder");
UIView *firstResponder = [keyWindow performSelector:sel];
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
}
}
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;
}
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]);
}
}
}
}
}
}
}
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
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;
}
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.
Related
Enable Search button when searching string is empty in default search bar
In my application I am using search functionality using default IOS search bar, If i place some string for search its working fine but after the first search i need to display the entire data Source (original content) My functionality is if the search string is empty it will display the entire data source. My issue is if i make the search string as empty in default search bar, the search button automatically come to hide state. I need to enable the search button even the string is empty.
Actually you can just set searchBar.enablesReturnKeyAutomatically = NO; Tested on iOS 7+
This code display Search Button if you have empty string. - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { [self.searchBar setShowsCancelButton:YES animated:YES]; self.tblView.allowsSelection = NO; self.tblView.scrollEnabled = NO; UITextField *searchBarTextField = nil; for (UIView *subview in self.searchBar.subviews) { if ([subview isKindOfClass:[UITextField class]]) { searchBarTextField = (UITextField *)subview; break; } } searchBarTextField.enablesReturnKeyAutomatically = NO; }
Swift 3/ iOS 10 for view1 in searchBar.subviews { for view2 in view1.subviews { if let searchBarTextField = view2 as? UITextField { searchBarTextField.enablesReturnKeyAutomatically = false break } } }
Use the following code for enable return key with no text UITextField *searchField = nil; for (UIView *subview in searchBar.subviews) { if ([subview isKindOfClass:[UITextField class]]) { searchField = (UITextField *)subview; break; } } if (searchField) { searchField.enablesReturnKeyAutomatically = NO; }
Maybe is an apple side bug?. Since from the .xib file setting auto-enable return Key does not work as expected. Just add the following code: - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { searchBar.enablesReturnKeyAutomatically = NO; return YES; }
Create a custom view & on that view add one button to remove the keyboard. Add that view when - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar delegate method of UISearchBar. On that button click resign the keyboard as well as the view which you created for that button. Also if you want to search on that button click then you can do it as well. Please see image for more clarification.
Its almost same as the accepted answer but if You are working with iOS 7 you will need extra for loop due to some changes in search bar - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { [self.searchBar setShowsCancelButton:YES animated:YES]; UITextField *searchBarTextField = nil; for (UIView *mainview in self.searchBar.subviews) { for (UIView *subview in mainview.subviews) { if ([subview isKindOfClass:[UITextField class]]) { searchBarTextField = (UITextField *)subview; break; } } } searchBarTextField.enablesReturnKeyAutomatically = NO; }
Use this it works for ios 6 and 7: - (void)searchBarTextDidBeginEditing:(UISearchBar *)search { UITextField *searchBarTextField = nil; for (UIView *mainview in search.subviews) { if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { if ([mainview isKindOfClass:[UITextField class]]) { searchBarTextField = (UITextField *)mainview; break; } } for (UIView *subview in mainview.subviews) { if ([subview isKindOfClass:[UITextField class]]) { searchBarTextField = (UITextField *)subview; break; } } } searchBarTextField.enablesReturnKeyAutomatically = NO; }
If you're looking for a bit more elegant solution, you could use recursion to find the textfield within the searchbar, as shown below. This should work for ios 6, 7, or any other future ios barring any deprecations by apple. - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { UITextField* textField = [self findTextFieldInView:searchBar]; if (textField) { textField.enablesReturnKeyAutomatically = NO; } } -(UITextField*)findTextFieldInView:(UIView*)view { if ([view isKindOfClass:[UITextField class]]) { return (UITextField*)view; } for (UIView* subview in view.subviews) { UITextField* textField = [self findTextFieldInView:subview]; if (textField) { return textField; } } return nil; }
Simple Swift version: if let searchTextField:UITextField = searchBar.subviews[0].subviews[2] as? UITextField { searchTextField.enablesReturnKeyAutomatically = false }
Here is a solution using Swift. Just paste it in your viewDidLoad function and make sure that you have an IBOutlet of your searchBar in the code. (on my example below, the inputSearchBar variable is the IBOutlet) // making the search button available when the search text is empty var searchBarTextField : UITextField! for view1 in inputSearchBar.subviews { for view2 in view1.subviews { if view2.isKindOfClass(UITextField) { searchBarTextField = view2 as UITextField searchBarTextField.enablesReturnKeyAutomatically = false break } } }
In swift use UISearchBarDelegate func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { searchBar.enablesReturnKeyAutomatically = false }`
Objective-C how to disable user interaction selectively
I have a Main View Controller that has many subviews. What I want is to disable all other views except one subview and its subviews programmatically from the subview file. But all I get is all frozen views. What did I do wrong? I tried this code: #define kDontDisableUserInteraction 321 - (id)initWithFrame:(CGRect)frame { NSLog(#"initWithFrame"); self = [super initWithFrame:frame]; if (self) { // Initialization code self.tag = kDontDisableUserInteraction; } return self; } -(void)something{ MVC *myController = [self getMVC]; for (UIView* subview in myController.view.subviews) { NSLog(#"subview.tag %i", subview.tag); if (subview.tag != kDontDisableUserInteraction){ subview.userInteractionEnabled = NO; } } for (UIView *view in self.subviews){ NSLog(#"enabled!"); view.userInteractionEnabled = YES; } } - (MVC *)getMVC { Class vcc = [MVC class]; // Called here to avoid calling it iteratively unnecessarily. UIResponder *responder = self; while ((responder = [responder nextResponder])) if ([responder isKindOfClass: vcc]) return (MVC *)responder; return nil; }
Following links may be helpful: How to disable touch input to all views except the top-most view? UIView -- "user interaction enabled" false on parent but true on child?
I solved it by applying a full screen of a button on all other views and get the one view that I want to have user interaction upon the button. This way I disallow the user to click on any function except the one view I want the user to click on certain functions.
UIMenuController hides the keyboard
I currently have an application which is for chatting. I used a UItextField for input box and bubbles for display messages, some thing like the system SMS. I want to enable copy paste on the message bubbles (labels). The problem is, when I want to show the UIMenuController, the label which i need to copy from need to become first responder. If the keyboard is currently displayed, when the label become first responder, the textfield will lost focus, thus the keyboard will be hide automatically. this cause an UI scroll and feels not good. Is there anyway that i can keep the keyboard shown even when i need to show the menu?
For those who still looking for answer here is code (main idea belongs to neon1, see linked question). The idea is following: if a responder doesn't know how to handle given action, it propogates it to the next responder in chain. Until now we have two candidates for first responders: Cell TextField Each of them have separate chain of responders (in fact, no, they do have common ancestor, so their chains have something in common, but we cannot use it): UITextField <- UIView <- ... <- UIWindow <- UIApplication UITableViewCell <- UIView <- ... <- UIWindow <- UIApplication So we would like to have following chain of reponders: UITextField <- UITableViewCell <- ..... <- UIWindow <- UIApplication We need to subclass UITextField (code is taken from here): CustomResponderTextView.h #interface CustomResponderTextView : UITextView #property (nonatomic, weak) UIResponder *overrideNextResponder; #end CustomResponderTextView.m #implementation CustomResponderTextView #synthesize overrideNextResponder; - (UIResponder *)nextResponder { if (overrideNextResponder != nil) return overrideNextResponder; else return [super nextResponder]; } #end This code is very simple: it returns real responder in case we haven't set any custom next responder, otherwise returns our custom responder. Now we can set new responder in our code (my example adds custom actions): CustomCell.m #implementation CustomCell - (BOOL) canBecomeFirstResponder { return YES; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return (action == #selector(copyMessage:) || action == #selector(deleteMessage:)); } #end - (void) copyMessage:(id)sender { // copy logic here } - (void) deleteMessage:(id)sender { // delete logic here } Controller - (void) viewDidLoad { ... UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:#"Custom copy" action:#selector(copyMessage:)]; UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:#"Custom delete" action:#selector(deleteMessage:)]; UIMenuController *menu = [UIMenuController sharedMenuController]; [menu setMenuItems:#[copyItem, deleteItem]]; ... } - (void) longCellTap { // cell is UITableViewCell, that has received tap if ([self.textField isFirstResponder]) { self.messageTextView.overrideNextResponder = cell; [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuDidHide:) name:UIMenuControllerDidHideMenuNotification object:nil]; } else { [cell becomeFirstResponder]; } } - (void)menuDidHide:(NSNotification*)notification { self.messageTextView.overrideNextResponder = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil]; } Last step is making first responder (in our case text field) propogate copyMessage: and deleteMessage: actions to next responder (cell in our case). As we know iOs sends canPerformAction:withSender: to know, if given responder can handle the action. We need to modify CustomResponderTextView.m and add the following function: CustomResponderTextView.m ... - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { if (overrideNextResponder != nil) return NO; else return [super canPerformAction:action withSender:sender]; } ... In case we've set our custom next responder we send all actions to it (you can modify this part, if you need some actions on textField), otherwise we ask our supertype if it can handles it.
You can try to subclass your uitextfield and override the firstresponder. Check in your long press gesture handler if the uitextfield is the first responder and override the nextresponder.
Just did it in Swift via Nikita Took's solution. I have a chat screen where there is a Text Field for text Input and Labels for messages (their display). When you tap on a message label, MENU (copy/paste/...) should appear, but the keyboard must stay open if already. I subclassed the input text field: import UIKit class TxtInputField: UITextField { weak var overrideNextResponder: UIResponder? override func nextResponder() -> UIResponder? { if overrideNextResponder != nil { return overrideNextResponder } else { return super.nextResponder() } } override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { if overrideNextResponder != nil { return false } else { return super.canPerformAction(action, withSender: sender) } } } Then in my custom message label (subclass of UILabel but it can be a View Controller in your case) which has logic to start UIMenuController, I added after if recognizer.state == UIGestureRecognizerState.Began { ... the following chunk if let activeTxtField = getMessageThreadInputSMSField() { if activeTxtField.isFirstResponder() { activeTxtField.overrideNextResponder = self } else { self.becomeFirstResponder() } } else { self.becomeFirstResponder() } When user taps outside of UIMenuController func willHideEditMenu() { if let activeTxtField = getMessageThreadInputSMSField() { activeTxtField.overrideNextResponder = nil } NSNotificationCenter.defaultCenter().removeObserver(self, name: UIMenuControllerWillHideMenuNotification, object: nil) } You have to get the reference to the activeTxtField object. I did it iterating the Navigation stack, getting my View Controller which holds the desired text field and then using it. Just in case you need it, here is the snippet for that part as well. var activeTxtField = CutomTxtInputField() for vc in navigationController?.viewControllers { if vc is CustomMessageThreadVC { let msgVC = vc as! CustomMessageThreadVC activeTxtField = msgVC.textBubble } }
How to enable cancel button with UISearchBar?
In the contacts app on the iPhone if you enter a search term, then tap the "Search" button, the keyboard is hidden, BUT the cancel button is still enabled. In my app the cancel button gets disabled when I call resignFirstResponder. Anyone know how to hide the keyboard while maintaining the cancel button in an enabled state? I use the following code: - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; } The keyboard slides out of view, but the "Cancel" button to the right of the search text field is disabled, so that I cannot cancel the search. The contacts app maintains the cancel button in an enabled state. I think maybe one solution is to dive into the searchBar object and call resignFirstResponder on the actual text field, rather than the search bar itself. Any input appreciated.
This method worked in iOS7. - (void)enableCancelButton:(UISearchBar *)searchBar { for (UIView *view in searchBar.subviews) { for (id subview in view.subviews) { if ( [subview isKindOfClass:[UIButton class]] ) { [subview setEnabled:YES]; NSLog(#"enableCancelButton"); return; } } } } (Also be sure to call it anywhere after [_searchBar resignFirstResponder] is used.)
try this for(id subview in [yourSearchBar subviews]) { if ([subview isKindOfClass:[UIButton class]]) { [subview setEnabled:YES]; } }
The accepted solution will not work when you start scrolling the table instead of tapping the "Search" button. In that case the "Cancel" button will be disabled. This is my solution that re-enables the "Cancel" button every time it is disabled by using KVO. - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Search for Cancel button in searchbar, enable it and add key-value observer. for (id subview in [self.searchBar subviews]) { if ([subview isKindOfClass:[UIButton class]]) { [subview setEnabled:YES]; [subview addObserver:self forKeyPath:#"enabled" options:NSKeyValueObservingOptionNew context:nil]; } } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // Remove observer for the Cancel button in searchBar. for (id subview in [self.searchBar subviews]) { if ([subview isKindOfClass:[UIButton class]]) [subview removeObserver:self forKeyPath:#"enabled"]; } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // Re-enable the Cancel button in searchBar. if ([object isKindOfClass:[UIButton class]] && [keyPath isEqualToString:#"enabled"]) { UIButton *button = object; if (!button.enabled) button.enabled = YES; } }
As of iOS 6, the button appears to be a UINavigationButton (private class) instead of a UIButton. I have tweaked the above example to look like this. for (UIView *v in searchBar.subviews) { if ([v isKindOfClass:[UIControl class]]) { ((UIControl *)v).enabled = YES; } } However, this is obviously brittle, since we're mucking around with the internals. It also can enable more than the button, but it works for me until a better solution is found. We should ask Apple to expose this.
This seemed to work for me (in viewDidLoad): __unused UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self]; I realize I should probably be using the UISearchDisplayController properly, but this was an easy fix for my current implementation.
You can use the runtime API to access the cancel button. UIButton *btnCancel = [self.searchBar valueForKey:#"_cancelButton"]; [btnCancel setEnabled:YES];
I expanded on what others here already posted by implementing this as a simple category on UISearchBar. UISearchBar+alwaysEnableCancelButton.h #import <UIKit/UIKit.h> #interface UISearchBar (alwaysEnableCancelButton) #end UISearchBar+alwaysEnableCancelButton.m #import "UISearchBar+alwaysEnableCancelButton.h" #implementation UISearchBar (alwaysEnableCancelButton) - (BOOL)resignFirstResponder { for (UIView *v in self.subviews) { // Force the cancel button to stay enabled if ([v isKindOfClass:[UIControl class]]) { ((UIControl *)v).enabled = YES; } // Dismiss the keyboard if ([v isKindOfClass:[UITextField class]]) { [(UITextField *)v resignFirstResponder]; } } return YES; } #end
Here's a slightly more robust solution that works on iOS 7. It will recursively traverse all subviews of the search bar to make sure it enables all UIControls (which includes the Cancel button). - (void)enableControlsInView:(UIView *)view { for (id subview in view.subviews) { if ([subview isKindOfClass:[UIControl class]]) { [subview setEnabled:YES]; } [self enableControlsInView:subview]; } } Just call this method immediately after you call [self.searchBar resignFirstResponder] like this: [self enableControlsInView:self.searchBar]; Voila! Cancel button remains enabled.
Till iOS 12, you can use like this:- if let cancelButton : UIButton = self.menuSearchBar.value(forKey: "_cancelButton") as? UIButton{ cancelButton.isEnabled = true } As of iOS 13, if you use like (forKey: "_cancelButton"), so this use of private API is caught and leads to a crash, unfortunately. For iOS 13+ & swift 5+ if let cancelButton : UIButton = self.menuSearchBar.value(forKey: "cancelButton") as? UIButton { cancelButton.isEnabled = true }
I found a different approach for making it work in iOS 7. What I'm trying is something like the Twitter iOS app. If you click on the magnifying glass in the Timelines tab, the UISearchBar appears with the Cancel button activated, the keyboard showing, and the recent searches screen. Scroll the recent searches screen and it hides the keyboard but it keeps the Cancel button activated. This is my working code: UIView *searchBarSubview = self.searchBar.subviews[0]; NSArray *subviewCache = [searchBarSubview valueForKeyPath:#"subviewCache"]; if ([subviewCache[2] respondsToSelector:#selector(setEnabled:)]) { [subviewCache[2] setValue:#YES forKeyPath:#"enabled"]; } I arrived at this solution by setting a breakpoint at my table view's scrollViewWillBeginDragging:. I looked into my UISearchBar and bared its subviews. It always has just one, which is of type UIView (my variable searchBarSubview). Then, that UIView holds an NSArray called subviewCache and I noticed that the last element, which is the third, is of type UINavigationButton, not in the public API. So I set out to use key-value coding instead. I checked if the UINavigationButton responds to setEnabled:, and luckily, it does. So I set the property to #YES. Turns out that that UINavigationButton is the Cancel button. This is bound to break if Apple decides to change the implementation of a UISearchBar's innards, but what the hell. It works for now.
SWIFT version for David Douglas answer (tested on iOS9) func enableSearchCancelButton(searchBar: UISearchBar){ for view in searchBar.subviews { for subview in view.subviews { if let button = subview as? UIButton { button.enabled = true } } } }
Most of the posted solutions are not robust, and will let the Cancel button get disabled under various circumstances. I have attempted to implement a solution that always keeps the Cancel button enabled, even when doing more complicated things with the search bar. This is implemented as a custom UISearchView subclass in Swift 4. It uses the value(forKey:) trick to find both the cancel button and the search text field, and listens for when the search field ends editing and re-enables the cancel button. It also enables the cancel button when switching the showsCancelButton flag. It contains a couple of assertions to warn you if the internal details of UISearchBar ever change and prevent it from working. import UIKit final class CancelSearchBar: UISearchBar { override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder: NSCoder) { super.init(coder: coder) setup() } private func setup() { guard let searchField = value(forKey: "_searchField") as? UIControl else { assertionFailure("UISearchBar internal implementation has changed, this code needs updating") return } searchField.addTarget(self, action: #selector(enableSearchButton), for: .editingDidEnd) } override var showsCancelButton: Bool { didSet { enableSearchButton() } } #objc private func enableSearchButton() { guard showsCancelButton else { return } guard let cancelButton = value(forKey: "_cancelButton") as? UIControl else { assertionFailure("UISearchBar internal implementation has changed, this code needs updating") return } cancelButton.isEnabled = true } }
Building on smileyborg's answer, just place this in your searchBar delegate: - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { dispatch_async(dispatch_get_main_queue(), ^{ __block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *); void (^ensureCancelButtonRemainsEnabled)(UIView *); weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) { for (UIView *subview in view.subviews) { if ([subview isKindOfClass:[UIControl class]]) { [(UIControl *)subview setEnabled:YES]; } weakEnsureCancelButtonRemainsEnabled(subview); } }; ensureCancelButtonRemainsEnabled(searchBar); }); } This solution works well on iOS 7 and above.
For iOS 10, Swift 3: for subView in self.movieSearchBar.subviews { for view in subView.subviews { if view.isKind(of:NSClassFromString("UIButton")!) { let cancelButton = view as! UIButton cancelButton.isEnabled = true } } }
For iOS 9/10 (tested), Swift 3 (shorter): searchBar.subviews.flatMap({$0.subviews}).forEach({ ($0 as? UIButton)?.isEnabled = true })
For iOS 11 and above, Swift 4-5: extension UISearchBar { func alwaysShowCancelButton() { for subview in self.subviews { for ss in subview.subviews { if #available(iOS 13.0, *) { for s in ss.subviews { self.enableCancel(with: s) } }else { self.enableCancel(with: ss) } } } } private func enableCancel(with view:UIView) { if NSStringFromClass(type(of: view)).contains("UINavigationButton") { (view as! UIButton).isEnabled = true } } } UISearchBarDelegate func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { self.searchBar.resignFirstResponder() self.searchBar.alwaysShowCancelButton() }
for (UIView *firstView in searchBar.subviews) { for(UIView* view in firstView.subviews) { if([view isKindOfClass:[UIButton class]]) { UIButton* button = (UIButton*) view; [button setEnabled:YES]; } } }
You can create your CustomSearchBar inheriting from UISearchBar and implement this method: - (void)layoutSubviews { [super layoutSubviews]; #try { UIView *baseView = self.subviews[0]; for (UIView *possibleButton in baseView.subviews) { if ([possibleButton respondsToSelector:#selector(setEnabled:)]) { [(UIControl *)possibleButton setEnabled:YES]; } } } #catch (NSException *exception) { NSLog(#"ERROR%#",exception); } }
A better solution is [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil].enabled = YES;
Better & Easy method: [(UIButton *)[self.searchBar valueForKey:#"_cancelButton"] setEnabled:YES];
Swift 5 & iOS 14 if let cancelButton : UIButton = self.menuSearchBar.value(forKey: "cancelButton") as? UIButton { cancelButton.isEnabled = true }
One alternative that should be slightly more robust against UIKit changes, and doesn't reference anything private by name is to use the appearance proxy to set the tag of the cancel button. i.e., somewhere in setup: let cancelButtonAppearance = UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]) cancelButtonAppearance.isEnabled = true cancelButtonAppearance.tag = -4321 Then we can use the tag, here the magic number -4321 to find the tag: extension UISearchBar { var cancelButton: UIControl? { func recursivelyFindButton(in subviews: [UIView]) -> UIControl? { for subview in subviews.reversed() { if let control = subview as? UIControl, control.tag == -4321 { return control } if let button = recursivelyFindButton(in: subview.subviews) { return button } } return nil } return recursivelyFindButton(in: subviews) } } And finally use searchBar.cancelButton?.isEnabled = true whenever the search bar loses focus, such as in the delegate. (Or if you use the custom subclass and call setShowsCancelButton from the delegate, you can override that function to also enable the button whenever it is shown.)
uikeyboard return
I have one view name:myplay.h and myplay.m my view contain one textfield name txtplay.. It contain one button name btnplay. In button event i want to check that if uikeyboard is open then close it. I know below code - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return TRUE; } and in button click event -(IBAction)btnplayclick:(id)sender { [self.txtplay resignFirstResponder]; .... .... } I want a global code to resign.
Try this: [self.view endEditing:YES]; From the doc: endEditing: Causes the view (or one of its embedded text fields) to resign the first responder status. (BOOL)endEditing:(BOOL)force 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. Available in iOS 2.0 and later.
The best way i think to be generic and reuse your code anywhere is to create a category for the UIView : #implementation UIView (FindFirstResponder) - (UIView *)findFirstResonder { if (self.isFirstResponder) { return self; } for (UIView *subView in self.subviews) { UIView *firstResponder = [subView findFirstResonder]; if (firstResponder != nil) { return firstResponder; } } return nil; } #end And then to call the method like that : -(IBAction)btnplayclick:(id)sender { UIView *firstResponder = [self.view findFirstResonder]; [firstResponder resignFirstResponder]; } It does the tricks perfectly for me.
or just write [self.view findAndResignFirstResponder];
Calling resignFirstResponder on a UITextField that ISN'T first responder is a harmless no-op. So go: for (UIView *candidate in self.subview) { if ([candidate isKindOfClass:[UITextView class]]) { [(UITextView *)candidate resignFirstResponder]; } }