Can't get shake events on iPhone - iphone

I am not being able to get shake events on iPhone.
I have followed other questions here with no result. I also tried following the GLPaint example from Apple, but it seems exactly like my source code, with a small diference. GLPaint's source code /works/, mine /doesn't/.
So, here it is what I have:
Controller.m
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(shakeEnded) name:#"shake" object:nil];
}
ShakingEnabledWindow.m
- (void)shakeEnded {
NSLog(#"Shaking ended.");
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake ) {
// User was shaking the device. Post a notification named "shake".
[[NSNotificationCenter defaultCenter] postNotificationName:#"shake" object:self];
NSLog(#"Shaken!");
}
}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event {
}
My XIB has a window, which is a ShakingEnabledWindow and an object, my Controller.
I am running out of ideas here, hope someone can give me a hand. :)

Documentation of NSNotificationCenter says:
addObserver:selector:name:object:
notificationSelector Selector that
specifies the message the receiver
sends notificationObserver to notify
it of the notification posting. The
method specified by
notificationSelector must have one and
only one argument (an instance of
NSNotification).
So your shakeEnded method is wrong as it takes no parameters. It should look:
- (void)shakeEnded:(NSNotification*)notiication {
NSLog(#"Shaking ended.");
}
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(shakeEnded:) name:#"shake" object:nil];
}

In viewDidAppear, become the first responder:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
And make sure you can be first responder:
- (BOOL)canBecomeFirstResponder {
return YES;
}
Then you can implement the motion detection.
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventTypeMotion){
//there was motion
}
}

I think you are incorrectly checking the motion type. You need to check against event.subtype instead of motion:
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if ( event.subtype == UIEventSubtypeMotionShake ) {
// Put in code here to handle shake
}
}

Related

motionEnded not called in appDelegate

I want to integrate shake feature throughout the app. So I am doing everything in appDelegate. I need to push a viewController, I am able to push in motionBegan, but i wanted to do it motionEnded. yes motion ended does work in a view controller, but in app delegate it is not being called.
Doing as
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self becomeFirstResponder];
}
- (BOOL)canBecomeFirstResponder{
return YES;
}
motionEnded not called
-(void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if(event.subtype==UIEventSubtypeMotionShake){
NSLog(#"motionEnded called");
}
}
motionBegan called
-(void) motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if(event.subtype==UIEventSubtypeMotionShake){
NSLog(#"motionBegan called");
}
}
you could basically register your viewController for applicationDidBecomeActiveNotification or any depending on your needs
for example in your viewController's viewDidLoad method you could register it for notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMethod)
name:UIApplicationDidBecomeActiveNotification object:nil];
and implement this method in your class, your myMethod will call everytime your application will become active
-(void) myMethod(){
// do your stuff
}
finally un-register viewController from the notification in dealloc method
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

How to delay the shake notification

I want to delay the shake for 5 seconds because if the user continuously shakes the device, the response is showing null. So that is why I want to delay the shake until n unless response is alive.
Here's my code is
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
[FlurryAnalytics logEvent:#"User shaked to update"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CheckWeather" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"startWeatherNeue" object:nil];
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
}
in objective c i use sleep(6) for stop processing,
put sleep(6) befor your fired notification code:-
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
sleep(6);
[FlurryAnalytics logEvent:#"User shaked to update"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CheckWeather" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"startWeatherNeue" object:nil];
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
}
and then process stop for 6 second and then notification fire after 6 seconde may be its helpful for you
other
u also use NSTimer :-
in NSTimer u check your response array count >0 in if else condition and call method with 1 second when your response array > 0 then call NSNOtification method
here my example :- with NSTimer
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
self.TimeOfActiveUser = [NSTimer scheduledTimerWithTimeInterval:01.0 target:self selector:#selector(checkInfoString) userInfo:nil repeats:YES];
}
-(IBAction)checkInfoString
{
if([responsearray count]>0)
{
[FlurryAnalytics logEvent:#"User shaked to update"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CheckWeather" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"startWeatherNeue" object:nil];
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
{
[super motionEnded:motion withEvent:event];
}
}
else
{
NSLOG
}
}
To perform some selector later you can use:
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

How to intercept long press on UITextView without disabling context menu?

I want to intercept long press on UITextview, but don't want to disable the context menu option at the same time.
If I use gesture recognizer on textview, it will disable context menu so I am using the method like below now.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
//fire my method here
}
But, it only fires the method when context menu shows up after the user long press some words. So when the user long press a blank space, then only the magnifying glass shows up, I can't fire the method at the time.
Does anyone have better ideas? Thanks!
//////The Problem Solved//////
Thanks to #danh and #Beppe, I made it even with tap gesture on UITextView. I wanted to show the font bar on textview by long press.
#First, I subclassed the UITextview.
#interface LisgoTextView : UITextView {
BOOL pressing_;
}
#property (nonatomic) BOOL pressing;
#end
#interface LisgoTextView (private)
- (void)longPress:(UIEvent *)event;
#end
#implementation LisgoTextView
#synthesize pressing = pressing_;
//--------------------------------------------------------------//
#pragma mark -- Long Press Detection --
//--------------------------------------------------------------//
- (void)longPress:(UIEvent *)event {
if (pressing_) {
//post notification to show font edit bar
NSNotification *fontEditBarNotification = [NSNotification notificationWithName:#"fontEditBarNotification"
object:nil userInfo:nil];
[[NSNotificationCenter defaultCenter] postNotification:fontEditBarNotification];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self performSelector:#selector(longPress:) withObject:event afterDelay:0.7];
pressing_ = YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
pressing_ = NO;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
pressing_ = NO;
}
#I used the delay to solve the conflict with tap gesture I implemented on UITextView.
- (void)tapGestureOnTextView:(UITapGestureRecognizer *)sender {
//cancel here if long press was fired first
if (cancelTapGesture_) {
return;
}
//don't fire show font bar
cancelShowFontBar_ = YES;
[self performSelector:#selector(enableShowFontBar) withObject:nil afterDelay:1.0];
//method here
}
- (void)showFontEditBar {
//cancel here if tap gesture was fired first
if (cancelShowFontBar_) {
return;
}
if (fontEditBarExists_ == NO) {
//method here
//don't fire tap gesture
cancelTapGesture_ = YES;
[self performSelector:#selector(enableTapGesture) withObject:nil afterDelay:1.0];
}
}
- (void)enableTapGesture {
cancelTapGesture_ = NO;
}
- (void)enableShowFontBar {
cancelShowFontBar_ = NO;
}
I usually avoid subclassing unless the docs explicitly suggest, this worked for me. Long press and context menu. Whoops - Answer just loaded by #Beppe. Great minds think alike :-)
#interface TextViewSubclass ()
#property(assign,nonatomic) BOOL pressing;
#end
#implementation TextViewSubclass
#synthesize pressing=_pressing;
- (void)longPress:(UIEvent *)event {
NSLog(#"long press");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
self.pressing = YES;
[self performSelector:#selector(longPress:) withObject:event afterDelay:2.0];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
self.pressing = NO;
}
#end
This worked for me. I just wanted to control what happens when the user taps or long presses a link
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
// check for long press event
BOOL isLongPress = YES;
for (UIGestureRecognizer *recognizer in self.commentTextView.gestureRecognizers) {
if ([recognizer isKindOfClass:[UILongPressGestureRecognizer class]]){
if (recognizer.state == UIGestureRecognizerStateFailed) {
isLongPress = NO;
}
}
}
if (isLongPress) {
// user long pressed a link, do something
} else {
// user tapped on a link, do something
}
// return NO cause you dont want the normal behavior to occur
return NO;
}
Adaptation of Lucas Chwe's answer to work on iOS 9 (and 8). See comment in his answer.
The gist: invert the logic by starting with "no long press", then switch to "long press detected" if at least one UILongPressGestureRecognizer is in Began state.
BOOL isLongPress = NO;
for (UIGestureRecognizer *recognizer in textView.gestureRecognizers) {
if ([recognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
if (recognizer.state == UIGestureRecognizerStateBegan) {
isLongPress = YES;
}
}
}
Still a bit hackish, but working for iOS 8 and 9 now.
This is a little tricky, but it works for me.
I add a subclass of UIButton on top of my UITextView, check for long touches and pass them to UITextView this way:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
isLongTouchDetected = NO;
[self performSelector:#selector(longTouchDetected) withObject:nil afterDelay:1.0];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (isLongTouchDetected == YES) {
[super touchesCancelled:touches withEvent:event];
} else {
[super touchesEnded:touches withEvent:event];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(longTouchDetected) object:nil];
}
}
- (void)longTouchDetected {
isLongTouchDetected = YES;
// pass long touch to UITextView
}
For SWIFT [Easiest Way]
extension UITextField {
override public func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == "paste:" {
return false
}
return super.canPerformAction(action, withSender: sender)
}
override public func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer.isKindOfClass(UILongPressGestureRecognizer) {
gestureRecognizer.enabled = false
}
super.addGestureRecognizer(gestureRecognizer)
return
}
}
Above code has also function for disable "PASTE" option in content menu.
Could you use the textViewDidChangeSelection method of the UITextViewDelegate protocol?

Why is my application not detecting a shake gesture?

I'm trying to enable shake gestures within my application but I'm having a problem.
I have different view controllers, but on one of them (not the main view controller for the application) nothing happens in response to a shake gesture. What could be the problem?
The relevant code is as follows:
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
NSLog (#"SHAKED");
}
}
I think you should be checking motion, not event. Like this:
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake ) {
NSLog(#"SHOOK");
}
}

Shake Detection iPhone 3.0 not working

I have a ViewController that works perfectly with a button that trigger an action. I would like to replace the button with a shake event so I've googled it around and created a ShakeDetector class that ineherits from UIView
and my implementation is as follow:
#implementation ShakeDetector
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake )
{
// User was shaking the device. Post a notification named "shake".
//[[NSNotificationCenter defaultCenter] postNotificationName:#"spin" object:self];
NSLog(#"sss");
}
}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
#end
But I can't make it work... any help?
Thanks
Put :
-(BOOL)canBecomeFirstResponder
{
return YES;
}
and for your view :
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewDidDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewDidDisappear:animated];
}
You can also write it in viewWillAppear and viewWillDisappear