I need to implement a simple pinch gesture in my cocos2d project with two or more sprites. Would you be so kind to tell me, how can I handle pitch gesture?
I need something like this:
-(void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
if (recognizer.state != UIGestureRecognizerStateCancelled)
{
if (recognizer.numberOfTouches == 3)
{
CGPoint firstPoint = [recognizer locationOfTouch:0 inView:recognizer.view];
CGPoint secondPoint = [recognizer locationOfTouch:1 inView:recognizer.view];
CGPoint thirdPoint = [recognizer locationOfTouch:2 inView:recognizer.view];
CGPoint fourthPoint = [recognizer locationOfTouch:3 inView:recognizer.view];
ball1.position=firstPoint;
ball2.position=secondPoint;
ball3.position=thirdPoint;
ball4.position=fourthPoint;
}
}
}
Here what I did (it took me quite a lot of googling)
In implementation file
-(id)init
{
self = [super init];
self.isTouchEnabled=YES;
if (self != nil)
{
}
return self;
}
//pinch recognising
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allUserTouches=[event allTouches];
if(allUserTouches.count==2)
{
UITouch* touch1=[[allUserTouches allObjects] objectAtIndex:0];
UITouch* touch2=[[allUserTouches allObjects] objectAtIndex:1];
CGPoint touch1location=[touch1 locationInView:[touch1 view]];
CGPoint touch2location=[touch2 locationInView:[touch2 view]];
touch1location=[[CCDirector sharedDirector] convertToGL:touch1location];
touch2location=[[CCDirector sharedDirector] convertToGL:touch2location];
ball.position=touch1location;
newBall.position=touch2location;
float currentdist=ccpDistance(touch1location, touch2location);
oldDist=currentdist;
}
}
-(void)ccTouchesMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
NSSet *allUserTouches=[event allTouches];
if(allUserTouches.count==2)
{
UITouch* touch1=[[allUserTouches allObjects] objectAtIndex:0];
UITouch* touch2=[[allUserTouches allObjects] objectAtIndex:1];
CGPoint touch1location=[touch1 locationInView:[touch1 view]];
CGPoint touch2location=[touch2 locationInView:[touch2 view]];
touch1location=[[CCDirector sharedDirector] convertToGL:touch1location];
touch2location=[[CCDirector sharedDirector] convertToGL:touch2location];
float currentdist=ccpDistance(touch1location, touch2location);
if (oldDist>=currentdist)
{
//[spriteToZoom setScale:spriteToZoom.scale-fabs((oldDist-currentdist)/100)];
[ball setPosition:touch1location];
[newBall setPosition:touch2location];
NSLog(#"pinch out");
}
else
{
//[spriteToZoom setScale:spriteToZoom.scale+fabs((oldDist-currentdist)/100)];
[ball setPosition:touch1location];
[newBall setPosition:touch2location];
NSLog(#"pinch in");
}
}
}
Related
I have written a Singleton Class for managing iAds.The iAds pop up after 5 seconds of the user inactivity. The idleTimerExceeded call generate a notification to show the iAd. This code works fine for my requirements but since I am new to iOS development, my application sometimes hangs unexpectedly after integrating this code. This code results in lots of warnings etc. I would like to optimize my code in terms of memory and performance.
I would be very thankful for your kind suggestions and reviews.
Below is my code:
iAdSingleton.h
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "iAd/iAd.h"
#interface iAdSingleton : UIViewController<ADBannerViewDelegate> {
ADBannerView *adView;
UIViewController *displayVC;
NSTimer *idleTimer;
BOOL isItFirstTime;
}
#property (nonatomic, retain) ADBannerView *adView;
#property (nonatomic, retain) UIViewController *displayVC;
#property (nonatomic) BOOL isItFirstTime;
+ (id) shareAdSingleton;
- (void) resetIdleTimer;
- (void) idleTimerExceeded;
#end
iAdSingleton.m
#import "iAdSingleton.h"
#implementation iAdSingleton
static iAdSingleton* _sharedAdSingleton = nil;
BOOL bannerVisible = NO;
BOOL controlAccessBannerVisibility = NO;
#synthesize adView, displayVC;
#synthesize isItFirstTime;
#define kMaxIdleTimeSeconds 5.0
+(id)sharedAdSingleton
{
#synchronized(self)
{
if(!_sharedAdSingleton)
_sharedAdSingleton = [[self alloc] init];
return _sharedAdSingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([iAdSingleton class])
{
NSAssert(_sharedAdSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedAdSingleton = [super alloc];
return _sharedAdSingleton;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil) {
/* Initialize The Parameters Over Here */
//adView = [[ADBannerView alloc] initWithFrame:CGRectMake(0, 480, 0, 0)];
adView = [[ADBannerView alloc] init];
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
self.adView.delegate=self;
[self resetIdleTimer];
}
return self;
}
-(void)dealloc
{
displayVC = nil;
if (adView) {
[adView removeFromSuperview]; //Remove ad view from superview
[adView setDelegate:nil];
adView = nil;
}
[super dealloc];
}
-(UIViewController *)viewControllerForPresentingModalView
{
return displayVC;
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
banner.hidden = NO;
if(!bannerVisible){
NSLog(#"Banner Changes 1 - Purpose: Visibility");
// [UIView beginAnimations:#"bannerAppear" context:NULL];
// banner.frame = CGRectOffset(banner.frame, 0, -100);
// [UIView commitAnimations];
bannerVisible = YES;
controlAccessBannerVisibility = YES;
}
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
//NSLog(#"Unable to receive Ad.");
NSLog(#"Banner Changes 2 - Purpose: Unable to Receive Ad.");
banner.hidden = YES;
if(bannerVisible){
[UIView beginAnimations:#"bannerDisappear" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, 100);
[UIView commitAnimations];
bannerVisible = NO;
}
}
- (BOOL) bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
NSLog(#"Pause anything necessary");
return YES;
}
- (void) bannerViewActionDidFinish:(ADBannerView *)banner
{
NSLog(#"We now resume to normal operations");
}
- (void)resetIdleTimer {
if (!idleTimer) {
idleTimer = [[NSTimer scheduledTimerWithTimeInterval:kMaxIdleTimeSeconds
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
}
else {
if (fabs([idleTimer.fireDate timeIntervalSinceNow]) < kMaxIdleTimeSeconds-1.0) {
[idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:kMaxIdleTimeSeconds]];
/*
Notification: HideAd
*/
NSLog(#"Notification Generated For HideAd");
[[NSNotificationCenter defaultCenter] postNotificationName:#"HideAdBanner" object:nil userInfo:nil];
}
}
}
- (void)idleTimerExceeded {
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDel.adVisible == NO) {
NSLog(#"Notification Generated For ShowAd");
/*
Notification: ShowAd
*/
if (controlAccessBannerVisibility == YES) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"ShowAdBanner" object:nil userInfo:nil];
}
}
}
#end
This is what you need. This code is thread safe as well as will not have any memory issue and warnings.
+ (iAdSingleton *) sharedInstance
{
static dispatch_once_t onceToken;
static iAdSingleton * __sharedInstance = nil;
dispatch_once(&onceToken, ^{
__sharedInstance = [[self alloc] init];
});
return __sharedInstance;
}
I'm trying to create a drawing view by drawing the line(s) using an Array of CGPoints.
I'm currently able to draw more than one line but the problem is that I don't know how to break each line when touch is ended.
The current status is -
line1 is drawn until touchended
When touchbegan again, line2 is drawn as well, BUT, line1 endpoint is connected with line2 starting point.
Implementation as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSUInteger tapCount = [[touches anyObject] tapCount];
if (tapCount == 2)
{
[pointArray removeAllObjects];
[self setNeedsDisplay];
}
else
{
if ([pointArray count] == 0)
pointArray = [[NSMutableArray alloc] init];
UITouch *touch = [touches anyObject];
start_location = [touch locationInView:self];
[pointArray addObject:[NSValue valueWithCGPoint:start_location]];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
current_location = [touch locationInView:self];
[pointArray addObject:[NSValue valueWithCGPoint:current_location]];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void)drawRect:(CGRect)rect
{
if ([pointArray count] > 0)
{
int i;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
for (i=0; i < ([pointArray count] -1); i++)
{
CGPoint p1 = [[pointArray objectAtIndex:i]CGPointValue];
CGPoint p2 = [[pointArray objectAtIndex:i+1]CGPointValue];
CGContextMoveToPoint(context, p1.x, p1.y);
CGContextAddLineToPoint(context, p2.x, p2.y);
CGContextStrokePath(context);
}
}
}
Please advise :-))
Thank you in advance,
Dudi Shani-Gabay
In this case, I think its better for you to keep separate arrays for separate lines. Let the "pointArray" be an array having number of arrays for each line drawn.
In "touchesBegan" method, you need to add new array object to pointArray as follows:
start_location = [touch locationInView:self];
NSMutableArray *newLineArray = [[NSMutableArray alloc] init];
[pointArray addObject:newLineArray];
[[pointArray lastObject] addObject:[NSValue valueWithCGPoint:start_location]];
In "touchesMoved", you have to replace
[pointArray addObject:[NSValue valueWithCGPoint:current_location]];
with the following:
[[pointArray lastObject] addObject:[NSValue valueWithCGPoint:current_location]];
In the "drawRect" method, the 'for' loop should be like this:
for (i=0; i < [pointArray count]; i++)
{
for (int j=0; j < ([[pointArray objectAtIndex:i] count] -1); j++)
{
CGPoint p1 = [[[pointArray objectAtIndex:i] objectAtIndex:j]CGPointValue];
CGPoint p2 = [[[pointArray objectAtIndex:i] objectAtIndex:j+1]CGPointValue];
CGContextMoveToPoint(context, p1.x, p1.y);
CGContextAddLineToPoint(context, p2.x, p2.y);
CGContextStrokePath(context);
}
}
I have a IPhone drawing application which is slowing down when I use the spray can tool. But when I check for leaks by running the application with leaks no leaks are shown. All other tools are working fine can some help me out in resolving this issue.
My Spray can tools class code is as following:
- init {
if ((self = [super init]))
{
trackingTouches = [[NSMutableArray array] retain];
startPoints = [[NSMutableArray array] retain];
paths = [[NSMutableArray array] retain];
}
return self;
}
- (void)activate { } - (void)deactivate {
[trackingTouches removeAllObjects];
[startPoints removeAllObjects];
[paths removeAllObjects];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// UIView *touchedView = [delegate viewForUseWithTool:self];
UITouch *theTouch = [touches anyObject];
UIView *touchedView = [delegate viewForUseWithTool:self];
endPoint = [theTouch locationInView:touchedView];
for (UITouch *touch in [event allTouches]) {
// CGFloat lineWidth=10;
// remember the touch, and its original start point, for future
[trackingTouches addObject:touch];
CGPoint location = [touch locationInView:touchedView];
[startPoints addObject:[NSValue valueWithCGPoint:location]];
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineCapStyle = kCGLineCapRound;
[path moveToPoint:location];
[path setLineWidth:delegate.strokeWidth];
[path addLineToPoint:location];
[paths addObject:path];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self deactivate];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// UITouch *theTouch = [touches anyObject];
// UIView *touchedView = [delegate viewForUseWithTool:self];
//endPoint = [theTouch locationInView:touchedView];
for (UITouch *touch in [event allTouches])
{ // make a line from the start point to the current point
NSUInteger touchIndex = [trackingTouches indexOfObject:touch];
// only if we actually remember the start of this touch...
if (touchIndex != NSNotFound) {
UIBezierPath *path = [paths objectAtIndex:touchIndex];
PathDrawingInfo *info = [PathDrawingInfo pathDrawingInfoWithPath:path fillColor:[UIColor clearColor] strokeColor:delegate.strokeColor];
[delegate addDrawable:info];
[trackingTouches removeObjectAtIndex:touchIndex];
[startPoints removeObjectAtIndex:touchIndex];
[paths removeObjectAtIndex:touchIndex];
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// UIView *touchedView = [delegate viewForUseWithTool:self];
UITouch *theTouch = [touches anyObject];
UIView *touchedView = [delegate viewForUseWithTool:self];
currentpoint = [theTouch locationInView:touchedView];
//currentpoint.y -=20;
//[self drawCircle];
for (UITouch *touch in [event allTouches]) {
CGFloat lineWidth=10;
// make a line from the start point to the current point
NSUInteger touchIndex = [trackingTouches indexOfObject:touch];
UIBezierPath *path = [UIBezierPath bezierPath];
if (touchIndex != NSNotFound) {
// CGPoint location = [touch locationInView:touchedView];
UIBezierPath *path = [paths objectAtIndex:touchIndex]; UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake((currentpoint.x-lineWidth),( currentpoint.y-lineWidth), lineWidth*2, lineWidth*2)];
NSInteger i,x,y;
NSInteger modNumber =5*(int)lineWidth;
for (i = 0; i < (lineWidth*lineWidth)/2; i++) {
do {
x = (random() % modNumber)+currentpoint.x - lineWidth*2;
y = (random() % modNumber)+currentpoint.y - lineWidth*2;
} while (![circle containsPoint:CGPointMake(x,y)]);
[path appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(x,y,0.0025,0.0025)]];
}
[path moveToPoint:currentpoint];
[path setLineWidth:delegate.strokeWidth];
[path addLineToPoint:currentpoint];
// only if we actually remember the start of this touch...
}
[paths addObject:path];;
}
}
- (void)drawTemporary {
for (UIBezierPath *path in paths) {
[delegate.strokeColor setStroke];
[path stroke];
}
}
- (void)dealloc {
[trackingTouches release];
trackingTouches = nil;
[startPoints release];
startPoints = nil;
[paths release];
paths = nil;
self.delegate = nil;
[super dealloc];
}
define the properties in the h block and synthesize in the m block.
#property (nonatomic, retain) NSMutableArray *trackingTouches;
#property (nonatomic, retain) NSMutableArray *startPoints;
#property (nonatomic, retain) NSMutableArray *paths;
#synthesize trackingTouches,startPoints,paths;
and use the dealloc block like this
- (void)dealloc {
[self setTrackingTouches:nil];
[self setStartPoints:nil];
[self setPaths:nil];
self.delegate = nil;
[super dealloc];
}
don't use UIBezierPath. create a CGMutablePathRef instead with the plain Core Graphics functions.
Hi when I am compiling my code i am getting this error-"accessing unknown 'flipDelegate' component of a property"
this is the code from where i am getting error-
//
// RootViewController.m
// Dolphia
//
// Created by Dolphia Nandi on 4/3/11.
// Copyright 2011 State University of New York at Buffalo. All rights reserved.
//
#import "RootViewController.h"
#import "MainViewController.h"
#import "FlipSideViewController.h"
#implementation RootViewController
#synthesize mainViewController;
#synthesize flipSideViewController;
- (void)loadMainViewController {
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.mainViewController = viewController;
self.mainViewController.flipDelegate = self;
[viewController release];
}
- (void)loadFlipSideViewController {
FlipSideViewController *viewController = [[FlipSideViewController alloc] initWithNibName:#"FlipSideViewController" bundle:nil];
self.flipSideViewController = viewController;
self.flipSideViewController.flipDelegate = self;
[viewController release];
}
- (void)viewDidLoad {
[self loadMainViewController]; // Don't load the flipside view unless / until necessary
[self.view addSubview:mainViewController.view];
}
// This method is called when either of the subviews send a delegate message to us.
// It flips the displayed view from the whoever sent the message to the other.
- (void)toggleView:(id)sender {
if (flipSideViewController == nil) {
[self loadFlipSideViewController];
}
UIView *mainWindow = mainViewController.view;
UIView *flipSideView = flipSideViewController.view;
/*[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
if(flipeffect >= 0 && flipeffect < 2) {
flipeffect++;
[UIView setAnimationTransition:((voiceViewController == sender) ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES];
} else if (flipeffect >= 2 && flipeffect < 4) {
flipeffect++;
[UIView setAnimationTransition:((voiceViewController == sender) ? UIViewAnimationTransitionCurlUp : UIViewAnimationTransitionCurlDown) forView:self.view cache:YES];
} else if (flipeffect >= 4 && flipeffect < 6) {
flipeffect++;
[UIView setAnimationTransition:((voiceViewController == sender) ? UIViewAnimationTransitionFlipFromLeft : UIViewAnimationTransitionFlipFromRight) forView:self.view cache:YES];
} else {
flipeffect++;
if(flipeffect > 7)
flipeffect = 0;
[UIView setAnimationTransition:((voiceViewController == sender) ? UIViewAnimationTransitionCurlDown : UIViewAnimationTransitionCurlUp) forView:self.view cache:YES];
}*/
if (mainViewController == sender) {
[flipSideViewController viewWillAppear:YES];
[mainViewController viewWillDisappear:YES];
[mainWindow removeFromSuperview];
[self.view addSubview:flipSideView];
[mainViewController viewDidDisappear:YES];
[flipSideViewController viewDidAppear:YES];
} else {
[mainViewController viewWillAppear:YES];
[flipSideViewController viewWillDisappear:YES];
[flipSideView removeFromSuperview];
[self.view addSubview:mainWindow];
[flipSideViewController viewDidDisappear:YES];
[mainViewController viewDidAppear:YES];
}
//[UIView commitAnimations];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[mainViewController release];
[flipSideViewController release];
[super dealloc];
}
#end
Both mainViewController and flipSideViewController should have flipDelegate as instance variable and make sure you add #synthesize and #property
What i have so far is
#synthesize txtCountry,txtState;
int flgTextField;
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
[pickerView reloadAllComponents];
// Make a new view, or do what you want here
if(textField == txtCountry || textField == txtState){
flgTextField = textField.tag;
[UIView beginAnimations:nil context:NULL];
//[pvState setFrame:CGRectMake(0.0f, 199.0f, 320.0f, 216.0f)];
[UIView commitAnimations];
return NO;
}
else {
return YES;
}
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
if(flgTextField==1){
return [arryCountry count];
}
else {
return [arryState count];
}
}
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if(flgTextField==1){
return [arryCountry objectAtIndex:row];
}
else{
return [arryState objectAtIndex:row];
}
}
- (void)viewDidLoad {
arryCountry = [[NSMutableArray alloc] init];
arryState = [[NSMutableArray alloc] init];
[arryCountry addObject:#" 100 "];
[arryCountry addObject:#" 200 "];
[arryCountry addObject:#" 400 "];
[arryCountry addObject:#" 600 "];
[arryCountry addObject:#" 1000 "];
[arryState addObject:#" a "];
[arryState addObject:#" b "];
[arryState addObject:#" c "];
[arryState addObject:#" d "];
[arryState addObject:#" e "];
[super viewDidLoad];
}
in my .m
and
#interface Contact : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
IBOutlet UITextField *txtCountry;
IBOutlet UITextField *txtState;
NSMutableArray *arryCountry;
NSMutableArray *arryState;
UIPickerView *pickerView;
}
#property(nonatomic,retain) IBOutlet UITextField *txtCountry;
#property(nonatomic,retain) IBOutlet UITextField *txtState;
in my .h file
Now the text fields are not editable and I need some help or guidance, or any tutorial on how to connect UIPicker with multiple sources that can be change when text fields are editing
So i see no one cares :)
what i have now is 3 textFields and whenever i touch textField1 or textField2 Picker changes values and there is no keyboard. When i touch textField3 keyboard appears and the picker goes hidden.Now if i dismiss the keyboard by clicking return and then click textField1 picker appears again, but if i dont dismiss the keyboard BY CLICKING BUTTON it stays over the picker. What I need is when the keyboard is firstResponder (and i see it on the screen) to hide it if i click on the textField1 and only to see the picker
int variabla;
-(void)textFieldDidBeginEditing:(UITextField *)textField{
[pickerView setHidden:YES];
if (textField1.editing == YES) {
[textField1 resignFirstResponder];
[pickerView setHidden:NO];
variabla = 1;
}else if (textField2.editing == YES) {
[textField2 resignFirstResponder];
[pickerView setHidden:NO];
variabla = 2;
}
NSLog(#"variabla %d",variabla);
[pickerView reloadAllComponents];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
{
if (variabla == 1) {
return [pickerArray1 count];
}else if (variabla == 2) {
return [pickerArray2 count];
}else {
return 0;
}
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
{
if (variabla == 1) {
return [pickerArray1 objectAtIndex:row];
}else if (variabla == 2) {
return [pickerArray2 objectAtIndex:row];
}else {
return 0;
}
}
- (void)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
[pickerView setHidden:YES];
pickerArray1 = [[NSMutableArray alloc] initWithObjects:#"0", #"1", #"2", nil];
pickerArray2 = [[NSMutableArray alloc] initWithObjects:#"3", #"4", #"5", nil];
}