Ok, i've read a few posts on this one (ex. UIImageView Gestures (Zoom, Rotate) Question) but I can't seem to fix my problem.
I have the following setup: an SKScene, an SKNode _backgroundLayer and 9 SKSpriteNodes that are tiles that make up the background and are attached to the _backgroundLayer.
Since these 9 tiles make a 3x3 square and they are quite large, I need to be able to zoom in and look at other SKSpriteNodes that will be on top of these 9 background images.
There are two problems:
1) When I pinch to zoom in or zoom out it seems like it is zooming in/out from location (0,0) of the _backgroundLayer and not from the touch location.
2) I have added some bounds so that the user can not scroll out of the 9 background images. In general it works. However, if I zoom in then move towards the top of the 9 background images and then zoom out the bounding conditions go berserk and the user can see the black space outside the background images. I need a way to limit the amount of zooming out that the user can do depending on where he's at.
Any ideas? Thanks!
I include my code below:
#import "LevelSelectScene.h"
#import "TurtleWorldSubScene.h"
#interface LevelSelectScene ()
#property (nonatomic, strong) SKNode *selectedNode;
#end
#implementation LevelSelectScene
{
SKNode *_backgroundLayer;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
_backgroundLayer = [SKNode node];
_backgroundLayer.name = #"backgroundLayer";
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[_backgroundLayer setScale:0.76];
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
} else {
[_backgroundLayer setScale:0.36];
}
[self addChild:_backgroundLayer];
SKTexture *backgroundTexture = [SKTexture textureWithImageNamed:#"levelSelect"];
int textureID = 0;
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
SKSpriteNode *background = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
background.anchorPoint = CGPointZero;
background.position = CGPointMake((background.size.width)*i, (background.size.height)*j);
background.zPosition = 0;
background.name = [NSString stringWithFormat:#"background%d", textureID];
textureID++;
[_backgroundLayer addChild:background];
}
}
[TurtleWorldSubScene displayTurtleWorld:self];
}
return self;
}
- (void)didMoveToView:(SKView *)view {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanFrom:)];
[[self view] addGestureRecognizer:panGestureRecognizer];
//UITapGestureRecognizer * tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
// [self.view addGestureRecognizer:tapRecognizer];
UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinch:)];
[[self view] addGestureRecognizer:pinchGestureRecognizer];
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
SKNode *node = [self nodeAtPoint:touchLocation];
_selectedNode = node;
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(translation.x, -translation.y);
CGPoint initialPosition = CGPointAdd(_backgroundLayer.position, translation);
_backgroundLayer.position = [self boundLayerPos:initialPosition];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
float scrollDuration = 0.2;
CGPoint velocity = [recognizer velocityInView:recognizer.view];
CGPoint pos = [_backgroundLayer position];
CGPoint p = CGPointMultiplyScalar(velocity, scrollDuration);
CGPoint newPos = CGPointMake(pos.x + p.x, pos.y - p.y);
newPos = [self boundLayerPos:newPos];
[_backgroundLayer removeAllActions];
SKAction *moveTo = [SKAction moveTo:newPos duration:scrollDuration];
[moveTo setTimingMode:SKActionTimingEaseOut];
[_backgroundLayer runAction:moveTo];
}
}
- (void)handlePinch:(UIPinchGestureRecognizer *) recognizer
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if(_backgroundLayer.xScale*recognizer.scale < 0.76) {
//SKSpriteNode *backgroundTile = (SKSpriteNode *)[_backgroundLayer childNodeWithName:#"background0"];
[_backgroundLayer setScale:0.76];
} else if(_backgroundLayer.xScale*recognizer.scale > 2) {
[_backgroundLayer setScale:2.0];
} else {
[_backgroundLayer runAction:[SKAction scaleBy:recognizer.scale duration:0]];
}
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
} else {
if(_backgroundLayer.xScale*recognizer.scale < 0.36) {
[_backgroundLayer setScale:0.36];
} else if(_backgroundLayer.xScale*recognizer.scale > 2) {
[_backgroundLayer setScale:2.0];
} else {
[_backgroundLayer runAction:[SKAction scaleBy:recognizer.scale duration:0]];
}
}
recognizer.scale = 1;
}
- (CGPoint)boundLayerPos:(CGPoint)newPos {
SKSpriteNode *backgroundTile = (SKSpriteNode *)[_backgroundLayer childNodeWithName:#"background0"];
CGPoint retval = newPos;
retval.x = MIN(retval.x, 0);
retval.x = MAX(retval.x, -(backgroundTile.size.width*_backgroundLayer.xScale*3)+self.size.width);
retval.y = MIN(retval.y, 0);
retval.y = MAX(retval.y, -(backgroundTile.size.height*_backgroundLayer.xScale*3)+self.size.height);
return retval;
}
Related
I am trying to make a table view cell that shows ratings for songs in a playlist. I have successfully created the cell so that it shows the current number of stars, and also an indication of how a new setting will be when you hover your mouse cursor over a cell to give a new rating.
The problem is that while mouseEnter, mouseExit and mouseMove works like a charm, I get no messages for mouseDown, which is required to actually change the value of the cell.
I have searched all over the Internet, but I can't find any solution to how to solve this problem anywhere. I have spent so many hours trying to sort this. I hope anyone have any answer or hint what I can do. Thank you.
The full code for the current implementation is as follows:
#import "FavouriteCellView.h"
#implementation FavouriteCellView {
NSTrackingArea *_trackingArea;
int _starsRated; //The current rating value
BOOL _hovering; //YES if the mouse is currently hovering over this cell
int _starsHovering; //The number of stars hovering, if the mouse is hovering over this cell
}
- (void)awakeFromNib {
[super awakeFromNib];
_starsRated = 1;
_hovering = NO;
_starsHovering = 0;
[self createTrackingArea];
}
- (void)createTrackingArea
{
_trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingMouseEnteredAndExited |NSTrackingActiveInActiveApp | NSTrackingMouseMoved owner:self userInfo:nil];
[self addTrackingArea:_trackingArea];
}
- (void)updateTrackingAreas{
[self removeTrackingArea:_trackingArea];
_trackingArea = nil;
[self createTrackingArea];
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// CGFloat middleX = [self bounds].size.width / 2.0f;
CGFloat middleY = [self bounds].size.height / 2.0f;
CGFloat starDivs = [self bounds].size.width / 5.0f;;
NSColor *starSelectedColor = [NSColor colorWithDeviceRed:0.8f green:0.0f blue:0.4f alpha:1.0f];
NSColor *starUnselectedColor = [NSColor colorWithDeviceRed:0.5f green:0.5f blue:0.5f alpha:1.0f];
NSColor *starHoverColor = [NSColor colorWithDeviceRed:1.0f green:0.843f blue:0.0f alpha:1.0f];
NSColor *starHoverColorSelected = [NSColor colorWithDeviceRed:0.9f green:0.843f blue:0.6f alpha:1.0f];
for (int i = 0; i < 5; i++) {
NSColor *useColor = [NSColor redColor];
if (_hovering && (i <= _starsHovering)) {
if (i <= _starsRated) {
useColor = starHoverColorSelected;
} else {
useColor = starHoverColor;
}
} else if (i <= _starsRated) {
useColor = starSelectedColor;
} else {
useColor = starUnselectedColor;
}
[self star:NSMakePoint((starDivs / 2.0f) + starDivs * i, middleY) color:useColor];
}
}
-(void)star:(NSPoint)center color:(NSColor *)color {
[color set];
CGFloat t = (2.0f * M_PI) / 10.0f;
NSBezierPath *path = [[NSBezierPath alloc] init];
CGFloat radii1 = 12.0f;
CGFloat radii2 = 4.0f;
CGFloat rot = M_PI / 2.0f;
BOOL first = YES;
for (int i = 0; i < 10; i++) {
CGFloat pointX = cos(t * i + rot) * radii1 + center.x;
CGFloat pointY = sin(t * i + rot) * radii1 + center.y;
CGFloat tempRadii = radii1;
radii1 = radii2;
radii2 = tempRadii;
if (first) {
first = NO;
[path moveToPoint:NSMakePoint(pointX, pointY)];
}
else {
[path lineToPoint:NSMakePoint(pointX, pointY)];
}
}
[path closePath];
[path fill];
/*
[[NSColor blackColor] set];
[path setLineWidth:0.25f];
[path stroke];
*/
}
-(NSView *)hitTest:(NSPoint)aPoint {
//THIS GETS CALLED
return self;
}
-(BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event {
printf("$"); //DOES NOT GET CALLED
return YES;
}
-(BOOL)acceptsFirstResponder {
printf("!"); //DOES NOT GET CALLED
return YES;
}
-(BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
printf("8"); //DOES NOT GET CALLED
return YES;
}
-(void)mouseDown:(NSEvent *)theEvent {
printf("o"); //DOES NOT GET CALLED
_starsRated = _starsHovering;
}
-(void)mouseUp:(NSEvent *)theEvent {
printf("O"); //DOES NOT GET CALLED
}
-(void)mouseEntered:(NSEvent *)theEvent {
//DOES GET CALLED
_hovering = YES;
[self setNeedsDisplay:YES];
}
-(void)mouseExited:(NSEvent *)theEvent {
//DOES GET CALLED
_hovering = NO;
[self setNeedsDisplay:YES];
}
-(void)mouseMoved:(NSEvent *)theEvent {
//DOES GET CALLED
NSPoint mouseLocation = [[self window] mouseLocationOutsideOfEventStream];
mouseLocation = [self convertPoint: mouseLocation
fromView: nil];
int newStarsHoveringValue = mouseLocation.x / ([self bounds].size.width / 5.0f);
if (newStarsHoveringValue != _starsHovering) {
_starsHovering = newStarsHoveringValue;
[self setNeedsDisplay:YES];
}
}
#end
It was a bit fiddly, but I managed to create a solution that works. I subclassed NSTableView, then overrode mouseDown with the following code:
-(void)mouseDown:(NSEvent *)theEvent {
NSPoint globalLocation = [theEvent locationInWindow];
NSPoint localLocation = [self convertPoint:globalLocation fromView:nil];
NSInteger clickedRow = [self rowAtPoint:localLocation];
if (clickedRow != -1) {
NSInteger clickedColumn = [self columnAtPoint:localLocation];
if (clickedColumn != -1) {
if (clickedColumn == 3) {
FavouriteCellView *fv = [self viewAtColumn:clickedColumn row:clickedRow makeIfNecessary:NO];
if (fv != nil) {
[fv mouseDown:theEvent];
}
return;
}
}
}
[super mouseDown:theEvent];
}
Now it works exactly like I wanted.
Question:
How to achieve this animation with Spritekit?
What I've done:
Problem:
1) I can draw all four petals,but once I lift my finger to draw the circle, it will still create a line from the previous point where I lift my finger to the new touches begin point. refer to gif below:
2) How to remove the solid orange line from the view incrementally (mine is too abrupt)?
3) Need to tune the .sks file properties.
4) https://stackoverflow.com/questions/29792443/set-the-initial-state-of-skemitternode
This is my code:
#import "GameScene.h"
#interface GameScene()
#property (nonatomic) SKEmitterNode* fireEmmitter;
#property (nonatomic) SKEmitterNode* fireEmmitter2;
#end
#implementation GameScene
NSMutableArray *_wayPoints;
NSTimer* myTimer;
-(void)didMoveToView:(SKView *)view {
_wayPoints = [NSMutableArray array];
//Setup a background
self.backgroundColor = [UIColor blackColor];
//setup a fire emitter
NSString *fireEmmitterPath = [[NSBundle mainBundle] pathForResource:#"magic" ofType:#"sks"];
_fireEmmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:fireEmmitterPath];
_fireEmmitter.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2 - 200);
_fireEmmitter.name = #"fireEmmitter";
_fireEmmitter.zPosition = 1;
_fireEmmitter.targetNode = self;
_fireEmmitter.particleBirthRate = 0;
[self addChild: _fireEmmitter];
//setup another fire emitter
NSString *fireEmmitterPath2 = [[NSBundle mainBundle] pathForResource:#"fireflies" ofType:#"sks"];
_fireEmmitter2 = [NSKeyedUnarchiver unarchiveObjectWithFile:fireEmmitterPath2];
_fireEmmitter2.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
_fireEmmitter2.name = #"fireEmmitter";
_fireEmmitter2.zPosition = 1;
_fireEmmitter2.targetNode = self;
_fireEmmitter2.particleBirthRate = 0;
[self addChild: _fireEmmitter2];
//Setup a LightNode
SKLightNode* light = [[SKLightNode alloc] init];
light.categoryBitMask = 1;
light.falloff = 1;
light.ambientColor = [UIColor whiteColor];
light.lightColor = [[UIColor alloc] initWithRed:1.0 green:1.0 blue:0.0 alpha:0.5];
light.shadowColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.3];
[_fireEmmitter addChild:light];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInNode:self.scene];
CGMutablePathRef ref = CGPathCreateMutable();
CGPoint p = touchPoint;
p = [self.scene convertPointToView:p];
CGPathMoveToPoint(ref, NULL, p.x, p.y);
_fireEmmitter.position = CGPointMake(touchPoint.x, touchPoint.y);
_fireEmmitter.particleBirthRate = 2000;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInNode:self.scene];
//On Dragging make the emitter with the attached light follow the position
for (UITouch *touch in touches) {
[self addPointToMove:touchPoint];
CGPoint location = [touch locationInNode:self];
[self childNodeWithName:#"fireEmmitter"].position = CGPointMake(location.x, location.y);
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
_fireEmmitter.particleBirthRate = 0;
[self performSelector:#selector(userHasCompletedTheDrawing) withObject:nil afterDelay:3];
}
- (void)userHasCompletedTheDrawing{
CGMutablePathRef path = CGPathCreateMutable();
if (_wayPoints && _wayPoints.count > 0) {
CGPoint p = [(NSValue *)[_wayPoints objectAtIndex:0] CGPointValue];
//p = [self.scene convertPointToView:p];
CGPathMoveToPoint(path, nil, p.x, p.y);
_fireEmmitter2.position = CGPointMake(p.x,p.y);
_fireEmmitter2.particleBirthRate = 1000;
for (int i = 0; i < _wayPoints.count; ++i) {
p = [(NSValue *)[_wayPoints objectAtIndex:i] CGPointValue];
CGPathAddLineToPoint(path, nil, p.x, p.y);
}
SKAction *followTrack = [SKAction followPath:path asOffset:NO orientToPath:YES duration:1];
[_fireEmmitter2 runAction:followTrack completion:^{
_fireEmmitter2.particleBirthRate = 0;
[_fireEmmitter2 runAction:[SKAction waitForDuration:1] completion:^{
//_fireEmmitter2.particleBirthRate = 0;
}];
}];
}
//myTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: #selector(removePointToMove) userInfo: nil repeats: YES];
[self performSelector:#selector(removeAllPointToMove) withObject:nil afterDelay:1];
}
- (void)addPointToMove:(CGPoint)point {
[_wayPoints addObject:[NSValue valueWithCGPoint:point]];
}
- (void)removeAllPointToMove{
[_wayPoints removeAllObjects];
}
- (void)removePointToMove{
if ([_wayPoints count]>0) {
[_wayPoints removeObjectAtIndex:0];
}
}
- (void)drawLines {
//1
NSMutableArray *temp = [NSMutableArray array];
for(CALayer *layer in self.view.layer.sublayers) {
if([layer.name isEqualToString:#"line"]) {
[temp addObject:layer];
}
}
[temp makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
//3
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.name = #"line";
lineLayer.strokeColor = [UIColor orangeColor].CGColor;
lineLayer.fillColor = nil;
lineLayer.lineWidth = 3;
lineLayer.lineJoin = kCALineJoinRound; /* The join style used when stroking the path. Options are `miter', `round'
* and `bevel'. Defaults to `miter'. */
lineLayer.zPosition = -1;
//4
CGPathRef path = [self createPathToMove];
lineLayer.path = path;
CGPathRelease(path);
[self.view.layer addSublayer:lineLayer];
}
- (CGPathRef)createPathToMove {
//1
CGMutablePathRef ref = CGPathCreateMutable();
//2
for(int i = 0; i < [_wayPoints count]; ++i) {
CGPoint p = [_wayPoints[i] CGPointValue];
p = [self.scene convertPointToView:p];
//3
if(i == 0 ) {
CGPathMoveToPoint(ref, NULL, p.x, p.y);
} else {
CGPathAddLineToPoint(ref, NULL, p.x, p.y);
}
}
return ref;
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
[self drawLines];
if ([_wayPoints count]==0) {
[myTimer invalidate];
}
}
#end
This is my .sks files properties:
Concerning your first question, you need to split your CGPathRef into multiple subpaths so that no line gets drawn between the petals and the center. Use the CGPathCloseSubpath function when you are done drawing the petals so that you can call CGPathMoveToPoint and CGPathAddLineToPoint afterwards.
I had created an puzzle game which was like Scrabble.
Here is the layout:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
What is my problem?
My problem was when I start touch from 1 and direction to 12, If touch and drag in slow then no problem but when drag in fast, I manage to 1, 6, 12 or 1, 7, 12 only. There is missing a number.
How to make sure the path numbers all be selected?
I am using touch began, touch moved and touch ended and check with coordinate to locate which number is being touched.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentTouchLocation = [touch locationInView:self.numberview];
if(!ispause && [time.text intValue] > 0){
if(!isbegan && !isended){
for(int i = 1; i <= 16; i++)
{
UIView *imageview = [self.numberview viewWithTag:i];
if (CGRectContainsPoint(imageview.frame, currentTouchLocation))
{
isbegan = YES;
isreverse = NO;
if([[ischose objectAtIndex:i-1] boolValue] == 0)
{
currentposition = imageview.tag;
positionvalue += pow(i, 3);
currentanswer += [self converter:[NSString stringWithFormat:#"%#", [allimagenumbers substringWithRange:NSMakeRange(i-1, 1)]]];
[ischose replaceObjectAtIndex:i-1 withObject:[NSNumber numberWithBool:YES]];
[self changeimage:#"selected"];
}
break;
}
}
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentTouchLocation = [touch locationInView:self.numberview];
float gapX = image1.frame.size.width / 8;
float gapY = image1.frame.size.height / 8;
if(isbegan && !isended)
{
if(currentTouchLocation.x >= 0 && currentTouchLocation.x <= self.numberview.frame.size.width && currentTouchLocation.y >= 0 && currentTouchLocation.y <= self.numberview.frame.size.height)
{
for(int i = 1; i <= 16; i++)
{
UIView *imageview = [self.numberview viewWithTag:i];
if (CGRectContainsPoint(imageview.frame, currentTouchLocation))
{
if((currentTouchLocation.x >= imageview.frame.origin.x + gapX && currentTouchLocation.x < imageview.frame.origin.x + imageview.frame.size.width - gapX) && (currentTouchLocation.y >= imageview.frame.origin.y + gapY && currentTouchLocation.y < imageview.frame.origin.y + imageview.frame.size.height - gapY ))
{
if([[ischose objectAtIndex:i-1] boolValue] == 0 && !isreverse)
{
currentposition = imageview.tag;
positionvalue += pow(i, 3);
currentanswer += [self converter:[NSString stringWithFormat:#"%#", [allimagenumbers substringWithRange:NSMakeRange(i-1, 1)]]];
[ischose replaceObjectAtIndex:i-1 withObject:[NSNumber numberWithBool:YES]];
[self changeimage:#"selected"];
}
else
{
if(currentposition != imageview.tag)
{
isreverse = YES;
}
else
{
isreverse = NO;
}
}
break;
}
}
}
}
else
{
isended = YES;
isoutofbound = YES;
if(isbegan && isoutofbound)
[self countinganswer];
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
if(!isoutofbound)
{
isended = YES;
[self countinganswer];
}
else
isoutofbound = NO;
}
-(void)changeimage:(NSString *)status{
if([status isEqualToString:#"default"])
{
for(int i = 1; i <=16;i++)
{
UIImageView *imageview = (UIImageView*)[self.numberview viewWithTag:i];
imageview.image = [UIImage imageNamed:[NSString stringWithFormat:#"stone%#", [allimagenumbers substringWithRange:NSMakeRange(i-1, 1)]]];
[image1 setUserInteractionEnabled:YES];
}
}
else if([status isEqualToString:#"correct"] || [status isEqualToString:#"selected"])
{
for(int i = 1; i<= ischose.count; i++)
{
if([[ischose objectAtIndex:i-1] boolValue] == 1)
{
UIImageView *imageview = (UIImageView*)[self.numberview viewWithTag:i];
imageview.image = [UIImage imageNamed:[NSString stringWithFormat:#"stone%#_correct", [allimagenumbers substringWithRange:NSMakeRange(i-1, 1)]]];
}
}
}
else if([status isEqualToString:#"wrong"] || [status isEqualToString:#"repeat"])
{
for(int i = 1; i<= ischose.count; i++)
{
if([[ischose objectAtIndex:i-1] boolValue] == 1)
{
UIImageView *imageview = (UIImageView*)[self.numberview viewWithTag:i];
imageview.image = [UIImage imageNamed:[NSString stringWithFormat:#"stone%#_wrong_repeat", [allimagenumbers substringWithRange:NSMakeRange(i-1, 1)]]];
}
}
}
}
Update:
Chatting with you, it appears that you have solved your problem (where a swipe over one of your UIImageView objects was not being detected). It looks like the solution was a unique issue (i.e. highly "localized" in Stack Overflow language) associated with your code to create reduced size "hit zones" that you constructed with your gap variables. It doesn't look like the solution was anything really associated with touchesMoved, iOS API, or iOS system performance. Regardless, I'm glad you solved the problem.
My original answer below was predicated on the original source code posted, which had the same logic repeated for each of the 16 UIImageView objects. I was just demonstrating how you can use a UIArray to significantly simplify that logic. I also use UIPanGestureRecognizer, which I think unifies the code, and with <UIKit/UIGestureRecognizerSubclass.h> you can cancel the gesture, in case the user's gesture went "out of bounds."
Original answer:
I'm assuming that you simply want to build an array of image numbers as the user drags their finger over the numbers. So the ARC code might look something like:
// NumberGameViewController.m
#import "NumberGameViewController.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface NumberGameViewController ()
{
NSArray *images;
NSMutableArray *results;
}
#end
#implementation NumberGameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// if you build an array of your images, the logic to determine which image you're over is much easier
images = #[self.image1, self.image2, self.image3, self.image4, self.image5, self.image6, self.image7, self.image8, self.image9, self.image10, self.image11, self.image12, self.image13, self.image14, self.image15, self.image16];
// I know you used `touchesMoved` and the like, but I think gesture recognizers are a little easier
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[self.numberview addGestureRecognizer:pan];
}
- (NSInteger)determineImageNumber:(CGPoint)point
{
for (NSInteger i = 0; i < [images count]; i++)
{
UIImageView *imageview = images[i];
// I'm just going to see if the user's finger was over the number in question.
// If you wanted more restrictive logic (e.g. 3/4ths of the frame), just adjust
// adjust the frame variable here.
CGRect frame = imageview.frame;
if (CGRectContainsPoint(frame, point))
return i;
}
return -1;
}
- (void)handlePan:(UIGestureRecognizer *)gesture
{
CGPoint location = [gesture locationInView:self.numberview];
NSInteger imageNumber = [self determineImageNumber:location];
static NSInteger lastImageNumber;
if (gesture.state == UIGestureRecognizerStateBegan)
{
results = [[NSMutableArray alloc] init];
if (imageNumber >= 0)
{
[results addObject:#(imageNumber)];
}
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
if (imageNumber >= 0)
{
if (imageNumber != lastImageNumber)
{
// If you want to do some visual adjustment of the image you're over, do it
// here.
// add the image to our array of results
[results addObject:#(imageNumber)];
// if you want to do some additional validation (e.g. do you have 16 points,
// has the user hit the same number twice, etc.), do that here
}
}
// by the way, let's check to see if we're still within the numberview subview, and if
// not, let's cancel the gesture
if (!CGRectContainsPoint(self.numberview.bounds, location))
{
gesture.state = UIGestureRecognizerStateCancelled;
return;
}
}
else if ((gesture.state == UIGestureRecognizerStateEnded) || (gesture.state == UIGestureRecognizerStateCancelled) || (gesture.state == UIGestureRecognizerStateFailed))
{
// At this point you'd do any final validation of the user's response, to see if they
// succeeded or not. I'm just displaying the results in the console
NSLog(#"%#", results);
}
if (imageNumber >= 0)
lastImageNumber = imageNumber;
}
#end
float gapX = image1.frame.size.width / 8;
float gapY = image1.frame.size.height / 8;
Here is the problem, there is a gap within all images so when fast drag it will be outer part of images
In my project I have a UIImageVIew where the user imports a backgroudn and then puts smaller pictures on that background.
I want to apply the gestures only to the smaller pictures that are being tapped. The gestures I am using are tap, long press, pand and pinch gestures. My problem is only with pinch gestures. I can apply long press, tap and pan individually BUT with pinch, not only the smaller pictures zoom but also the background. Any idea of how can I fix this?
in my - (void)viewDidLoad I have set:
//single tap
UITapGestureRecognizer *tapg =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tapAction:)];
[self.itemView addGestureRecognizer:tapg];
// double tap
UITapGestureRecognizer *doubleTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(doubleTapAction:)];
doubleTap.numberOfTapsRequired = 2;
[self.itemView addGestureRecognizer:doubleTap];
// longPress
UILongPressGestureRecognizer *longPress =
[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(longPressAction:)];
[self.itemView addGestureRecognizer:longPress];
// pan
UIPanGestureRecognizer *pan =
[[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(panAction:)];
[self.itemView addGestureRecognizer:pan];
//pinch
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchAction:)];
[self.itemView addGestureRecognizer:pinch];
then,
// tap
- (void)tapAction:(UITapGestureRecognizer *) sender{
CGPoint tapPoint = [sender locationInView:self.itemView];
NSLog(#">>>tap x=%.2f, y=%.2f", tapPoint.x, tapPoint.y);
int i =0;
isTaped = NO;
for (i = 0; i < ivMax; i++) {
if (CGRectContainsPoint((*(iViews+i)).frame, tapPoint)) {
isTaped = YES;
iViewsTapidx = i;
NSLog(#"i = %d", i);
break;
}
}
}
// doubleTap
- (void)doubleTapAction:(UITapGestureRecognizer *) sender{
NSLog(#">>>doubleTap");
CGPoint tapPoint = [sender locationInView:self.itemView];
isTaped = NO;
isDblTaped = NO;
int i =0;
for (i = 0; i < ivMax; i++) {
if (CGRectContainsPoint((*(iViews+i)).frame, tapPoint)) {
isDblTaped = YES;
iViewsDblTapidx = i;
break;
}
}
// view
if (isDblTaped) {
NSLog(#"remove %d", i);
(*(iViews+i)).tag = 0;
[*(iViews+i) removeFromSuperview];
}
}
// longPress
- (void)longPressAction:(UILongPressGestureRecognizer *) sender{
if ([sender state] == UIGestureRecognizerStateBegan) {
NSLog(#">>>longPress 1");
}else if ([sender state] == UIGestureRecognizerStateEnded) {
// NSLog(#">>>longPress 2");
CGPoint tapPoint = [sender locationInView:self.itemView];
NSLog(#">>>longPress 2 x=%.2f, y=%.2f", tapPoint.x, tapPoint.y);
int i =0;
for (i = 0; i < ivMax; i++) {
NSLog(#"i = %d", i);
if ((*(iViews+i)).tag == 0) {
break;
}
}
if (i < ivMax) {
//set smaller picture
UIImage *stampImage;
NSString *imagepath = [NSString stringWithFormat:#"%#%#",[FileUtility getPDFImageFolderPath], self.menu.imageID];
if (self.menu.imageID == nil || [self.menu.imageID isEqualToString:#""]) {
stampImage = [UIImage imageNamed:#"NotExistFile.jpg"];
} else {
stampImage = [UIImage imageWithContentsOfFile:imagepath];
}
int parcent = stampImage.size.width / _width;
//show smaller picture
*(iViews+i) = [[UIImageView alloc] initWithImage:stampImage];
(*(iViews+i)).frame = CGRectMake(tapPoint.x - stampImage.size.width/parcent/2,
tapPoint.y - stampImage.size.height/parcent/2,
stampImage.size.width/parcent,
stampImage.size.height/parcent);
(*(iViews+i)).tag = i+1;
[self.itemView addSubview:*(iViews+i)];
iViewsTapidx = i;
isTaped = YES;
}
}
}
// Pan
- (void)panAction:(UIPanGestureRecognizer *) sender{
NSLog(#">>>pan");
if (isTaped) {
CGPoint p = [sender translationInView:self.itemView];
CGPoint movePoint = CGPointMake((*(iViews+iViewsTapidx)).center.x + p.x,
(*(iViews+iViewsTapidx)).center.y + p.y);
(*(iViews+iViewsTapidx)).center = movePoint;
// NSLog(#">>>pan x=%.2f, y=%.2f --> x=%.2f, y=%.2f", p.x, p.y, movePoint.x, movePoint.y);
NSLog(#">>>pan x=%.2f, y=%.2f", p.x, p.y);
[sender setTranslation:CGPointZero inView:self.itemView];
}
}
// scale the piece by the current scale
// reset the gesture recognizer's rotation to 0 after applying so the next callback is a delta from the current scale
- (void)pinchAction:(UIPinchGestureRecognizer *)sender
{
if ([sender state] == UIGestureRecognizerStateBegan || [sender state] == UIGestureRecognizerStateChanged) {
[sender view].transform = CGAffineTransformScale([[sender view] transform], [sender scale], [sender scale]);
[sender setScale:1];
}
}
give your image name insted of
[self.itemView addGesture.....
So I was just coding along, then Xcode just started going whack when I hit build and gave me all these error that I have never had before
Here is the file:
import
#import <OpenGLES/EAGLDrawable.h>
#import "EAGLView.h"
#define USE_DEPTH_BUFFER 0
// A class extension to declare private methods #interface EAGLView ()
#property (nonatomic, retain) EAGLContext *context; #property (nonatomic, assign) NSTimer *animationTimer;
- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;
- (void) updateScene:(float)delta;
- (void) renderScene;
#end
#implementation EAGLView
#synthesize context; #synthesize animationTimer; #synthesize animationInterval;
// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class]; }
//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}
animationInterval = 1.0 / 60.0;
CGRect rect = [[UIScreen mainScreen] bounds];
// Set up OpenGL projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0,
rect.size.width, 0, rect.size.height, -1, 1);
glMatrixMode(GL_MODELVIEW); glViewport(0, 0, rect.size.width,
rect.size.height);
// Initialize OpenGL states glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND_SRC);
glEnableClientState(GL_VERTEX_ARRAY); glClearColor(0.0f, 0.0f,
0.0f, 1.0f);
// init [self initGame];
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f / 60.0f;
//[sharedSoundManager playMusicWithKey:#"song" timesToRepeat:-1];
}
return self; }
-(void) initGame{
//SET GAME STATE
//0 = Menu
//1 = Gameplay
//2 = death screen
gameState = 0;
player = [[Circle alloc] init];
score = 0;
scoreString = [NSString alloc];
scoreAdder = 1;
//[self setupScore];
menu = [[Image alloc] initWithImage:[UIImage imageNamed:#"loadImage.png"]];
bk = [[Image alloc] initWithImage:[UIImage imageNamed:#"bk.png"]];
squared = [[Image alloc] initWithImage:[UIImage imageNamed:#"squared.png"]];
gameRunning = TRUE;
gameState = 0;
squares = [[Squares alloc] init];
[squares addSquare: CGPointMake(100, 100) : 0];
//font = [[AngelCodeFont alloc]initWithFontImageNamed:#"font1.png" controlFile:#"font1.fnt"
scale: 0.0f filter: nil];
// Init sound sharedSoundManager = [SingletonSoundManager sharedSoundManager]; [sharedSoundManager loadSoundWithKey:#"menu"
fileName:#"Menu" fileExt:#"mp3" frequency: 22050];
[sharedSoundManager loadBackgroundMusicWithKey:#"music" fileName:#"bkmusic" fileExt:#"aif"];
menuMusicVariable = 1;
gameMusicVariable = 1;
}
-(void) setRunning: (bool) boolean{
gameRunning = boolean; }
-(void) runContinueCountdown{
if(gameRunning == NO){
int timer = 300;
int countdownNum;
timer --;
if(timer <= 300){
if(timer > 200){
countdownNum = 3;
}
if(timer <= 200){
if(timer > 100){
countdownNum = 2;
}
}
if(timer <= 100){
if(timer >= 1){
countdownNum = 1;
}
}
if(timer == 0 && countdownNum == 1){
[self setRunning: YES];
}
} }
- (void) mainGameLoop { CFTimeInterval time; float delta; time = CFAbsoluteTimeGetCurrent(); delta = (time - lastTime);
[self updateScene:delta]; [self renderScene]; lastTime = time;
}
- (void)updateScene:(float)delta { // Update Game Logic
if(gameRunning){
if(gameState == 0){
//MENU
if(menuMusicVariable == 1){
[sharedSoundManager stopPlayingMusic];
[sharedSoundManager playSoundWithKey:#"Menu" gain:10 pitch:10 location:Vector2fMake(0, 0) shouldLoop: TRUE];
menuMusicVariable = 0;
}
[scoreLabel setHidden: YES];
} else if(gameState == 1){
//GAMEPLAY
if(gameMusicVariable == 1){
[sharedSoundManager stopPlayingMusic];
[sharedSoundManager playMusicWithKey: #"music" timesToRepeat: -1];
gameMusicVariable = 0;
}
[scoreLabel setHidden: NO];
//game is running
if([player getAlive] == true){
score = score + scoreAdder;
} else {
score = score;
}
[player move];
[self checkSquareToCircleCollisions];
[self checkSquareToSquareCollisions];
[squares update];
} else if(gameState == 2){
//DEATH SCREEN
[sharedSoundManager stopPlayingMusic];
[scoreLabel setBounds: CGRectMake(100, 100 , 100, 40)];
}
} else {
//game is puased
} }
- (void)renderScene {
// Make sure we are renderin to the frame buffer
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// Clear the color buffer with the glClearColor which has been set glClear(GL_COLOR_BUFFER_BIT); //Render the game Scene
if(gameState == 0){
//MENU
[menu renderAtPoint: CGPointMake(0, 0) centerOfImage: NO];
} else if(gameState == 1){
//GAMEPLAY
[bk renderAtPoint: CGPointMake(0, 0) centerOfImage: NO];
[self drawScore];
[player draw];
[squares render];
//[font drawStringAt: CGPointMake(150, 100) text:#"HELLO FONTS"];
} else if(gameState ==2){
//DEATH SCREEN
[squared renderAtPoint: CGPointMake(0, 0) centerOfImage: NO];
} // Switch the render buffer and framebuffer so our scene is displayed on the screen
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES]; }
- (void)layoutSubviews {
[EAGLContext setCurrentContext:context];
[self destroyFramebuffer];
[self createFramebuffer];
[self renderScene]; }
- (BOOL)createFramebuffer {
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
if (USE_DEPTH_BUFFER) {
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
}
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
NSLog(#"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}
return YES; }
- (void)destroyFramebuffer {
glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;
if(depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
} }
- (void)startAnimation {
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self
selector:#selector(mainGameLoop) userInfo:nil repeats:YES]; }
- (void)stopAnimation {
self.animationTimer = nil; }
- (void)setAnimationTimer:(NSTimer *)newTimer {
[animationTimer invalidate];
animationTimer = newTimer; }
- (void)setAnimationInterval:(NSTimeInterval)interval {
animationInterval = interval;
if (animationTimer) {
[self stopAnimation];
[self startAnimation];
} }
-(void) setupScore{
scoreLabel = [NSString stringWithFormat:#"%d", score];
scoreLabel.frame = CGRectMake(262, 250, 100, 40);
[scoreLabel setText: scoreString];
//normally you'll want a transparent background for your label
scoreLabel.backgroundColor = [UIColor clearColor];
//you can use non-standard fonts
[scoreLabel setFont:[UIFont fontWithName:#"TimesNewRoman" size: 1.0f]];
//change the label's text color
scoreLabel.textColor = [UIColor whiteColor];
//add it to your view
scoreLabel.transform = CGAffineTransformMakeRotation(89.53);
[self addSubview:scoreLabel]; }
-(void) resetScore {
score = 0;
scoreLabel.textColor = [UIColor blackColor];
[scoreLabel release]; }
-(void)drawScore{
[scoreLabel setText: scoreString]; }
-(void) checkSquareToCircleCollisions{
NSMutableArray *array = [squares getSquares];
for(int i = 0; i < [squares getCount]; i++){
Square *s = [array objectAtIndex: i];
CGRect rect1 = [player getRect];
CGRect rect2 = [s getRect];
if (CGRectIntersectsRect(rect1, rect2)){
player.alive = NO;
gameState = 2;
}
} }
-(void) checkSquareToSquareCollisions{
NSMutableArray *array = [squares getSquares];
for(int i = 0; i < [squares getCount]; i++){
Square *s = [array objectAtIndex: 0];
Square *ss = [array objectAtIndex: i];
CGRect rect1 = [s getRect];
CGRect rect2 = [ss getRect];
if (CGRectIntersectsRect(rect1, rect2)) {
[s setDirection: [s getXDir] * -1 : [s getYDir] * -1];
[ss setDirection: [ss getXDir] * -1 : [ss getYDir] * -1];
}
} }
-(void) spawnSquares {
// FINISH METHOD
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(gameState == 0){
//MENU
UITouch *touch = [[event allTouches] anyObject];
gameState = 1;
//[self initGame];
} else if(gameState == 1){
//GAMEPLAY
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:touch.view];
touchPos.y = 480 - touchPos.y;
[player setPos: touchPos];
} else if(gameState == 2){
//DEATH SCREEN
UITouch *touch = [[event allTouches] anyObject];
gameState = 0;
//[self resetScore];
[self initGame];
} }
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(gameState == 0){
//MENU
UITouch *touch = [[event allTouches] anyObject];
//[self initGame];
} else if(gameState == 1){
//GAMEPLAY
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:touch.view];
touchPos.y = 480 - touchPos.y;
[player setPos: touchPos];
} else if(gameState == 2){
//DEATH SCREEN
UITouch *touch = [[event allTouches] anyObject];
}}
- (void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration {
point.y = acceleration.y * 10;
point.x = acceleration.x * 10;
CGPoint pos = [player getPos];
pos = CGPointMake(pos.x + point.x, pos.y + point.y);
[player setPos: pos];
//Right - MAY HAVE TO CHANGE
if(pos.x < 0){
pos = CGPointMake(320, pos.y);
}
//left
if(pos.x < 320){
pos = CGPointMake(0, pos.y);
}
//Top
if(pos.y < 0){
pos = CGPointMake(pos.x, 460);
}
//Bottom
if(pos.x < 460){
pos = CGPointMake(pos.x, 0);
}
}
- (void)dealloc {
[self stopAnimation];
if ([EAGLContext currentContext] == context) {
[EAGLContext setCurrentContext:nil];
}
[context release];
[player release];
[menu release];
[sharedSoundManager release];
[bk release];
[squared release];
[squares release];
[scoreLabel release];
[scoreString release];
[super dealloc]; }
#end // here it says: excepted } and also its excepting #end
You are missing a brace in the runContinueCountdown at the end of the second if statement.
if(timer <= 300){
if(timer > 200){
countdownNum = 3;
}
Add a brace to the end of this if statement