I'm trying to make a shake events.
I tried:
1) How do I detect when someone shakes an iPhone? (posts of Kendall, and Eran)
2) motionBegan: Not Working
but nothig helps.
My View becomes first responder, but motionBegan/motionEnded never called.
Is there some additiol settings must be done, or i'm missing somethig? My iOS SDK is 4.3.
I have a class of UIView:
#import "ShakeView.h"
#implementation ShakeView
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog (#"123");
if ( event.subtype == UIEventSubtypeMotionShake ) {
NSLog(#"Shake!");
}
if ([super respondsToSelector:#selector(motionEnded:withEvent:)]) {
[super motionEnded:motion withEvent:event];
}
}
#end
In my ViewController's xib class of View is ShakeView.
my ViewController pushed:
Wheel *secondViewController = [[Wheel alloc] initWithNibName:#"Wheel" bundle:nil];
[self.navigationController pushViewController:secondViewController animated:YES];
[secondViewController release];
In my ViewController:
- (void) viewDidAppear:(BOOL)animated
{
[self.view becomeFirstResponder];
[super viewWillAppear:animated];
NSLog(#"%d", [self.view isFirstResponder]);
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.view resignFirstResponder];
[super viewWillDisappear:animated];
}
It logs "1", so it IS first responder. But it logs nothing else.
I spend a half day on this few lines of code, and I have no more ideas. Do anyone knows how to solve it?
Thanks.
This is much too late to help SentineL, but I was having the same problem and I like his question because it is clear that he has all the relevant code in place -- except one crucial line, in the application delegate's didFinishLaunching:
[self.window makeKeyAndVisible];
This is very hard to debug, because even without this line, everything else will be fine. Your gestures will work, your controls will respond, you will be able to make your view first responder (as SentineL checked) -- but your subclassed window or view or view controller will never receive the motion events.
Which doesn't make sense to me. Why would makeKeyAndVisible affect the accelerometer but not gestures? Hopefully some more experienced user can answer that.
P.S. If you use this code as an example, I would recommend that you omit the super respondsToSelector conditional. Of course it responds to the selector; you're overriding it.
Related
i have a screen having navigation controller and text field. when i move next and come back i want the keyboard should be hidden in first screen. I am hiding keyboard like on textfield event.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
But how to do that in View related events so that whenever my view appears keyboard is hidden..
Pls guide/Help.
thanks in adv.
I think this is also a good way to remove keyboard with in iOS App if your UITextView or UITextField not connected through the IBOutlet.
If you want to Hide Keyboard with UIViewController LifeCycle Events like with viewWillAppear or etc. Follow this
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[self view] endEditing:YES];
}
Otherwise if you object connected using IBOutLet this code will work fine as you describe too.
[yourTextField resignFirstResponder];
Add this code to your ViewWillAppear :
for(id obj in self.view.subviews)
{
if([obj isKindOfClass:[UITextField class]])
{
[obj resignFirstResponder];
}
}
This would take in all the textfields in that particular view here it is the whole view and add the code you had written previously for removing the keyboard.
A good habit is to write this code in your screen's -viewWillDisappear. So, when you navigate from one screen to another at that time it will remove the keyboard from that screen.
- (void)viewWillDisappear:(BOOL)animated
{
[self.view endEditing:YES];
[super viewWillDisappear:animated];
}
For multiple textFields, it is better to use -endEditing for that particular view instead of -resignFirstResponder for any single textField. Take a look at my Answer.
//This is for Swift
override func viewWillAppear(animated: Bool)
{
self.view.endEditing(true)
}
The thing that you are doing wrong is , when you are moving back previous controller to the current controller , the keyboard is up due to the selected textfield of previous controller .
And in the current controller the code:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[self view] endEditing:YES];
}
It will not work as no textfield is selected at this controller. So what you need to do is write the same code in the previous controller viewWillDisappear Method it will surely resolve your Problem .
- (void)viewWillDisappear:(BOOL)animated
{
[self.view endEditing:YES];
[super viewWillDisappear:animated];
}
Background: App has a shake to go home feature. Home view Only supports portrait.
If you shake a bit harder than usual, the view that you are on starts to rotate (which is fine) , but then it detects a shake and does a popViewControlller to the home view. When it does this it loads the navigation controller just fine, but the view under (the home content) gets loaded behind the bar and is stretched up (it's basically loading underneath the navigation bar, so it gets stretched up)
The back button handles this just fine from landscape to portrait (since its not mid transitions)
How should I go about handling this orientation change (from the shake) so I can pop back into the root view controller, without the view loading under the navigation bar?
Edit:What's happening is the content thinks that it has the entire view to load, so it stretches itself to take the entire screen, not realizing theres a navigationbar above it. I can tell since the images loading are stretched out
added a bounty of 50.
Edit Here's How I'm detecting Shakes and Popping
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
UINavigationController *navController = self.navigationController;
[[self retain] autorelease];
HomeViewController *home = [[HomeViewController alloc]init];
[navController popViewControllerAnimated:YES];
home.title =#"Home View Controller";
[home release];
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
Here's my App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
navController = [[UINavigationController alloc]init];
[self.window addSubview:navController.view];
HomeViewController *home = [[HomeViewController alloc]init];
[[self home] setFrame:[[UIScreen mainScreen] applicationFrame]];
I'll include a mockup here.
Normal View:
Stretched View After a Shake/Pop:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
I'm a bit puzzled by your code so I'd really suggest starting from the beginning. As Lukya mentioned, there's no reason to recreate the HomeViewController. I'm also baffled by the "[[self retain] autorelease];" bit. That shouldn't be necessary unless you're doing something incorrectly elsewhere.
So I would start with this... In application:didFinishLaunchingWithOptions: do something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
HomeViewController *home = [[[HomeViewController alloc] init] autorelease];
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:home] autorelease];
[self.window addSubview:navController.view];
}
The window will retain a your nav controller and the nav controller will retain your HomeViewController.
Then in motionEnded:withEvent: do something like:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
That should be it.
If that does not work then can you give any other info? For example, does HomeViewController implement (and return YES) in shouldAutorotateToInterfaceOrientation:? If so, can you return no so it doesn't rotate since your first line says "Home view Only supports portrait"?
Edit: An example of willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation: as well.
In the header for whatever controller you're detecting shakes in add a boolean:
BOOL isRotating;
In your implementation file add the two UIViewController methods we want to override -- something like:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
isRotating = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
isRotating = NO;
}
Now, do something like this for your event handler:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake && !isRotating)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
in your home view controller's xib, go to the inspector for the view and set top bar as navigation bar.. and in view did load set self.navigationBarHidden = NO;...
NOTE:
there are many thing wrong with the code you've posted.. but none of them causes the orientation problem... in fact this seems to be the only code you need in that method:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[navController popViewControllerAnimated:YES];
}
}
so you might want to change this code as well..
Have you tried calling [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:YES]; in your home view controller? You could also try to place this in where you detect a shake.
I have come across this issue with underlapping the navigation bar. I am not sure what causes it but you can work around it by calling,
[[self loadingView] setFrame:[[UIScreen mainScreen] applicationFrame]];
after the problem view is added to window in the application delegate.
I need to hide something on the screen when the user has activates the application by switching it to the foreground.
I have tried inserting my code within applicationDidBecomeActive or applicationWillEnterForeground and although it runs OK the old screen with the text I want to hide is displayed momentarily.
How can I hide the field before the screen is redrawn?
Thanks
iphaaw
I think the problem is, iOS will capture a screenshot from your app in the moment it goes to the background, so the animation will work in an instant.
The only way in my opinion to do this is to hide / cover your view in moment the app goes to the background.
Write some code in applicationWillResignActive: to 'hide' whatever you need to hide.
I faced a similar situation but, instead of hiding, I wanted to show a block code screen to grant access. Anyway I think that the solution also applies to your needs.
I often implement a custom base view controller in my iOS applications. So instead of dealing with applicationDidBecomeActive: or applicationWillResignActive: I setup this view controller to listen for the equivalent notifications:
#interface BaseViewController : UIViewController
- (void)prepareForGrantingAccessWithNotification:(NSNotification *)notification;
- (void)grantAccessWithNotification:(NSNotification *)notification;
#end
#implementation BaseViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self addNotificationHandler:#selector(grantAccessWithNotification:)
forNotification:UIApplicationDidBecomeActiveNotification];
[self addNotificationHandler:#selector(prepareForGrantingAccessWithNotification:)
forNotification:UIApplicationWillResignActiveNotification];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)prepareForGrantingAccessWithNotification:(NSNotification *)notification {
// Hide your views here
myCustomView.alpha = 0;
// Or in my case, hide everything on the screen
self.view.alpha = 0;
self.navigationController.navigationBar.alpha = 0;
}
- (void)grantAccessWithNotification:(NSNotification *)notification {
// This is only necessary in my case
[self presentBlockCodeScreen];
self.view.alpha = 1;
self.navigationController.navigationBar.alpha = 1;
...
}
#end
How do I use shake-events in iOS 4.0+? I tested this solution but isn't working in 4.1. Is it possible that something changed or am I doing someting wrong? motionBegan: Not Working
From this SO question How do I detect when someone shakes an iPhone?
The main trick is that you need to have some UIView (not view controller) that you want as firstResponder to receive the shake event messages. Here's the code that you can use in any UIView to get shake events:
#implementation ShakingView
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
// Put in code here to handle shake
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
- (BOOL)canBecomeFirstResponder
{ return YES; }
#end
You can easily transform any UIView (even system views) into a view that can get the shake event simply by subclassing the view with only these methods (and then selecting this new type instead of the base type in IB, or using it when allocating a view).
In the view controller, you want to set this view to become first responder:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
Don't forget that if you have other views that become first responder from user actions (like a search bar or text entry field) you'll also need to restore the shaking view first responder status when the other view resigns!
This method works even if you set applicationSupportsShakeToEdit to NO.
I have this inside my viewController:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventSubtypeMotionShake) {
NSLog(#"I have shaked");
}
}
Why is this not working?
Edit:
I do infact have this:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
If the UIViewController is loaded at the very start of the application, I've seen an odd glitch in OS 3.0 where it would not become the first responder unless you delayed the appropriate message a bit. Try placing
[self performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.3];
within -loadView or something else that is called when the controller is first set up.
There may be a more elegant way to work around this, but this approach has worked for me.
The viewController must be the first responder during the shake to receive this event.
This could be one reason it's not working.