Variable state lost when set within gesture recogniser function - iphone

I am using UIPanGestureRecognizer to detect swipes in my app. The gesture recogniser fires correctly but when I set a variable or try to call a function from within it the variables are set but, on next iteration (of my tick function for example) the variable is not set. It is as if it is setting it on a different instance? Can anyone help me out here?
So _ApplyForce gets set to true in the handlePan function BUT it is always false when I try to read it elsewhere (in my tick function).
Update: I have added all the places _ApplyForce is used to help show how it is used. Also, the reason I use this variable to trigger off the force impulse is because of this question where I couldn't seem to trigger a force from within the gesture function, after some investigation it turned out I couldnt change or alter any class variable - hence the new question.
AppDelegate.mm:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
....
HelloWorldLayer *layer = (HelloWorldLayer *) [[HelloWorldLayer scene].children objectAtIndex:0];
UIPanGestureRecognizer *gestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:layer action:#selector(handlePanFrom:)] autorelease];
[viewController.view addGestureRecognizer:gestureRecognizer];
}
HelloWorldLayer.h
#interface HelloWorldLayer : CCLayer
{
...
bool _ApplyForce;
}
HelloWorldLayer.mm
- (id)init {
if ((self=[super init])) {
_ApplyForce = false;
}
}
- (void)tick:(ccTime) dt {
_world->Step(dt, 10, 10);
if (_ApplyForce)
{
NSLog(#"apply force");
b2Vec2 force = b2Vec2(200, 200);
_body->ApplyLinearImpulse(force,_body->GetWorldCenter());
_ApplyForce = false;
}
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
_ApplyForce=true;
}
}

Isn't that because in your tick method if _ApplyForce is true the end of the statement sets it to false? Which would mean that immediately after it is true it is false.
I might recommend adding some NSLogs to track ticks and _ApplyForce states.

Related

-[CustomWindow hitTest:withEvent:] implementation to forward events

I have a custom window (that should be on top of everything, including the keyboard) to show an overlay thing, something like the overlay you see when pressing the volume up/down buttons in the device.
So I made a custom window OverlayWindow so far everything works fine and windows in the back are receiving their events normally. However hitTest:withEvent: is called several times and sometimes it even returns nil. I wonder if that is normal/correct? If not how can I fix that?
// A small (WIDTH_MAX:100) window in the center of the screen. If it matters
const CGSize screenSize = [[UIScreen mainScreen] bounds].size;
const CGRect rect = CGRectMake(((int)(screenSize.width - WIDTH_MAX)*0.5),
       ((int)(screenSize.height - WIDTH_MAX)*0.5), WIDTH_MAX, WIDTH_MAX);
overlayWindow = [[CustomWindow alloc] initWithFrame:rect];
overlayWindow.windowLevel = UIWindowLevelStatusBar; //1000.0
overlayWindow.hidden = NO; // I don't need it to be the key (no makeKeyAndVisible)
 
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// Find the front most window (with the highest window level) and
// call this method on that window. It should will make the event be
// forwarded to it
// Situation1: This method is called twice (or even more, it depend
// on the number of windows the app has) per event: Why? Is this the
// *normal* behaviour?
NSLog(#" ");
NSLog(#"Point: %# Event: %p\n", NSStringFromCGPoint(point), event);
UIView *view = nil;
if (CGRectContainsPoint(self.bounds, point)) {
NSLog(#"inside window\n");
NSArray *wins = [[UIApplication sharedApplication] windows];
__block UIWindow *frontMostWin = nil;
[wins enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"win: %#\n", obj);
if ([obj windowLevel] >= [frontMostWin windowLevel] && obj != self) {
frontMostWin = obj;
}
}];
NSLog(#"frontMostWindow:%#\n finding a new view ...\n", frontMostWin);
CGPoint p = [frontMostWindow convertPoint:point fromWindow:self];
view = [frontMostWindow hitTest:p withEvent:event];
// Situation2: sometimes view is nil here, Is that correct?
}
NSLog(#"resultView: %#\n", view);
return view;
}
EDIT:
Also I've noticed that
if hitTest:withEvent: always returns nil it works too. This is only when I call overlayWindow.hidden = NO;
if I call [overlayWindow makeKeyAndVisible] returning nil in hitTest:withEvent: does not always work. It looks like a key window requires a proper implementation of the hit testing method?
Am I missing something about event forwarding here?
Do frontMostWindow means frontMostWin?
It looks like even if we use only one UIWindow, hitTest:withEvent: will be executed on it at least 2 times. So, I guess it is normal.
You can receive null at
view = [frontMostWindow hitTest:p withEvent:event];
due to following reasons:
frontMostWindow is null itself (as example, if you have only one window)
p is ouside frontMostWindow bounds (as example, when frontMostWindow is keyboard and your touch is somewhere else)
frontMostWindow have property userInteractionEnabled set to NO;
hitTest:withEvent: being call several times is normal. This is probably because you only display a UILabel or UIImageView on the overlayed window and thus touches are automatically forwareded.
However I think you don't really need another OverlayWindow, instead you might consider a UIView on the top of the keyWindow. This should make your application cleaner...
I did face the same problem.
I tried to solve it based your post and another solution was found.
This code implemented in uiwindow overlaying on the top.
This code will pass events through the under window when area has no view.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(#" ");
NSLog(#"Point: %# Event: %p\n", NSStringFromCGPoint(point), event);
UIView *view = nil;
UIView *resultView = [super hitTest:point withEvent:event];
if (resultView == self) {
NSLog(#"touched in transparent window !!");
return nil;
}
NSLog(#"touched in view!!");
return resultView;
}
After all, Thanks. Your post is very helpful.

Swallow touches, unless touching a child of my current layer

I am writing a pause menu using a CCLayer. I need the layer to swallow touches so that you cannot press the layer below, however I also need to be able to use the buttons on the pause layer itself.
I can get the layer to swallow touches, but the menu won't work either.
Here is my code:
pauseLayer.m
#import "PauseLayer.h"
#implementation PauseLayer
#synthesize delegate;
+ (id) layerWithColor:(ccColor4B)color delegate:(id)_delegate
{
return [[[self alloc] initWithColor:color delegate:_delegate] autorelease];
}
- (id) initWithColor:(ccColor4B)c delegate:(id)_delegate {
self = [super initWithColor:c];
if (self != nil) {
NSLog(#"Init");
self.isTouchEnabled = YES;
CGSize wins = [[CCDirector sharedDirector] winSize];
delegate = _delegate;
[self pauseDelegate];
CCSprite * background = [CCSprite spriteWithFile:#"pause_background.png"];
[self addChild:background];
CCMenuItemImage *resume = [CCMenuItemImage itemFromNormalImage:#"back_normal.png"
selectedImage:#"back_clicked.png"
target:self
selector:#selector(doResume:)];
resume.tag = 10;
CCMenu * menu = [CCMenu menuWithItems:resume,nil];
[menu setPosition:ccp(0,0)];
[resume setPosition:ccp([background boundingBox].size.width/2,[background boundingBox].size.height/2)];
[background addChild:menu];
[background setPosition:ccp(wins.width/2,wins.height/2)];
}
return self;
}
-(void)pauseDelegate
{
NSLog(#"pause delegate");
if([delegate respondsToSelector:#selector(pauseLayerDidPause)])
[delegate pauseLayerDidPause];
}
-(void)doResume: (id)sender
{
if([delegate respondsToSelector:#selector(pauseLayerDidUnpause)])
[delegate pauseLayerDidUnpause];
[self.parent removeChild:self cleanup:YES];
}
- (void)onEnter {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
[super onEnter];
}
- (void)onExit {
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
return YES;
}
-(void)dealloc
{
[super dealloc];
}
#end
why dont you just disable touches on the game layer?
like in the onEnter method disable the touches on the game layer..and onExit re enable them
something like
-onEnter{
gameLayer.isTouchEnabled=NO;
....
}
-onExit{
gameLater.isTouchEnabled=YES;
...
}
also you wont need CCTouchDispatcher
According to your code, the problem is the modal layer is swallowing events even if it's for the own children.
To solve this problem, you have to set the touch priority of the children even higher than the modal layer itself.
In other words, set the menu's touch priority value below modal layer's.
There are two solutions.
Simply override "CCMenu::registerWithTouchDispatcher()" method and set the priority higher from the beginning.
Change menu's touch priority using "setPriority" method of the touchDispatcher or "setHandlerPriority" of menu itself.
To achieve second solution, you have to pay attention to the timing.
"CCMenu::registerWithTouchDispatcher()" is called somewhere AFTER "onEnter" and "onEnterTransitionDidFinish".
So, use "scheduleOnce" or something like that.
Sample codes.
- (id) initWithColor:(ccColor4B)c delegate:(id)_delegate {
self = [super initWithColor:c];
if (self != nil) {
//your codes...... put CCMenu in instance
[self scheduleOnce:#selector(setMenuPriority:) delay:0];
}
return self;
}
- (void) setMenuPriority (ccTime)dt {
[[[CCDirector sharedDirector] touchDispatcher] setPriority:INT_MIN forDelegate:menu];
//priority "INT_MIN" because you set the layer's priority to "INT_MIN+1".
}
PS: My english is not so good, so if there are loose sentences, correction will be very pleased :)
The problem is, that the layer/node hierarchy is not considered when propagating touches.
The touches are handed from the touch handler with the smallest priority value to the ones with the highest.
The touches are not forwarded anymore, once one of the responders swallows the touch.
You can use an approach similar to CCMenu. CCMenu handles all touches and detects which CCMenuItemhas been selected, based on the position of these items.
If you implement this the same way, you let your PauseLayer handle and swallow all touches and use a seperate mechanism to determine which child element in your PauseLayer has been selected.
Example Implementation: CCMenu Subclass
I have implemented a solution in this repository:
https://github.com/Ben-G/MGWU-Widgets/tree/master/Projectfiles/Components/CCMenuBlocking
That component is a CCMenuSubclass that swallows all touches and does not forward them.
Example Implementation: CCNode
Here is a more general solution of a CCNode that swallows touches and only forwards them to its own children:
https://github.com/Ben-G/MGWU-Widgets/blob/master/Projectfiles/Components/Popup/PopUp.m

Cocos2d-iPhone: "update" called after dealloc

So I have a subclass of a CCSprite object, and in its init method, I call:
[self scheduleUpdate]
I later release this object from its parent CCNode like so:
[self removeChild:sprite cleanup:YES];
In addition, I call [self unscheduleUpdate] in the sprite's dealloc method.
However, I'm getting a bad memory access, so it appears that the update method is still attempted after the object is released (I've narrowed it down to this, as it works perfectly if I comment out the [self scheduleUpdate] line.
Any ideas?
Found this post in an attempt to ask the same question. I tried unschedule update (within my init method as well) with no luck, but then realized that by moving the [self unscheduleUpdate]; to the actual update method (which is running continuously, unlike the init method) based on a condition it worked!
So, for those looking for copy paste, here's a progress bar example that I'm implementing from http://www.ccsprite.com/cocos2d/using-ccprogresstimer-cocos2d-example.html#HCB_comment_box
-(id) init
{
//initialize progress bar, make sure to add a file named green_health_bar.png to your
//resource folder
timer = [CCProgressTimer progressWithFile:#"green_health_bar.png"];
//set the progress bar type to horizontal from left to right
timer.type = kCCProgressTimerTypeHorizontalBarRL;
//initialize the progress bar to zero
timer.percentage = 0;
//add the CCProgressTimer to our layer and set its position
[self addChild:timer z:1 tag:20];
[timer setPosition:ccp(100, 280)];
[self scheduleUpdate];
}
and in your update method:
-(void)update:(ccTime)dt
{
//get progress bar
CCNode* node = [self getChildByTag:20];
timer.percentage += dt * 10;
if (timer.percentage >= 100)
{
[self gameOver]; //used to stop parallax and show gameover menu
[self unscheduleUpdate];
}
}
I usually don't allow forum reply emails, but feel free to ask questions via #russ152!
Hmm.. try not to use scheduleUpdate? I tried looking for self unscheduleUpdate but there is not such function in a CCNode.. You can try [self unscheduleAllselectors], which stops all selectors of the object , including the update selector, if you are not using the object anymore.. Or use custom selectors instead..

Why isn't my UILabel being changed?

Why isn't my UILabel being changed? I am using the following code, and nothing is happening:
- (void)awakeFromNib {
percentCorrect.adjustsFontSizeToFitWidth;
percentCorrect.numberOfLines = 3;
percentCorrect.minimumFontSize = 100;
}
Here is my Implemintation code:
- (void) updateScore {
double percentScore = 100.0 * varRight / (varWrong + varRight);
percentCorrect.text = [NSString stringWithFormat:#"%.2f%%", percentScore];
}
- (void)viewDidLoad {
percentCorrect.adjustsFontSizeToFitWidth = YES;
percentCorrect.numberOfLines = 3;
percentCorrect.minimumFontSize = 100;
percentCorrect.text = #"sesd";
}
- (void)correctAns {
numberRight.text = [NSString stringWithFormat:#"%i Correct", varRight];
}
-(void)wrongAns {
numberWrong.text = [NSString stringWithFormat:#"%i Incorrect", varWrong];
}
#pragma mark Reset Methods
- (IBAction)reset:(id)sender; {
NSString *message = #"Are you sure you would like to reset?";
self.wouldYouLikeToReset = [[UIAlertView alloc] initWithTitle:#"Reset?" message:message delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil];
[wouldYouLikeToReset addButtonWithTitle:#"Continue"];
[self.wouldYouLikeToReset show];
// Now goes to (void)alertView and see's what is being pressed!
}
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0)
{
NSLog(#"Cancel button pressed");
}
else
{
varRight = 0;
varWrong = 0;
[self wrongAns];
[self correctAns];
percentCorrect.text = [NSString stringWithFormat:#"0.0%%"];
}
}
#pragma mark Button Action Methods
- (IBAction)right:(id)sender; {
varRight++;
[self correctAns];
[self updateScore];
}
- (IBAction)wrong:(id)sender; {
varWrong++;
[self wrongAns];
[self updateScore];
}
- (IBAction)subOneRight:(id)sender {
if (varRight > 0 ) {
varRight--;
[self correctAns];
[self updateScore];
}
}
- (IBAction)subOneWrong:(id)sender {
if (varWrong > 0) {
varWrong--;
[self wrongAns];
[self updateScore];
}
}
-(IBAction)addHalfCredit:(id)sender;
{
varWrong++;
varRight++;
[self wrongAns];
[self correctAns];
[self updateScore];
}
#end
Any ideas?
Thanks
In order for the adjustsFontSizeToFitWidth setting to come into effect, the numberOfLines property must be set to 1. It won't work if it's != 1.
Are awakeFromNib, viewDidLoad, viewWillAppear being called at all?
The minimumFontSize property will do nothing if the text fits in the current bounds with the current font. Did you set the font property for the label?
percentCorrect.font = [UIFont systemFontOfSize:20];
Finally, isn't a minimumFontSize = 100 a little too big for a min font size?
Make sure everything is hooked up correctly. Make sure the IBOutlet for the UITextfield is setup and set break points within the method and see that the code is being touched. If it is, it's possible percentCorrect hasn't been hooked up correctly.
You shouldn't have to init your label if it is in the nib. If you are, then you created the label twice. So who knows which one you are messaging to. As soon as you initialized the label, you leaked the first one. So the label you have on screen is NOT the one you are manipulating in code.
Try placing your code in viewDidLoad instead. It should be initialized by then.
If that doesn't work, try viewDidAppear: simply to try to debug this.
It's possible that percentCorrect hasn't yet been initialized. Is percentCorrect equal to nil when that function is called, by any chance? If so, wait until after it's properly initialized to set its properties.
What are you expecting to happen? Does the label show when your code is commented out? How is percentCorrect defined in the nib?
Have you tried:
- (void)awakeFromNib {
percentCorrect.adjustsFontSizeToFitWidth = YES;
percentCorrect.numberOfLines = 3;
percentCorrect.minimumFontSize = 100;
percentcorrent.text = #"What is the text in percentCorrect?";
}
I had the same problem. Seems that setText doesn't automatically force a redraw when the change happens on a non-main thread. UI updates should always be done on the main thread to ensure responsiveness. There's another way to force it, using a selector:
label = [[UILabel alloc] init]; //assumes label is a data member of some class
...
(some later method where you want to update the label)
...
[label performSelectorOnMainThread:#selector(setText) withObject:#"New label value" waitUntilDone:false];
You may also get results from simply saying:
[label setNeedsDisplay];
which will force the update internally, but at the SDK's discretion. I found that didn't work for me, thus why I recommend the selector on the main thread.
What I found is sometimes , don't rely too much on IB , just add a line to set the frame :
labelx.frame=CGRectMake(labelx.frame.origin.x,labelx.frame.origin.y, 300, labelx.frame.size.height);
Then , autoresize works !

How to disable multitouch?

My app has several buttons which trigger different events. The user should NOT be able to hold down several buttons. Anyhow, holding down several buttons crashes the app.
And so, I'm trying to disable multi-touch in my app.
I've unchecked 'Multiple Touch' in all the xib files, and as far as I can work out, the properties 'multipleTouchEnabled' and 'exclusiveTouch' control whether the view uses multitouch. So in my applicationDidFinishLaunching I've put this:
self.mainViewController.view.multipleTouchEnabled = NO;
self.mainViewController.view.exclusiveTouch = YES;
And in each of my view controllers I've put this in the viewDidLoad
self.view.multipleTouchEnabled = NO;
self.view.exclusiveTouch = YES;
However, it still accepts multiple touches. I could do something like disable other buttons after getting a touch down event, but this would be an ugly hack. Surely there is a way to properly disable multi-touch?
If you want only one button to respond to touches at a time, you need to set exclusiveTouch for that button, rather than for the parent view. Alternatively, you could disable the other buttons when a button gets the "Touch Down" event.
Here's an example of the latter, which worked better in my testing. Setting exclusiveTouch for the buttons kind-of worked, but led to some interesting problems when you moved your finger off the edge of a button, rather than just clicking it.
You need to have outlets in your controller hooked up to each button, and have the "Touch Down", "Touch Up Inside", and "Touch Up Outside" events hooked to the proper methods in your controller.
#import "multibuttonsViewController.h"
#implementation multibuttonsViewController
// hook this up to "Touch Down" for each button
- (IBAction) pressed: (id) sender
{
if (sender == one)
{
two.enabled = false;
three.enabled = false;
[label setText: #"One"]; // or whatever you want to do
}
else if (sender == two)
{
one.enabled = false;
three.enabled = false;
[label setText: #"Two"]; // or whatever you want to do
}
else
{
one.enabled = false;
two.enabled = false;
[label setText: #"Three"]; // or whatever you want to do
}
}
// hook this up to "Touch Up Inside" and "Touch Up Outside"
- (IBAction) released: (id) sender
{
one.enabled = true;
two.enabled = true;
three.enabled = true;
}
#end
- (void)viewDidLoad {
[super viewDidLoad];
for(UIView* v in self.view.subviews)
{
if([v isKindOfClass:[UIButton class]])
{
UIButton* btn = (UIButton*)v;
[btn setExclusiveTouch:YES];
}
}
}
- (void)viewDidLoad {
[super viewDidLoad];
for(UIView* v in self.view.subviews)
{
if([v isKindOfClass:[UIButton class]])
{
UIButton* btn = (UIButton*)v;
[btn setExclusiveTouch:YES];
}
}
}
This code is tested and working perfectly for me.there is no app crash when pressing more than one button at a time.
Your app crashes for a reason. Investigate further, use the debugger, see what's wrong instead of trying to hide the bug.
Edit:
OK, ok, I have to admit I was a bit harsh. You have to set the exclusiveTouch property on each button. That's all. The multipleTouchEnabled property is irrelevant.
To disable multitouch in SWIFT:
You need first to have an outlet of every button and afterwards just set the exclusive touch to true.Therefore in you viewDidLoad() would have:
yourButton.exclusiveTouch = true.
// not really necessary but you could also add:
self.view.multipleTouchEnabled = false
If you want to disable multi touch throughout the application and don't want to write code for each button then you can simply use Appearance of button. Write below line in didFinishLaunchingWithOptions.
UIButton.appearance().isExclusiveTouch = true
Thats great!! UIAppearance
You can even use it for any of UIView class so if you want to disable multi touch for few buttons. Make a CustomClass of button and then
CustomButton.appearance().isExclusiveTouch = true
There is one more advantage which can help you. In case you want to disable multi touch of buttons in a particular ViewController
UIButton.appearance(whenContainedInInstancesOf: [ViewController2.self]).isExclusiveTouch = true
Based on neoevoke's answer, only improving it a bit so that it also checks subviews' children, I created this function and added it to my utils file:
// Set exclusive touch to all children
+ (void)setExclusiveTouchToChildrenOf:(NSArray *)subviews
{
for (UIView *v in subviews) {
[self setExclusiveTouchToChildrenOf:v.subviews];
if ([v isKindOfClass:[UIButton class]]) {
UIButton *btn = (UIButton *)v;
[btn setExclusiveTouch:YES];
}
}
}
Then, a simple call to:
[Utils setExclusiveTouchToChildrenOf:self.view.subviews];
... will do the trick.
This is quite often issue being reported by our testers. One of the approach that I'm using sometimes, although it should be used consciously, is to create category for UIView, like this one:
#implementation UIView (ExclusiveTouch)
- (BOOL)isExclusiveTouch
{
return YES;
}
Pretty much simple you can use make use of ExclusiveTouch property in this case
[youBtn setExclusiveTouch:YES];
This is a Boolean value that indicates whether the receiver handles touch events exclusively.
Setting this property to YES causes the receiver to block the delivery of touch events to other views in the same window. The default value of this property is NO.
For disabling global multitouch in Xamarin.iOS
Copy&Paste the code below:
[DllImport(ObjCRuntime.Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
internal extern static IntPtr IntPtr_objc_msgSend(IntPtr receiver, IntPtr selector, bool isExclusiveTouch);
static void SetExclusiveTouch(bool isExclusiveTouch)
{
var selector = new ObjCRuntime.Selector("setExclusiveTouch:");
IntPtr_objc_msgSend(UIView.Appearance.Handle, selector.Handle, isExclusiveTouch);
}
And set it on AppDelegate:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
...
SetExclusiveTouch(true); // setting exlusive to true disables the multitouch
...
}
My experience is that, by default, a new project doesn't even allow multitouch, you have to turn it on. But I suppose that depends on how you got started. Did you use a mutlitouch example as a template?
First of all, are you absolutely sure multitouch is on? It's possible to generate single touches in sequence pretty quickly. Multitouch is more about what you do with two or more fingers once they are on the surface. Perhaps you have single touch on but aren't correctly dealing with what happens if two buttons are pressed at nearly the same time.
I've just had exactly this problem.
The solution we came up with was simply to inherit a new class from UIButton that overrides the initWithCoder method, and use that where we needed one button push at a time (ie. everywhere):
#implementation ExclusiveButton
(id)initWithCoder: (NSCoder*)decoder
{
[self setExclusiveTouch:YES];
return [super initWithCoder:decoder]
}
#end
Note that this only works with buttons loaded from nib files.
I created UIView Class Extension and added this two functions. and when i want to disable view touch i just call [view makeExclusiveTouch];
- (void) makeExclusiveTouchForViews:(NSArray*)views {
for (UIView * view in views) {
[view makeExclusiveTouch];
}
}
- (void) makeExclusiveTouch {
self.multipleTouchEnabled = NO;
self.exclusiveTouch = YES;
[self makeExclusiveTouchForViews:self.subviews];
}
If you want to disable multitouch programmatically, or if you are using cocos2d (no multipleTouchEnabled option), you can use the following code on your ccTouches delegate:
- (BOOL)ccTouchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event {
NSSet *multiTouch = [event allTouches];
if( [multiTouch count] > 1) {
return;
}
else {
//else your rest of the code
}
Disable all the buttons on view in "Touch Down" event and enable them in "Touch Up Inside" event.
for example
- (void) handleTouchDown {
for (UIButton *btn in views) {
btn.enable = NO;
}
}
- (void) handleTouchUpInside {
for (UIButton *btn in views) {
btn.enable = Yes;
}
------
------
}
I decided this problem by this way:
NSTimeInterval intervalButtonPressed;
- (IBAction)buttonPicturePressed:(id)sender{
if (([[NSDate date] timeIntervalSince1970] - intervalButtonPressed) > 0.1f) {
intervalButtonPressed = [[NSDate date] timeIntervalSince1970];
//your code for button
}
}
I had struggled with some odd cases when dragging objects around a view, where if you touched another object at the same time it would fire the touchesBegan method. My work-around was to disable user interaction for the parent view until touchesEnded or touchesCancelled is called.
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
// whatever setup you need
self.view.userInteractionEnabled = false
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
// whatever setup you need
self.view.userInteractionEnabled = true
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
// whatever setup you need
self.view.userInteractionEnabled = true
}
A Gotcha:
If you are using isExclusiveTouch, be aware that overriding point(inside:) on the button can interfere, effectively making isExclusiveTouch useless.
(Sometimes you need to override point(inside:) for handling the "button not responsive at bottom of iPhone screen" bug/misfeature (which is caused by Apple installing swipe GestureRecognizers at the bottom of the screen, interfering with button highlighting.)
See: UIButton fails to properly register touch in bottom region of iPhone screen
Just set all relevant UIView's property exclusiveTouch to false do the trick.