Related
I have a custom cell, and add a viewController as a subview to that cell:
TestViewController *vc = [[TestViewController alloc] initWithNibName:#"TestViewController" bundle:nil];
[self addSubview:vc.view];
The new viewController has a tableView in it. When I try to scroll that tableView, the base tableView, where the custom cell lives, is being scrolled.
How can I solve this?
Thanks in advance
I would advice against embedding tableviews however if you really want to do it that way, the solution would probably be to implement the hitTest method on the base tableView :
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//here you will need to check if the point lies within any of the
//child tableviews and return it .. else return the super method
if ([self pointInChildTable:point]) {
return [self childTableForPoint:point];
}
return [super hitTest:point withEvent:event];
}
note that pointInChildTable and childTableForPoint should probably be the same method that returns a dictionary with the results. I separated them for explanation only.
Hope this helps.
I have an UIView high 30, than i add an UITableView as subview with 30 as origin.y. Because the table remains “under” the main view I cannot select the cells. Consequently I have implemented this method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Tocco intercettato");
if (oldSet && oldEvent) {
[table touchesEnded:oldSet withEvent:oldEvent];
}
oldSet = touches;
oldEvent = event;
[table touchesBegan:touches withEvent:event];
[table touchesEnded:touches withEvent:event];
}
The taps work, but the slides does not work and consequently I cannot slide the table. Some idea in order to resolve?
Passing the touches from an overlying UIView to an underlying UITableView does not work (as you're seeing, the taps go through but not the scrolling movements). This is because UITableView (as a subclass of UIScrollView) does all sorts of under-the-hood weirdness with the responder chain (your code is also kind of odd, where you call both touchesBegan and touchesEnded from the overlying view's touchesBegan method, but this isn't why you can't scroll the table).
A simple way to achieve what you want (assuming I've understood that correctly) is to override the hitTest:withEvent: method on your overlying view and have the method return the underlying UITableView (instead of self, which is the default implementation):
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UITableView *tv = (UITableView *)[self.superview.subviews objectAtIndex:0];
return tv;
}
Note that this code assumes the UITableView is the first view added to your view controller, and that the overlying UIView is added after that.
Edit: OK, after re-reading your question, I think I understand your question. You have a main view with a height of 30, and to that view you've added a UITableView at y = 30. I think adding this method to your main view will do what you need:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (point.y < self.frame.size.height) {
return self;
} else {
return myTableView; // assumes myTableView is a reference to the
// UITableView that you've added
}
}
Essentially, you're telling the OS to pass all touches on to myTableView if they're outside the bounds of your main view.
However, a simpler way would be to add both the 30-pixel-high main view (the orange thing) and the UITableView as subviews of another view (or view controller). That way, you wouldn't have to do anything kludgy to get your table to behave like a normal table.
A UIWebView is normally focused on the selected content when I double tap a part of a website, and now I want the UIWebView to ignore the double tap but still be able to be interacted with.
How would I go about this?
The Double Tap is recognized by a UITapGestureRecognizer.
Go through the view hierarchy if you really want this.
A little bit tricky, but it should work.
The codes look like this:
- (void)goThroughSubViewFrom:(UIView *)view {
for (UIView *v in [view subviews])
{
if (v != view)
{
[self goThroughSubViewFrom:v];
}
}
for (UIGestureRecognizer *reco in [view gestureRecognizers])
{
if ([reco isKindOfClass:[UITapGestureRecognizer class]])
{
if ([(UITapGestureRecognizer *)reco numberOfTapsRequired] == 2)
{
[view removeGestureRecognizer:reco];
}
}
}
}
I've put a demo here: NoDoubleTapWebView
Hope it helps.
iOS 5.0+:
A UIWebView contains a UIScrollView to display its content and is accessible by the scrollView property of UIWebView. So, one possible way of doing this is to subclass UIScrollView and override -touchesShouldBegin:withEvent:inContentView. You can get the number of taps from a UITouch object from its tapCount property, which can be used to filter out double-tap gestures and call the super method otherwise. This would look something like:
-(BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
UITouch * touch = [touches anyObject]; //Get a touch object
if (2 == touch.tapCount) { //If this is a double-tap...
return NO; //...don't begin touches
}
else { //Otherwise, call the superclass' implementation
return YES;
}
}
However, in order to set the web view's scroll view as your custom subclass, you may also have to subclass UIWebView (which is generally frowned upon). With that said, it would likely work to subclass UIWebView and redefine the scrollView property. In the interface file:
#interface MyAwesomeWebView : UIWebView {
}
#property (nonatomic, readonly, retain) MyAwesomeScrollView * scrollView;
#end
And in the implementation file:
#implementation
#dynamic scrollView
#end
Still, proceed with caution.
Pre iOS 5.0:
As Alan Moore wrote in the comments, you can add a transparent view on top of the web view, capture all touch events with hit testing, and forward the touch events to the web view unless there's a double tap.
EDIT: Updated answer to include caveats about subclassing UIWebView, etc.
EDIT 2: Included second answer for pre iOS 5.0
I think the way to do it is to add a UITapGestureRecognizer on top of your UIWebView, then you can filter double taps as you wish.
You can find sample code/discussion here:
Add a UITapGestureRecognizer to a UIWebView
Is there a simple way of allowing interaction with a button in a UIView that lies under another UIView - where there are no actual objects from the top UIView on top of the button?
For instance, at the moment I have a UIView (A) with an object at the top and an object at the bottom of the screen and nothing in the middle. This sits on top of another UIView that has buttons in the middle (B). However, I cannot seem to interact with the buttons in the middle of B.
I can see the buttons in B - I've set the background of A to clearColor - but the buttons in B do not seem to receive touches despite the fact that there are no objects from A actually on top of those buttons.
EDIT - I still want to be able to interact with the objects in the top UIView
Surely there is a simple way of doing this?
You should create a UIView subclass for your top view and override the following method:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// UIView will be "transparent" for touch events if we return NO
return (point.y < MIDDLE_Y1 || point.y > MIDDLE_Y2);
}
You may also look at the hitTest:event: method.
While many of the answers here will work, I'm a little surprised to see that the most convenient, generic and foolproof answer hasn't been given here. #Ash came closest, except that there is something strange going on with returning the superview... don't do that.
This answer is taken from an answer I gave to a similar question, here.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self) return nil;
return hitView;
}
[super hitTest:point withEvent:event] will return the deepest view in that view's hierarchy that was touched. If hitView == self (i.e. if there is no subview under the touch point), return nil, specifying that this view should not receive the touch. The way the responder chain works means that the view hierarchy above this point will continue to be traversed until a view is found that will respond to the touch. Don't return the superview, as it is not up to this view whether its superview should accept touches or not!
This solution is:
convenient, because it requires no references to any other views/subviews/objects;
generic, because it applies to any view that acts purely as a container for touchable subviews, and the configuration of the subviews does not affect the way it works (as it does if you override pointInside:withEvent: to return a particular touchable area).
foolproof, there's not much code... and the concept isn't difficult to get your head around.
I use this often enough that I have abstracted it into a subclass to save pointless view subclasses for one override. As a bonus, add a property to make it configurable:
#interface ISView : UIView
#property(nonatomic, assign) BOOL onlyRespondToTouchesInSubviews;
#end
#implementation ISView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self && onlyRespondToTouchesInSubviews) return nil;
return hitView;
}
#end
Then go wild and use this view wherever you might use a plain UIView. Configuring it is as simple as setting onlyRespondToTouchesInSubviews to YES.
There are several ways you could handle this. My favorite is to override hitTest:withEvent: in a view that is a common superview (maybe indirectly) to the conflicting views (sounds like you call these A and B). For example, something like this (here A and B are UIView pointers, where B is the "hidden" one, that is normally ignored):
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint pointInB = [B convertPoint:point fromView:self];
if ([B pointInside:pointInB withEvent:event])
return B;
return [super hitTest:point withEvent:event];
}
You could also modify the pointInside:withEvent: method as gyim suggested. This lets you achieve essentially the same result by effectively "poking a hole" in A, at least for touches.
Another approach is event forwarding, which means overriding touchesBegan:withEvent: and similar methods (like touchesMoved:withEvent: etc) to send some touches to a different object than where they first go. For example, in A, you could write something like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self shouldForwardTouches:touches]) {
[B touchesBegan:touches withEvent:event];
}
else {
// Do whatever A does with touches.
}
}
However, this will not always work the way you expect! The main thing is that built-in controls like UIButton will always ignore forwarded touches. Because of this, the first approach is more reliable.
There's a good blog post explaining all this in more detail, along with a small working xcode project to demo the ideas, available here:
http://bynomial.com/blog/?p=74
You have to set upperView.userInteractionEnabled = NO;, otherwise the upper view will intercept the touches.
The Interface Builder version of this is a checkbox at the bottom of the View Attributes panel called "User Interaction Enabled". Uncheck it and you should be good to go.
Custom implementation of pointInside:withEvent: indeed seemed like the way to go, but dealing with hard-coded coordinates seemed odd to me. So I ended up checking whether the CGPoint was inside the button CGRect using the CGRectContainsPoint() function:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return (CGRectContainsPoint(disclosureButton.frame, point));
}
Lately I wrote a class that will help me with just that. Using it as a custom class for a UIButton or UIView will pass touch events that were executed on a transparent pixel.
This solution is a somewhat better than the accepted answer because you can still click a UIButton that is under a semi transparent UIView while the non transparent part of the UIView will still respond to touch events.
As you can see in the GIF, the Giraffe button is a simple rectangle but touch events on transparent areas are passed on to the yellow UIButton underneath.
Link to class
I guess I'm a bit late to this party, but I'll add this possible solution:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView != self) return hitView;
return [self superview];
}
If you use this code to override a custom UIView's standard hitTest function, it will ignore ONLY the view itself. Any subviews of that view will return their hits normally, and any hits that would have gone to the view itself are passed up to its superview.
-Ash
Just riffing on the Accepted Answer and putting this here for my reference. The Accepted Answer works perfectly. You can extend it like this to allow your view's subviews to receive the touch, OR pass it on to any views behind us:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// If one of our subviews wants it, return YES
for (UIView *subview in self.subviews) {
CGPoint pointInSubview = [subview convertPoint:point fromView:self];
if ([subview pointInside:pointInSubview withEvent:event]) {
return YES;
}
}
// otherwise return NO, as if userInteractionEnabled were NO
return NO;
}
Note: You don't even have to do recursion on the subview tree, because each pointInside:withEvent: method will handle that for you.
This approach is quite clean and allows that transparent subviews are not reacting to touches as well. Just subclass UIView and add the following method to its implementation:
#implementation PassThroughUIView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *v in self.subviews) {
CGPoint localPoint = [v convertPoint:point fromView:self];
if (v.alpha > 0.01 && ![v isHidden] && v.userInteractionEnabled && [v pointInside:localPoint withEvent:event])
return YES;
}
return NO;
}
#end
Setting userInteraction property disabled might help. Eg:
UIView * topView = [[TOPView alloc] initWithFrame:[self bounds]];
[self addSubview:topView];
[topView setUserInteractionEnabled:NO];
(Note: In the code above, 'self' refers to a view)
This way, you can only display on the topView, but won't get user inputs. All those user touches will through this view and the bottom view will respond for them. I'd use this topView for displaying transparent images, or animate them.
My solution here:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint pointInView = [self.toolkitController.toolbar convertPoint:point fromView:self];
if ([self.toolkitController.toolbar pointInside:pointInView withEvent:event]) {
self.userInteractionEnabled = YES;
} else {
self.userInteractionEnabled = NO;
}
return [super hitTest:point withEvent:event];
}
Hope this helps
There's something you can do to intercept the touch in both views.
Top view:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Do code in the top view
[bottomView touchesBegan:touches withEvent:event]; // And pass them on to bottomView
// You have to implement the code for touchesBegan, touchesEnded, touchesCancelled in top/bottom view.
}
But that's the idea.
Here is a Swift version:
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
return !CGRectContainsPoint(buttonView.frame, point)
}
Swift 3
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if subview.frame.contains(point) {
return true
}
}
return false
}
I have never built a complete user interface using the UI toolkit, so I don't have much experience with it. Here is what I think should work though.
Every UIView, and this the UIWindow, has a property subviews, which is an NSArray containing all the subviews.
The first subview you add to a view will receive index 0, and the next index 1 and so forth. You can also replace addSubview: with insertSubview: atIndex: or insertSubview:aboveSubview: and such methods that can determine the position of your subview in the hierarchy.
So check your code to see which view you add first to your UIWindow. That will be 0, the other will be 1.
Now, from one of your subviews, to reach another you would do the following:
UIView * theOtherView = [[[self superview] subviews] objectAtIndex: 0];
// or using the properties syntax
UIView * theOtherView = [self.superview.subviews objectAtIndex:0];
Let me know if that works for your case!
(below this marker is my previous answer):
If views need to communicate with each other, they should do so via a controller (that is, using the popular MVC model).
When you create a new view, you can make sure it registers itself with a controller.
So the technique is to make sure your views register with a controller (which can store them by name or whatever you prefer in a Dictionary or Array). Either you can have the controller send a message for you, or you can get a reference to the view and communicate with it directly.
If your view doesn't have a link back the controller (which may be the case) then you can make use of singletons and/or class methods to get a reference to your controller.
I think the right way is to use the view chain built into the view hierarchy.
For your subviews that are pushed onto the main view, do not use the generic UIView, but instead subclass UIView (or one of its variants like UIImageView) to make MYView : UIView (or whatever supertype you want, such as UIImageView). In the implementation for YourView, implement the touchesBegan method. This method will then get invoked when that view is touched. All you need to have in that implementation is an instance method:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event ;
{ // cannot handle this event. pass off to super
[self.superview touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event]; }
this touchesBegan is a responder api, so you dont need to declare it in your public or private interface; it's one of those magic api's you just have to know about. This self.superview will bubble up the request eventually to the viewController. In the viewController, then, implement this touchesBegan to handle the touch.
Note that the touches location (CGPoint) is automatically adjusted relative to the encompassing view for you as it is bounced up the view hierarchy chain.
Just want to post this, coz I had somewhat similar problem, spent substantial amount of time trying to implement answers here without any luck. What I ended up doing:
for(UIGestureRecognizer *recognizer in topView.gestureRecognizers)
{
recognizer.delegate=self;
[bottomView addGestureRecognizer:recognizer];
}
topView.abView.userInteractionEnabled=NO;
and implementing UIGestureRecognizerDelegate :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Bottom view was a navigation controller with number of segues and I had sort of a door on top of it that could close with pan gesture. Whole thing was embedded in yet another VC. Worked like a charm. Hope this helps.
Swift 4 Implementation for HitTest based solution
let hitView = super.hitTest(point, with: event)
if hitView == self { return nil }
return hitView
Derived from Stuart's excellent, and mostly foolproof answer, and Segev's useful implementation, here is a Swift 4 package that you can drop into any project:
extension UIColor {
static func colorOfPoint(point:CGPoint, in view: UIView) -> UIColor {
var pixel: [CUnsignedChar] = [0, 0, 0, 0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
context!.translateBy(x: -point.x, y: -point.y)
view.layer.render(in: context!)
let red: CGFloat = CGFloat(pixel[0]) / 255.0
let green: CGFloat = CGFloat(pixel[1]) / 255.0
let blue: CGFloat = CGFloat(pixel[2]) / 255.0
let alpha: CGFloat = CGFloat(pixel[3]) / 255.0
let color = UIColor(red:red, green: green, blue:blue, alpha:alpha)
return color
}
}
And then with hitTest:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard UIColor.colorOfPoint(point: point, in: self).cgColor.alpha > 0 else { return nil }
return super.hitTest(point, with: event)
}
I have quite a few controls scattered throughout many table cells in my table, and I was wondering if there's an easier way to dismiss the keyboard without having to loop through all my controls and resigning them all as the first responder. I guess the question is.. How would I get the current first responder to the keyboard?
Try:
[self.view endEditing:YES];
You can force the currently-editing view to resign its first responder status with [view endEditing:YES]. This hides the keyboard.
Unlike -[UIResponder resignFirstResponder], -[UIView endEditing:] will search through subviews to find the current first responder. So you can send it to your top-level view (e.g. self.view in a UIViewController) and it will do the right thing.
(This answer previously included a couple of other solutions, which also worked but were more complicated than is necessary. I've removed them to avoid confusion.)
You can send a nil targeted action to the application, it'll resign first responder at any time without having to worry about which view currently has first responder status.
Objective-C:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
Swift 3.0:
UIApplication.shared.sendAction(#selector(resignFirstResponder), to: nil, from: nil, for: nil)
Nil targeted actions are common on Mac OS X for menu commands, and here's a use for them on iOS.
To be honest, I'm not crazy about any of the solutions proposed here. I did find a nice way to use a TapGestureRecognizer that I think gets to the heart of your problem: When you click on anything besides the keyboard, dismiss the keyboard.
In viewDidLoad, register to receive keyboard notifications and create a UITapGestureRecognizer:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(keyboardWillShow:) name:
UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:#selector(keyboardWillHide:) name:
UIKeyboardWillHideNotification object:nil];
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(didTapAnywhere:)];
Add the keyboard show/hide responders. There you add and remove the TapGestureRecognizer to the UIView that should dismiss the keyboard when tapped. Note: You do not have to add it to all of the sub-views or controls.
-(void) keyboardWillShow:(NSNotification *) note {
[self.view addGestureRecognizer:tapRecognizer];
}
-(void) keyboardWillHide:(NSNotification *) note
{
[self.view removeGestureRecognizer:tapRecognizer];
}
The TapGestureRecognizer will call your function when it gets a tap and you can dismiss the keyboard like this:
-(void)didTapAnywhere: (UITapGestureRecognizer*) recognizer {
[textField resignFirstResponder];
}
The nice thing about this solution is that it only filters for Taps, not swipes. So if you have scrolling content above the keyboard, swipes will still scroll and leave the keyboard displayed. By removing the gesture recognizer after the keyboard is gone, future taps on your view get handled normally.
This is a solution to make the keyboard go away when hit return in any textfield, by adding code in one place (so don't have to add a handler for each textfield):
consider this scenario:
i have a viewcontroller with two textfields (username and password).
and the viewcontroller implements UITextFieldDelegate protocol
i do this in viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
username.delegate = self;
password.delegate = self;
}
and the viewcontroller implements the optional method as
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
and irrespective of the textfield you are in, as soon as i hit return in the keyboard, it gets dismissed!
In your case, the same would work as long as you set all the textfield's delegate to self and implement textFieldShouldReturn
A better approach is to have something "steal" first responder status.
Since UIApplication is a subclass of UIResponder, you could try:
[[UIApplication sharedApplication] becomeFirstResponder]
[[UIApplication sharedApplication] resignFirstResponder]
Failing that, create a new UITextField with a zero sized frame, add it to a view somewhere and do something similar (become followed by resign).
Tuck this away in some utility class.
+ (void)dismissKeyboard {
[self globalResignFirstResponder];
}
+ (void) globalResignFirstResponder {
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
for (UIView * view in [window subviews]){
[self globalResignFirstResponderRec:view];
}
}
+ (void) globalResignFirstResponderRec:(UIView*) view {
if ([view respondsToSelector:#selector(resignFirstResponder)]){
[view resignFirstResponder];
}
for (UIView * subview in [view subviews]){
[self globalResignFirstResponderRec:subview];
}
}
#Nicholas Riley & #Kendall Helmstetter Geln & #cannyboy:
Absolutely brilliant!
Thank you.
Considering your advice and the advice of others in this thread, this is what I've done:
What it looks like when used:
[[self appDelegate] dismissKeyboard]; (note: I added appDelegate as an addition to NSObject so I can use anywhere on anything)
What it looks like under the hood:
- (void)dismissKeyboard
{
UITextField *tempTextField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease];
tempTextField.enabled = NO;
[myRootViewController.view addSubview:tempTextField];
[tempTextField becomeFirstResponder];
[tempTextField resignFirstResponder];
[tempTextField removeFromSuperview];
}
EDIT
Amendment to my answer to included tempTextField.enabled = NO;. Disabling the text field will prevent UIKeyboardWillShowNotification and UIKeyboardWillHideNotification keyboard notifications from being sent should you rely on these notifications throughout your app.
Quick tip on how to dismiss the keyboard in iOS when a user touches anywhere on the screen outside of the UITextField or keyboard. Considering how much real estate the iOS keyboard can take up, it makes sense to have an easy and intuitive way for your users to dismiss the keyboard.
Here's a link
A lot of overly-complicated answers here, perhaps because this is not easy to find in the iOS documentation. JosephH had it right above:
[[view window] endEditing:YES];
Here's what I use in my code. It works like a charm!
In yourviewcontroller.h add:
#property (nonatomic) UITapGestureRecognizer *tapRecognizer;
Now in the .m file, add this to your ViewDidLoad function:
- (void)viewDidLoad {
//Keyboard stuff
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapAnywhere:)];
tapRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapRecognizer];
}
Also, add this function in the .m file:
- (void)handleSingleTap:(UITapGestureRecognizer *) sender
{
[self.view endEditing:YES];
}
Even Simpler than Meagar's answer
overwrite touchesBegan:withEvent:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[textField resignFirstResponder];`
}
This will dismiss the keyboardwhen you touch anywhere in the background.
You should send endEditing: to working window being the subclass of UIView
[[UIApplication sharedApplication].windows.firstObject endEditing:NO];
In your view controller's header file add <UITextFieldDelegate> to the definition of your controller's interface so that it conform to the UITextField delegate protocol...
#interface someViewController : UIViewController <UITextFieldDelegate>
... In the controller's implementation file (.m) add the following method, or the code inside it if you already have a viewDidLoad method ...
- (void)viewDidLoad
{
// Do any additional setup after loading the view, typically from a nib.
self.yourTextBox.delegate = self;
}
... Then, link yourTextBox to your actual text field
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField
{
if (theTextField == yourTextBox) {
[theTextField resignFirstResponder];
}
return YES;
}
The best way to dismiss keyboard from UITableView and UIScrollView are:
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag
In swift 3 you can do the following
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
Jeremy's answer wasn't quite working for me, I think because I had a navigation stack in a tab view with a modal dialog on top of it. I'm using the following right now and it is working for me, but your mileage may vary.
// dismiss keyboard (mostly macro)
[[UIApplication sharedApplication].delegate dismissKeyboard]; // call this in your to app dismiss the keybaord
// --- dismiss keyboard (in indexAppDelegate.h) (mostly macro)
- (void)dismissKeyboard;
// --- dismiss keyboard (in indexAppDelegate.m) (mostly macro)
// do this from anywhere to dismiss the keybard
- (void)dismissKeyboard { // from: http://stackoverflow.com/questions/741185/easy-way-to-dismiss-keyboard
UITextField *tempTextField = [[UITextField alloc] initWithFrame:CGRectZero];
UIViewController *myRootViewController = <#viewController#>; // for simple apps (INPUT: viewController is whatever your root controller is called. Probably is a way to determine this progragrammatically)
UIViewController *uivc;
if (myRootViewController.navigationController != nil) { // for when there is a nav stack
uivc = myRootViewController.navigationController;
} else {
uivc = myRootViewController;
}
if (uivc.modalViewController != nil) { // for when there is something modal
uivc = uivc.modalViewController;
}
[uivc.view addSubview:tempTextField];
[tempTextField becomeFirstResponder];
[tempTextField resignFirstResponder];
[tempTextField removeFromSuperview];
[tempTextField release];
}
You may also need to override UIViewController disablesAutomaticKeyboardDismissal to get this to work in some cases. This may have to be done on the UINavigationController if you have one.
Subclass your textfields... and also textviews
In the subclass put this code..
-(void)conformsToKeyboardDismissNotification{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissKeyBoard) name:KEYBOARD_DISMISS object:nil];
}
-(void)deConformsToKeyboardDismissNotification{
[[NSNotificationCenter defaultCenter] removeObserver:self name:KEYBOARD_DISMISS object:nil];
}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self resignFirstResponder];
}
In the textfield delegates (similarly for textview delegates)
-(void)textFieldDidBeginEditing:(JCPTextField *)textField{
[textField conformsToKeyboardDismissNotification];
}
- (void)textFieldDidEndEditing:(JCPTextField *)textField{
[textField deConformsToKeyboardDismissNotification];
}
All set.. Now just post the notification from anywhere in your code. It will resign any keyboard.
And in swift we can do
UIApplication.sharedApplication().sendAction("resignFirstResponder", to: nil, from: nil, forEvent: nil)
To dismiss a keyboard after the keyboard has popped up, there are 2 cases,
when the UITextField is inside a UIScrollView
when the UITextField is outside a UIScrollView
2.when the UITextField is outside a UIScrollView
override the method in your UIViewController subclass
you must also add delegate for all UITextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
In a scroll view, Tapping outside will not fire any event, so in that case use a Tap Gesture Recognizer,
Drag and drop a UITapGesture for the scroll view and create an IBAction for it.
to create a IBAction, press ctrl+ click the UITapGesture and drag it to the .h file of viewcontroller.
Here I have named tappedEvent as my action name
- (IBAction)tappedEvent:(id)sender {
[self.view endEditing:YES]; }
the abouve given Information was derived from the following link, please refer for more information or contact me if you dont understand the abouve data.
http://samwize.com/2014/03/27/dismiss-keyboard-when-tap-outside-a-uitextfield-slash-uitextview/
I hate that there's no "global" way to programmatically dismiss the keyboard without using private API calls. Frequently, I have the need to dismiss the keyboard programmatically without knowing what object is the first responder. I've resorted to inspecting the self using the Objective-C runtime API, enumerating through all of its properties, pulling out those which are of type UITextField, and sending them the resignFirstResponder message.
It shouldn't be this hard to do this...
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];
You can recursively iterate through subviews, store an array of all UITextFields, and then loop through them and resign them all.
Not really a great solution, especially if you have a lot of subviews, but for simple apps it should do the trick.
I solved this in a much more complicated, but much more performant way, but using a singleton/manager for the animation engine of my app, and any time a text field became the responder, I would assign assign it to a static which would get swept up (resigned) based on certain other events... its almost impossible for me to explain in a paragraph.
Be creative, it only took me 10 minutes to think through this for my app after I found this question.
A slightly more robust method I needed to use recently:
- (void) dismissKeyboard {
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *window in windows) [window endEditing:true];
// Or if you're only working with one UIWindow:
[[UIApplication sharedApplication].keyWindow endEditing:true];
}
I found some of the other "global" methods didn't work (for example, UIWebView & WKWebView refused to resign).
Add A Tap Gesture Recognizer to your view.And define it ibaction
your .m file will be like
- (IBAction)hideKeyboardGesture:(id)sender {
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *window in windows) [window endEditing:true];
[[UIApplication sharedApplication].keyWindow endEditing:true];
}
It's worked for me
Yes, endEditing is the best option. And From iOW 7.0, UIScrollView has a cool feature to dismiss the keyboard on interacting with the scroll view. For achieving this, you can set keyboardDismissMode property of UIScrollView.
Set the keyboard dismiss mode as:
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag
It has few other types. Have a look at this apple document.
In swift :
self.view.endEditing(true)
the easist way is to call the method
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(![txtfld resignFirstResponder])
{
[txtfld resignFirstResponder];
}
else
{
}
[super touchesBegan:touches withEvent:event];
}
You have to use one of these methods,
[self.view endEditing:YES];
or
[self.textField resignFirstResponder];