I have implemented iCarousel (https://github.com/nicklockwood/iCarousel) into my Xcode project and now have a scrolling carousel of views. Inside each view I have a UIButton, which I have added a UILongPressGestureRecogniser to, like this:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] init];
[lpgr setMinimumPressDuration:1.5];
[lpgr addTarget:self action:#selector(testAction)];
[self.demoButton addGestureRecognizer:lpgr];
However, when I tap for 1.5 seconds, the following error is displayed in the console:
2014-07-01 09:50:08.002 ExampleApp[3117:892602] -[ExampleVC testAction]: message sent to deallocated instance 0x15cd7bd20
(lldb)*
I haven't seen any section of the code which is releasing the views, so very confused. Why is this and how can I fix it?
I think there can be a problem like this:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] init];
[lpgr setMinimumPressDuration:1.5];
[lpgr addTarget:self action:#selector(testAction)];
[self.demoButton addGestureRecognizer:lpgr];
You forgot to release lpgr after these lines.
[lpgr release];
And if you miss this line, and demo button is released by iCarussel because of memory optimization, your gestureRecognizer won't be released and will send a message to the deallocated demoButton.
And use the LongPress like this:
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
and handle:
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture{
if (gesture.state == UIGestureRecognizerStateEnded) {
//Do Whatever You want on End of Gesture
}
else if (gesture.state == UIGestureRecognizerStateBegan){
//Do Whatever You want on Began of Gesture
}
}
and don't forget to implement <UIGestureRecognizerDelegate>
Update:
Inside each view I have a UIButton
You have a button inside each view, but about your code I see that you have a class variable demobutton. So when you initialize a view add your demo button like this:
UIView* yourView ...
UIButton* demoButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
[yourView addSubview:demoButton];
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] init];
[lpgr setMinimumPressDuration:1.5];
[lpgr addTarget:self action:#selector(testAction:)];
[demoButton addGestureRecognizer:lpgr];
[demoButton release];
[lpgr release];
Related
We need to set a UIButton with 2 methods, the first one is when you touch it (down and up) you get one action , but when you long press it, you get another one.
For example, to get data about it when you long click, and when regular click , another thing.
How can i achieve this with UIButton ?
UIButton *CAT = [UIButton buttonWithType:UIButtonTypeCustom];
CAT.contentHorizontalAlignment=UIControlContentHorizontalAlignmentCenter;
CAT.backgroundColor=[UIColor clearColor];
[CAT addTarget:self action:#selector(newcat:)forControlEvents:UIControlEventTouchUpInside];
I have started with adding it a gesture with this
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[CAT addGestureRecognizer:longPress];
But this triggered only when you take off your finger .
i want it to be triggered while my finger is still there after 1-2 seconds .
Can i do that? and can i adjust the time required for it to trigger ?
You can use the event UIControlEventTouchDown, use delay time for long press and you must deal with UIControlEventTouchUpInside and UIControlEventTouchUpOutside. good luck!
this is normal method
UIButton *CAT = [UIButton buttonWithType:UIButtonTypeCustom];
CAT.contentHorizontalAlignment=UIControlContentHorizontalAlignmentCenter;
CAT.backgroundColor=[UIColor clearColor];
[CAT addTarget:self action:#selector(newcat:)forControlEvents:UIControlEventTouchUpInside];
UILongPressGestureRecognizer *longpressGesture =
[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(longPressHandler:)];
longpressGesture.minimumPressDuration = 5; // set the time interval
[longpressGesture setDelegate:self];
[CAT addGestureRecognizer:longpressGesture];
[self.view addsubview: CAT];
longpress action method
- (void)longPressHandler:(UILongPressGestureRecognizer *)gestureRecognizer {
NSLog(#"longPressHandler");
// do long press action
}
normal action method
-(void) newcat:(id)sender
{
// do normal action here
}
I'm trying to build a generic class with a couple of Gesture Recognizers, so that I can call one method and have both Recognizers added to the view
Here is my class implementation code
#import "saUIHelper.h"
#implementation saUIHelper
-(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
NSLog(#"pan");
}
-(void)handleLP:(UILongPressGestureRecognizer *)recognizer
{
NSLog(#"Test");
}
-(saUIHelper*)initWithView:(UIView*)view
{
self = [super init];
if(self){
self.myView = view;
self.myView.userInteractionEnabled = YES;
}
return self;
}
-(void)start
{
UIPanGestureRecognizer* panRecognizer =
[[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(handlePan:)];
[self.myView addGestureRecognizer:panRecognizer];
UILongPressGestureRecognizer* lpRecognizer =
[[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleLP:)];
[self.myView addGestureRecognizer:lpRecognizer];
}
#end
And I try to use it like this
UIImage *playImg = [UIImage imageNamed:#"playBtn"];
self.playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
self.playBtn.frame = CGRectMake(0.0f,0.0f, 60.0f, 60.0f);
self.playBtn.center = CGPointMake(viewWidth/2,(viewHeight/2)-35);
[self.playBtn setBackgroundImage:playImg forState:UIControlStateNormal];
[self.playBtn addTarget:self action:#selector(play:) forControlEvents:UIControlEventTouchUpInside];
[self.menu addSubview:self.playBtn];
saUIHelper* helper = [[saUIHelper alloc] initWithView:self.playBtn];
[helper start];
When I run this code and do a Pan or LongPressure gesture on the button I added, I get a crash with BAD_ACCESS_EXEC code
If however instead of calling [helper start]; I type in the same code as implemented in the start method, and add the handlePan: and handleLP: callbacks to the main class, I get no issues and everything runs fine
It's obvious I'm doing something very wrong here, not sure if it's threading issues or something else, can anyone give me some pointers?
Store a strong reference to saUIHelper* helper in your view/viewcontroller so that it doesn't get dealloced. You're probably getting a crash because the target is the helper, which might have been dealloced.
Today I tried to add an additional UILongPressGestureRecognizer to an UISwitch, which did not really work. My code is the following:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
[lpgr setDelaysTouchesBegan:YES];
[lpgr setDelaysTouchesEnded:YES];
[lpgr setCancelsTouchesInView:YES];
[self.switcher addGestureRecognizer:lpgr];
in the viewDidLoad method of the viewController that is the parent viewController of that switch. (the switcher is an instance variable, set through a storyboard. The IBOutlet is set properly from the UISwitch to the switcherProperty of its viewController).
On a couple of other controls (like a UIButton, UISlider, UIStepper and so on) this works, even without the touches cancelled or delayed, and perfectly triggers the target method. However, with my switch, it refuses that behavior.
I have tried removing any other gestureRecognizer on the UISwitch by iterating through all gestureRecognizers on that switch and calling [switcher removeGestureRecognizer: ... ], I´ve also set all of them to require my longPressGestureRecognizer to fail. Other types of gestureRecognizers don´t fire as well.
As far as I understand, GestureRecognizers will receive touches, before the view or control, which they belong to, receives them. Thus, setCancelsTouchesInView:YES and setDelaysTouchesInView:YES should enable the gestureRecognizer to handle every single gesture until it fails or succeeds, without the view knowing, right?
--------------EDIT------------------
The whole content of my method:
- (void)viewDidLoad
{
[super viewDidLoad];
UIGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.view addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.button addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.slider addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.segmentedControl addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.switcher addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.stepper addGestureRecognizer:lpgr];
}
As I said, for the other controls this is working, only the switch does not want to work
try with it
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == yourSwitch)) {//change it to your condition
return NO;
}
return YES;
}
make sure you confirm UIGestureRecognizerDelegate for it to work.
So I have a method then when called generates a simple UIView with some labels in it:
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 300, 250)];
view1.backgroundColor = [UIColor redColor];
view1.userInteractionEnabled = YES;
[self.view addSubview:view1];
I call this method 6 times so it places 6 UIViews (I give them different coordinates of course) around the screen.
How can I detect when a user swipes right on one of them and then trigger some other method?
I've tried something like this:
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(myLabel1Tap)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[view1 addGestureRecognizer:swipeRight];
and then a method:
- (void)myLabel1Tap {
}
But I'm not sure what to do inside that method, how can I know which view was swiped if they are all called the same 'view1'?
change the gesture recognizers selector to accept an argument (by adding a colon after the method signature)
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(myLabel1Swipe:)];
this will mean the gesture recognizer will get passed in, and then you can perform actions based on the gesture recognizers properties, e.g.
- (void)myLabel1Swipe:(UISwipeGestureRecognizer *)recogniser
{
UIView *swipedView = [recognizer view];
//do whatever you want with this view
}
I have this code in viewDidLoad:
UILongPressGestureRecognizer *change = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(dragGestureChange:)];
[imageView addGestureRecognizer:change];
for (UILongPressGestureRecognizer *gestureRecognizer in imageView.gestureRecognizers)
{
[gestureRecognizer requireGestureRecognizerToFail:change];
}
when I call the class where is this code, the first time it's all ok , but the second time it crash because EXC_BAD_ACCESS; it don't happens if instead of an imageView I use a scrollView, why???
With this code, you are requiring all recognizers (including change) to fail for change to succeed, which cannot be good
UILongPressGestureRecognizer *change = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(dragGestureChange:)];
[imageView addGestureRecognizer:change];
for (UILongPressGestureRecognizer *gestureRecognizer in imageView.gestureRecognizers)
{
[gestureRecognizer requireGestureRecognizerToFail:change];
}
you should change it to this
UILongPressGestureRecognizer *change = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(dragGestureChange:)];
for (UILongPressGestureRecognizer *gestureRecognizer in imageView.gestureRecognizers)
{
[gestureRecognizer requireGestureRecognizerToFail:change];
}
[imageView addGestureRecognizer:change];
And you should release change here. Don't know if that will actually solve your problem, can't see anything else wrong in what you posted.
Maybe try this: UIButtonLongTab. Use the Background-Image for your Images. I use this in an App within a ScrollView and it works without problem.