I am building a converter app. In the main screen I have a text field to input numbers and below the text field a picker view will allow users to select conversion parameters, (for example kg to g).
I can hide the keyboard when user click the background by using the following method
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.enterInput resignFirstResponder];
but when I touch the picker view the keyboard is not hiding.
My question is how to dismiss the keyboard when a user touches the picker view.
Got a solution
1) First Create a hidden roundRect botton and change the type to custom (fit the size of the picker).
2) Create a touch up inside action
- (IBAction)hiddenButtonToHideKeyboard:(id)sender {
[self.enterInput resignFirstResponder];
}
3) Create a keyboard appear notification
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(onKeyboardAppear:) name:UIKeyboardWillShowNotification object:nil];
4) Create a keyboard disappear notification
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(onKeyboardHide:) name:UIKeyboardWillHideNotification object:nil];
5) Make the button visible when keyboard is appeared
-(void)onKeyboardAppear:(NSNotification *)notification
{
hiddenButtonToHideKeyboard.hidden=NO;
}
6) Hide the button when the keyboard is disappeared
-(void)onKeyboardHide:(NSNotification *)notification
{
hiddenButtonToHideKeyboard.hidden=YES;
}
5) done
I dont think it is a perfect solution but it works for me :)
I use this in my code. I climb the responder chain and reassign the responder. I put this in my method that shows the pickerView. So far no unexpected issues. Seems to work if a keyboard was showing and seems not to crash if there wasn't a keyboard showing.
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
Here you go. UIPickerViews are a fairly complex system of nested UIViews which is why you weren't getting any response from the touchesBegan:withEvent: method. What you can do is create your UIPickerView subclass as follows:
//
// MyPickerView.h
//
#import <UIKit/UIKit.h>
// Protocol Definition that extends UIPickerViewDelegate and adds a method to indicate a touch
#protocol MyPickerViewDelegate <UIPickerViewDelegate>
// This is the method we'll call when we've received a touch. Our view controller should implement it and hide the keyboard
- (void)pickerViewDidReceiveTouch:(UIPickerView *)pickerView;
#end
#interface MyPickerView : UIPickerView
// We're redefining delegate to require conformity to the MyPickerViewDelegate protocol we just made
#property (nonatomic, weak) id <MyPickerViewDelegate>delegate;
#end
//
// MyPickerView.m
//
#import "MyPickerView.h"
#implementation MyPickerView
#synthesize delegate = _myPickerViewDelegate; // We changed the data type of delegate as it was declared in the superclass so it's important to link it to a differently named backing variable
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// We make sure to call the super method so all standard functionality is preserved
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView) {
// This will be true if the hit was inside of the picker
[_myPickerViewDelegate pickerViewDidReceiveTouch:self];
}
// Return our results, again as part of preserving our superclass functionality
return hitView;
}
#end
Then in your ViewController change it to conform to <MyPickerViewDelegate> instead of <UIPickerViewDelegate>. This is ok since MyPickerViewDelegate inherits from UIPickerViewDelegate and will pass through the standard UIPickerViewDelegate methods.
Finally implement pickerViewDidReceiveTouch: in your view controller:
- (void)pickerViewDidReceiveTouch:(UIPickerView *)pickerView {
[enterInput resignFirstResponder];
}
Related
Sorry for the basic Question, I'm fairly new to programming and trying to understand something in the code apple suggester for a certain solution to something I wanted to preform.
I created a simple notes app, very basic, currently I have:
1. CreateNote view controller
2. NotesList table view controller
So I wanted to add a behaviour when a note is being created and a user types below the keyboard so the text resized so the user can still see what he types and the text is not going behind the keyboard.
So I add some lines of code suggested by apple to accomplish that.
In the viewWillAppear called a method on NSNotificationCenter and I could not understand where is an NSNotificationCenter object is declared...?
So this is my CreateNote view controller(Please help me understand why they could preform this call):
#import "NMCreateNotesViewController.h"
#interface NMCreateNotesViewController () <UITextViewDelegate>
#property (weak, nonatomic) IBOutlet UIBarButtonItem *createButton;
#property (weak, nonatomic) IBOutlet UITextView *textField;
#end
#implementation NMCreateNotesViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// listen for keyboard hide/show notifications so we can properly adjust the table's height
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
#pragma mark - Notifications
- (void)adjustViewForKeyboardReveal:(BOOL)showKeyboard notificationInfo:(NSDictionary *)notificationInfo
{
// the keyboard is showing so resize the table's height
CGRect keyboardRect = [[notificationInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSTimeInterval animationDuration =
[[notificationInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect frame = self.textField.frame;
// the keyboard rect's width and height are reversed in landscape
NSInteger adjustDelta = UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? CGRectGetHeight(keyboardRect) : CGRectGetWidth(keyboardRect);
if (showKeyboard)
frame.size.height -= adjustDelta;
else
frame.size.height += adjustDelta;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.textField.frame = frame;
[UIView commitAnimations];
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
[self adjustViewForKeyboardReveal:YES notificationInfo:[aNotification userInfo]];
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
[self adjustViewForKeyboardReveal:NO notificationInfo:[aNotification userInfo]];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.createButton) return;
if (self.textField.text.length > 0) {
self.note = [[NMNote alloc] init];
self.note.content = self.textField.text;
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Is NSNotificationCenter is part of UITextView?
No it is not. NSNotificationCenter is - as it's name says - a notification center. Objects can subscribe to notifications and post notifications with NSNotificationCenter to handle and notify of certain events.
They are using NSNotificationCenter to have the viewcontroller subscribe to UIKeyboardWillHideNotification and UIKeyboardWillShowNotification events.
Take a look at this one:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
NSNotificationCenter is designed to be used as a singleton ( I believe this is the correct term, correct me if I'm wrong ) so we access the NSNotificationCenter for this app's process by calling the class method defaultCenter. it adds observer 'self' ( which in this case is an instance of the view controller) and basically instructs it to send the message keyboardWillShow to the observer when a Notification under the name of UIKeyboardWillShowNotification is fired.
What object fires the UIKeyboardWillShowNotification? Well it's not a UITextView, this notification name is actually defined in UIWindow.h so it probably came from there, which in turn probably was invoked from UIKeyboard which is not a public API as far as I know.
NSNotificationCenter is a class. In Objective-C, classes are declared in header files; this one is in NSNotification.h. (Try pressing Command-Shift-O and typing "NSNotificationCenter" to find this yourself.) When you want to use a class, you #import the header file that the class is declared in; this makes the compiler read that header file first and make all the classes (and other globals) available to your code to use.
This would be a huge pain, though, since a typical Cocoa app uses zillions of classes and other globals from Apple's libraries. So, instead, you just need to #import <Foundation/Foundation.h> which is a header file that just includes a bunch of other header files, including NSNotification.h. (The import statement for Foundation is probably in your own header file, or something else like #import <UIKit/UIKit.h> which also will include foundation and ultimately NSNotification.h.)
One final detail is that there's is probably a "prefix" header in your project which includes UIKit.h in all of your files automatically, so anything declared in there is always available to your code.
NSNotificationCenter is a class in Foundation.
NSNotificationCenter doesn't need to be declared and stored in a variable, its just a call, to explain it further, think of NSNotificationCenter as a tackboard where things get posted, you add a note to that backboard by creating the NSNotificationCenter, and you assingn observers to look at that board, and do something when a note is added.
The NSNotificationCenter object being used in this case is a Singleton. What you need to know is that sending the message defaultCenter to the NSNotificationCenter class object always returns the same NSNotificationCenter object.
Here's what the call to default center might look like
+ (NSNotificationCenter*)defaultCenter
{
static dispatch_once_t once;
static NSNotificationCenter* sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
/*
set up properties, etc
*/
});
return sharedInstance;
}
I am setting up a NOTIFICATION to get KEYBOARD information to adjust the view so that the selected text field is not covered by the KEYBOARD.
#property CGSize keyboard;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:#"UIKeyboardWillShowNotification"
object:nil];
...
}
- (void) keyboardWillShow:(NSNotification *)note
{
NSDictionary *userInfo = [note userInfo];
self.keyboard = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
}
I have set up the UITextFieldDelegate so I can use the KEYBOARD information in
- (void)textFieldDidBeginEditing:(UITextField *)textField
Everything works perfectly ...
except for the very first time.
The NOTIFICATION that sets up the KEYBOARD property is called after textFieldDidBeginEditing so the very first time the KEYBOARD property has not been set up so it displays incorrectly.
After that things are fine since the KEYBOARD property has already been set up and the values don't change.
How can I get the KEYBOARD information before the first execution of textFieldDidBeginEditing?
EDIT:
Did find something of a solution, and it does seem to work, but it feels a little hackish to me and I'm still wondering if there is a way to get the keyboard information.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.txtField becomeFirstResponder];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.txtField resignFirstResponder];
}
Does give a brief flash at the very bottom of the screen as it displays and hides the keyboard, which I don't like, but it's probably not very obvious or noticeable to someone that doesn't know to look for it.
I have to break up the becomeFirstResponder and resignFirstResponder into different methods because if they get called from the same method then keyboardWillShow does not get called.
Also, can not place becomeFirstResponder in viewDidLoad because keyboardWillShow is not called in that situation either.
If anyone has an improvement, or a better way, I'd love to hear it.
One way you can do this is that instead of setting the text field's frame in the begin editing, you can iterate through your text fields checking the isFirstResponderproperty, and move the frame of the one that is first responder. You can do all that in your method where you get the keyboards frame.
I used to have my UIView that worked fine with several delegates some of them below. Now I changed that UIView from IB to be a UIScrollView (Now used as main view).
Since I've changed to UIScrollView my event delegates such as those below dont work anymore. Such as keyboard and also I had an element that I could move around and not it is just static.
I assigned all delegates form the IB that I could think of and did most things that I know. but i am running out of ideas on why the events are not getting triggered....
if i go back to the old UIView by doing cmd + z they work.
Could anyone point me in the right direction??
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
[self.view endEditing:TRUE];
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
// register for keyboard notifications
return YES;
}
EDIT - Complementary answer:
#Wezly Answer is totally valid.
But if anyone doesn't want to subclass the UIScrollView and use only UITextFieldDelegate methods.
another way of doing it is adding to viewDidLoad:
Note: you still cant access many things, but its another workaround
///
/// DelegateNotifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.view.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldDidEndEditing:)
name:UITextFieldTextDidEndEditingNotification
object:self.view.window];
I suggest that you subclass your UIScrollView object and add your touch events inside of it like below..
canvasObject.h
#import <UIKit/UIKit.h>
#interface canvasObject : UIScrollView
#end
canvasObject.m
#import "canvasObject.h"
#implementation canvasObject
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.canCancelContentTouches = false;
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
//Do Stuff
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
return YES;
}
#end
Then link your UIScrollView using the identity inspector in interface builder to the new scroll view subclass like below..
I have UITextView which shows some text data from CoreData entity.
I need the possibility to edit this text data and save changes after keyboard dismissed.
How to do that?
I know how to save data but I don't know how to dismiss keyboard if I tap outside UITextView. May be there is some native way to do that.
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if ([appDelegate.managedObjectContext save:&error]) {
...
}
Thanks for any help or advice!
First you need to set the delegate for UITextview to current view.We have native code for when we click on return button on keyboard we will get the event using UIKit.Framework, Inside that function write your save or other logics.
Code:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
// ------- here is your logics -------- //
}
textField => Your text field object.
To save data after you stop editing UITextView and have dismissed the keyboard,
use TextView's Delegate method
(void)textViewDidEndEditing:(UITextView *)textView
In this method, save the new text in the Entity's appropriate property and save the managed object context to persist the data.
To dismiss to dismiss keyboard if you tap outside UITextView
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[TextView resignFirstResponder];
}
To dismiss the keyboard you can use.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.view endEditing:YES];
}
I don't care about which key was pressed, or how long, or anything like that. All I need is a way to detect that the user has touched the screen, even if that happens to be on the part of the screen covered by a keyboard.
My goal here is to detect a lack of interaction to 'reset' the app to a default state if left along long enough -- think a 'kiosk mode' application. The issue is that I can't detect when the keyboard is touched, as the keyboard apparently intercepts all touch events, even before my customer window can handle them.
Edit:
Considered (and dismissed) is the use of just keyboard show and hide notifications -- we need to prolong screen display if the user is actively typing. Since we use UIWebViews to display certain things, we also can't use the delegate methods of UITextViews or UITextFields.
It sounds like detecting all touches—on the keyboard and elsewhere—would suffice. We can do that by subclassing UIApplication to override sendEvent:.
We'll extend the UIApplicationDelegate protocol with a new message, application:willSendTouchEvent:, and we'll make our UIApplication subclass send the message to its delegate before handling any touch event.
MyApplication.h
#interface MyApplication : UIApplication
#end
#protocol MyApplicationDelegate <UIApplicationDelegate>
- (void)application:(MyApplication *)application willSendTouchEvent:(UIEvent *)event;
#end
MyApplication.m
#implementation MyApplication
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeTouches) {
id<MyApplicationDelegate> delegate = (id<MyApplicationDelegate>)self.delegate;
[delegate application:self willSendTouchEvent:event];
}
[super sendEvent:event];
}
#end
We'll need to make our app delegate conform to the MyApplicationDelegate protocol:
AppDelegate.h
#import "MyApplication.h"
#interface AppDelegate : UIResponder <MyApplicationDelegate>
// ...
AppDelegate.m
#implementation AppDelegate
- (void)application:(MyApplication *)application willSendTouchEvent:(UIEvent *)event {
NSLog(#"touch event: %#", event);
// Reset your idle timer here.
}
Finally, we need to make the app use our new MyApplication class:
main.m
#import "AppDelegate.h"
#import "MyApplication.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv,
NSStringFromClass([MyApplication class]),
NSStringFromClass([AppDelegate class]));
}
}
How about using the keyboardShow and keyBoardHide Notifications to set a timer? After x seconds you return the app to your required state.
Could even reset the timer in scrollview delegates or textField delegates if you want to.
Take a look at:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
And a method like this:
- (void)keyboardDidShow:(NSNotification *)note {
idleTimer = [NSTimer scheduledTimerWithTimeInterval:120
target:nil
selector:#selector(yourMethodHere)
userInfo:nil
repeats:NO];
}
Be sure to declare and #synthesize your timer in the .h and .m file.
Hope it helps.
UITextField or UITextView has a delegate method to detect when user types something:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// user tapped a key
//
// reset your "idle" time variable to say user has interacted with your app
}
Remember to conform to the UITextField or UITextView protocol depending which one you use (maybe both if you've got both text field and text views). Also remember to mark each of your text field or text view's delegate as your view controller.
<UITextFieldDelegate,UITextViewDelegate>
Updated Answer
Ron, not sure if you actually googled but I found this:
iPhone: Detecting user inactivity/idle time since last screen touch