I'm using the shake api like this:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[img stopAnimating];
}
}
How do I detect that the shaking has stopped?
You are on the right track, however, there are still more things you need to add to detect shaking:
You can test this by adding an NSLog to the motionBegan or motionEnded methods, and in the Simulator, press CONTROL + COMMAND + Z
#pragma mark - Shake Functions
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:NO];
[self becomeFirstResponder];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:NO];
}
-(void)viewDidDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewDidDisappear:NO];
}
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake )
{
// shaking has began.
}
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake )
{
// shaking has ended
}
}
Related
I'm working on an application that has a window with a fully sized NSOpenGLView. I'm using [view addCursorRect] and [cursor set] to show a custom cursor, but when I press any key on the keyboard, the cursor resets to the default arrow. I've also tried overriding resetCursorRects and calling invalidateCursorRects when a key is pressed, which results in a flickering cursor.
The cursor switches back to my custom cursor when I click anywhere in the view, so I suppose the keyboard presses somehow unfocus my view. Is there any way to prevent the view from becoming unfocused when I press a key?
You need to remember your cursor settings + when you need to reset call e.g refreshCursor method. Example implementation:
- (void)refreshCursor
{
NSCursor *cursor = nil;
if ([self graphic]) {
cursor = [graphic hoverCursorForPoint:[self mousePosition]];
} else if ([self group]){
cursor = [NSCursor openHandCursor];
}
[self setHoverCursor:cursor];
[self refresh];
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
[self removeTrackingArea:[self trackingArea]];
NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:[self visibleRect]
options:NSTrackingActiveAlways|NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved
owner:self userInfo:nil];
[self setTrackingArea:area];
[self addTrackingArea:[self trackingArea]];
}
- (void)setCursorRects
{
[self discardCursorRects];
if ([self hoverCursor]) {
[self addCursorRect:[self visibleRect] cursor:[self hoverCursor]];
}
}
- (void)setHoverCursor:(NSCursor *)hoverCursor {
if (_hoverCursor != hoverCursor) {
_hoverCursor = hoverCursor;
[self setCursorRects];
}
}
- (void)resetCursorRects {
[super resetCursorRects];
[self setCursorRects];
}
- (void)mouseExited:(NSEvent *)event
{
[[NSCursor currentSystemCursor] set];
}
- (void)mouseEntered:(NSEvent *)event
{
}
- (void)mouseMoved:(NSEvent *)event
{
[self refreshCursor];
}
- (void)keyUp:(NSEvent *)event
{
[self refreshCursor];
}
//allow key events
- (BOOL)acceptsFirstResponder
{
return YES;
}
I am having some physics body nodes. Sprite Kit immediately calls didbegincontact method. I want that method should be called when i release the touch so that it may perform actions when click was released instead of calling method immediately causing some action setting problems for me.
- (void)didBeginContact:(SKPhysicsContact *)contact
{ NSLog(#"%hhd", _touching);
if(_touching == NO)
return;
something here
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_touching = YES;
NSLog(#"%hhd", _touching);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_touching = NO;
NSLog(#"%hhd", _touching);
something here
}
Set the global Variable, ie
BOOL _touching;
When you touch / release (touches ended and began) you set that var to YES / NO;
In the didbegincontact, use something like
if(_touching == YES) {
// what I want to happen when I am touching
}
else {
// i must not be touching so do this
}
Here is the basic set up - however I think its the game logic thats the issue, maybe think of a different way to solve your problem
#interface XBLMyScene()
#property (strong, nonatomic) SKNode *world;
#property (strong, nonatomic) SKSpriteNode *ball;
#property BOOL touching;
#end
#implementation XBLMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.world = [SKNode node];
[self addChild:self.world];
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(500, 0)];
self.ball = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(40, 40)];
self.ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(40, 40)];
self.ball.position = CGPointMake(200, 300);
[self.world addChild:self.ball];
self.touching = NO;
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.touching = YES;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.touching = NO;
}
- (void) didSimulatePhysics
{
if (self.touching) {
NSLog(#"I am touching");
}
else {
NSLog(#"I am not touching");
}
}
I am working on a magazine viewer and I have to use UIWebView for pages because of html5 interactive contents. First I tried uiwebviews in a UIScrollView but scrollview was too slow on sliding pages.
So now I am trying to write my own scrollview-like code. I have a MainView.xib, a view controller (Viewer) and extended UIWindow class (TouchCapturingWindow) that I take from here : http://wyldco.com/blog/2010/11/how-to-capture-touches-over-a-uiwebview/
when I try to slide pages fast, there is no problem but when I touch and slowly slide by not pulling my finger, "Viewer" view controller stops receiving touch events and so pages stop moving. I am logging TouchCapturingWindow, it still sending events. I searched many information and tutorials but I couldn't make it work. How can I make it continuously receive touch events?
I uploaded a simple Xcode project that contains only this part of my project. You can download here : http://testdergi.mysys.com/touchEvents.zip
When you run project, you should first download pages (180Kb) by tapping "Download Pages" button, then tap the "Open Viewer" button.
You can also look over the code below :
TouchCapturingWindow.h :
#interface TouchCapturingWindow : UIWindow {
NSMutableArray *views;
#private
UIView *touchView;
}
- (void)addViewForTouchPriority:(UIView*)view;
- (void)removeViewForTouchPriority:(UIView*)view;
#end
TouchCapturingWindow.m :
#implementation TouchCapturingWindow
- (void)dealloc {
}
- (void)addViewForTouchPriority:(UIView*)view {
if ( !views ) views = [[NSMutableArray alloc] init];
[views addObject:view];
}
- (void)removeViewForTouchPriority:(UIView*)view {
if ( !views ) return;
[views removeObject:view];
}
- (void)sendEvent:(UIEvent *)event {
//get a touch
UITouch *touch = [[event allTouches] anyObject];
//check which phase the touch is at, and process it
if (touch.phase == UITouchPhaseBegan) {
for ( UIView *view in views ) {
//if ( CGRectContainsPoint([view frame], [touch locationInView:[view superview]]) ) {
NSLog(#"TouchCapturingWindow --> TouchPhaseBegan");
touchView = view;
[touchView touchesBegan:[event allTouches] withEvent:event];
break;
}
}
else if (touch.phase == UITouchPhaseMoved) {
NSLog(#"TouchCapturingWindow --> TouchPhaseMoved");
if ( touchView ) {
[touchView touchesMoved:[event allTouches] withEvent:event];
}
else
{
NSLog(#"touch view is nil");
}
}
else if (touch.phase == UITouchPhaseCancelled) {
NSLog(#"TouchCapturingWindow --> TouchPhaseCancelled");
if ( touchView ) {
[touchView touchesCancelled:[event allTouches] withEvent:event];
touchView = nil;
}
}
else if (touch.phase == UITouchPhaseEnded) {
NSLog(#"TouchCapturingWindow --> TouchPhaseEnded");
if ( touchView ) {
[touchView touchesEnded:[event allTouches] withEvent:event];
touchView = nil;
}
}
//we need to send the message to the super for the
//text overlay to work (holding touch to show copy/paste)
[super sendEvent:event];
}
#end
Viewer.h :
#interface Viewer : UIViewController{
int currentPage;
int totalPages;
IBOutlet UIView *pagesView;
int lastTchX;
int difference;
BOOL hasMoved;
int touchBeganSeritX;
}
- (IBAction)backBtnClicked:(id)sender;
- (void)loadImages;
- (void)animationDidStop;
- (void)loadSinglePage:(int)pageNo;
#property (nonatomic, retain) IBOutlet UIView *pagesView;
#end
Viewer.m :
#interface Viewer ()
#end
#implementation Viewer
#synthesize pagesView;
- (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 from its nib.
}
- (IBAction)backBtnClicked:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
- (void)loadImages
{
for(UIView *subView in pagesView.subviews)
{
[subView removeFromSuperview];
}
currentPage = 1;
totalPages = 12;
for(int count = 1; count <= 12; count++)
{
[self loadSinglePage:count];
}
}
- (void)loadSinglePage:(int)pageNo
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES);
NSString *cachesDir = [paths objectAtIndex:0];
NSString *pagesDir = [NSString stringWithFormat:#"%#/pages", cachesDir];
int pageX = (pageNo - 1) * 768;
UIWebView *aPageWebView = [[UIWebView alloc] init];
[aPageWebView setFrame:CGRectMake(pageX, 0, 768, 1024)];
aPageWebView.backgroundColor = [UIColor clearColor];
aPageWebView.opaque = YES;
[aPageWebView setClearsContextBeforeDrawing:YES];
aPageWebView.clipsToBounds = NO;
[aPageWebView setScalesPageToFit:YES];
NSString *hamData = [NSString stringWithFormat:#"<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"user-scalable=yes, width=1024, height=1365, maximum-scale=1.0\"><style type=\"text/css\">body {margin:0; padding:0;}</style></head><body bgcolor=\"#508CCF\"><div id=\"touchable\" style=\"top:0px; left:0px; width:1024px; height:1365px; background-image:url(%#.jpg)\"></div></body></html>", [[NSNumber numberWithInt:pageNo] stringValue]];
[aPageWebView loadHTMLString:hamData baseURL:[NSURL fileURLWithPath:pagesDir isDirectory:YES]];
aPageWebView.scrollView.bounces = NO;
[aPageWebView.scrollView setMaximumZoomScale:1.3333f];
[aPageWebView.scrollView setMinimumZoomScale:1.0f];
aPageWebView.scrollView.zoomScale = 1.0f;
[aPageWebView setMultipleTouchEnabled:YES];
[pagesView addSubview:aPageWebView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesBegan");
UITouch *myTouch = [[event allTouches] anyObject];
int curTchX = [myTouch locationInView:self.view].x;
lastTchX = curTchX;
hasMoved = NO;
touchBeganSeritX = pagesView.frame.origin.x;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Viewer : .....moved");
hasMoved = YES;
UITouch *myTouch = [[event allTouches] anyObject];
int curTchX = [myTouch locationInView:self.view].x;
difference = curTchX - lastTchX;
int newX = (pagesView.frame.origin.x + difference);
if(newX <= 0)
{
[pagesView setFrame:CGRectMake((pagesView.frame.origin.x + difference), 0, pagesView.frame.size.width, 1024)];
}
lastTchX = curTchX;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesEnded");
if(hasMoved == YES)
{
hasMoved = NO;
int curSeritX = pagesView.frame.origin.x;
curSeritX = curSeritX / (-1);
int newX = 0;
if(difference < 0) //Sağa geçilecek
{
if((currentPage + 1) <= totalPages)
{
currentPage++;
}
}
else //Sola geçilecek
{
if((currentPage - 1) >= 1)
{
currentPage--;
}
}
newX = (currentPage - 1)*768*(-1);
[UIView animateWithDuration:0.2f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
// Do your animations here.
[pagesView setFrame:CGRectMake(newX, 0, pagesView.frame.size.width, pagesView.frame.size.height)];
}
completion:^(BOOL finished){
if (finished) {
// Do your method here after your animation.
}
}];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesCancelled");
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
I am answering my own question. I found something after a hard work. I changed "(void)sendEvent:(UIEvent *)event" method like the code below. I saw that use of "[super sendEvent:event];" after UITouchPhaseMoved phase is problematic.
- (void)sendEvent:(UIEvent *)event {
//we need to send the message to the super for the
//text overlay to work (holding touch to show copy/paste)
//[super sendEvent:event];
//get a touch
UITouch *touch = [[event allTouches] anyObject];
//check which phase the touch is at, and process it
if (touch.phase == UITouchPhaseBegan) {
for ( UIView *view in views ) {
//if ( CGRectContainsPoint([view frame], [touch locationInView:[view superview]]) ) {
NSLog(#"TouchCapturingWindow --> TouchPhaseBegan");
touchView = view;
[touchView touchesBegan:[event allTouches] withEvent:event];
break;
}
[super sendEvent:event];
}
else if (touch.phase == UITouchPhaseMoved) {
NSLog(#"TouchCapturingWindow --> TouchPhaseMoved");
if ( touchView ) {
[touchView touchesMoved:[event allTouches] withEvent:event];
int curTchX = [touch locationInView:self].x;
NSLog(#"curTchX : %d", curTchX);
}
else
{
NSLog(#"touch view is nil");
}
}
else if (touch.phase == UITouchPhaseCancelled) {
NSLog(#"TouchCapturingWindow --> TouchPhaseCancelled");
if ( touchView ) {
[touchView touchesCancelled:[event allTouches] withEvent:event];
touchView = nil;
}
[super sendEvent:event];
}
else if (touch.phase == UITouchPhaseEnded) {
NSLog(#"TouchCapturingWindow --> TouchPhaseEnded");
if ( touchView ) {
[touchView touchesEnded:[event allTouches] withEvent:event];
touchView = nil;
}
[super sendEvent:event];
}
//we need to send the message to the super for the
//text overlay to work (holding touch to show copy/paste)
}
But the new problem is; if I add a video in a web page, and try to forward the video with its slider, page is moving. I have to find a way to detect if user taps on a video.
I have subclassed UIButton to draw graphics, but the "addTarget" won't work.
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
I did not do anything with making adjustments to that method.
The Button graphics work ok on the "touches".
If I use a standard UIButton then the "addTarget" works fine.
Not sure what I am missing??
thx
#import <UIKit/UIKit.h>
#interface CButton : UIButton
{
CGContextRef c;
int myState;
}
#end
#import "CButton.h"
#implementation CButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
//NSLog(#"init state: %d", self.state);
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// myState = 1;
myState = !myState;
// NSLog(#"BeginState: %d", self.state);
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
// myState = 0;
// NSLog(#"EndState: %d", self.state);
// [self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
#end
You have to call super from your event handlers when you're all done handling events. How else will the button know of the touches to call your action?
e.g.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// myState = 1;
myState = !myState;
// NSLog(#"BeginState: %d", self.state);
[self setNeedsDisplay];
[super touchesBegan:touches withEvent:event]; // you need this
}
That being said, the comments on your original post are right, subclassing UIButton can get tricky. But it looks as if you have things under control (mostly). Good luck!
I'm trying to get shake gesture to work. Here is some code
In my implementation file
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(event.type == UIEventSubtypeMotionShake)
{
NSLog(#"Shake gesture detected");
}
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
I read that in order to make shake gesture to work UIView should be the firstResponder. This is why I added that code in implementation
if(self.view.isFirstResponder)
{
NSLog(#"first");
}
else
{
NSLog(#"no");
}
- (void)viewDidAppear {
[self becomeFirstResponder];
}
When I run the app the output of NSLog is NO. What I miss ? And how to get shake gesture to work
I guess you're doing this in your UIViewController? That's not correct … You have to subclass UIView like this:
ShakeView.h:
//
// ShakeView.h
//
#import <UIKit/UIKit.h>
#interface ShakeView : UIView {
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (BOOL)canBecomeFirstResponder;
#end
ShakeView.m:
//
// ShakeView.m
//
#import "ShakeView.h"
#implementation ShakeView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake ) {
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] ) {
[super motionEnded:motion withEvent:event];
}
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
#end
Then use ShakeView instead of your "normal" UIView and implement this in your UIViewController:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
NSLog(#"Shake happend …");
}
- (void)viewDidAppear:(BOOL)animated
{
[self.view becomeFirstResponder];
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self.view resignFirstResponder];
[super viewWillDisappear:animated];
}
That's it. Hope it helps :)