I have written a simple custom gesture recognizer like this:
#interface myRecognizer : UIGestureRecognizer {
NSString * _name;
CGPoint _startPoint;
}
- (void)resetMe;
- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
#end
and I have initiated it like this:
myRecognizer *recognizer = [[myRecognizer alloc] initWithTarget:self action:#selector(didRecognize_myRecognizer:)];
[self.view addGestureRecognizer:recognizer];
[recognizer release];
so far so good. and I have my action here:
-(void)didRecognize_myRecognizer:(UIGestureRecognizer*)theRecognizer
{
//do stuff here
}
ok, this is working fine so far, and everything is good. But, here is my question, how can I get access to the _name property I created in my custom gesture in the //do stuff here arear? Basically, I would like to make many gestures, but only have a single method used for the actions, depending on the name of the gesture. In debugging mode I can clearly see the _name variable, but if I try writing code like this: theRecognizer._name, I'm told that there is no "_name" property in UIGEestureRecognizers. if I change the method to this:
-(void)didRecognize_myRecognizer:(myRecognizer*)theRecognizer
{
//do stuff here
}
the same thing happens. and, that wouldn't be good anyway because i want to use the method for all of the gesture recognizers i make. am I clueless? show me the light!
First off, you have to declare an accessor for the instance variable. Dot notation is for properties not instance variables. Declare name as property.
Then, You can check the class of the gesture recognizer and extract the name.
NSString * name = nil;
if ( [theRecognizer isMemberOfClass:[myRecognizer class]] ) {
name = [(myRecognizer *)theRecognizer name];
}
Related
I have many buttons in the view and 1 method with a different parameter for all of that buttons (when touched down)
Now I want to slide from a button to others that this method can be triggered for each button.
For example I slide from button 1 to button 5, the method can be triggered 5 times with parameter from 1 to 5.
I have many buttons, so I can't use touchesMoved.
These are the buttons:
First create your button list as a instance variable:
NSMutableArray *_buttonList;
in viewDidLoad:
_buttonList = [[NSMutableArray alloc] init];
Then:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[_buttonList removeAllObjects];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = touches.anyObject;
UIView *touchView = touch.view;
if ([touchView isKindOfClass:[UIButton class]]
&& ! [_buttonList containsObject:touchView])
{ // add any additionnal check you may want
[_buttonList addObject:touchView];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
for (UIButton *button in _buttonList) {
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
Add buttons in a view and add tags to all better in increasing order then(You can map array entry with a button) add add panGestureRecognizer in that view. On handler method check panGesture state if its began or ended. And then get location of pan.
Now you can loop over buttons and check which button frame lies at that position and you will know which button is at that position.
You can do this for both states to get start and end button, loop from startButtonTag to endButtonTag and UIButton *button = (UIButton*)[view viewWithTag:tag] and then can call method by sending that button as parameter.
I am trying to create a custom UIControl similar to a slider.
This control is to be the subview of a view that also has a tap gesture recognizer attached to it.
The problem now is that this tap gesture recognizer cancels the touches sent to my control. Is there a way I can override this from within the code of my control?
If I look into the standard controls in iOS it looks as if UIButton has a way of overriding the tap gesture recognizer but UISlider doesn't. So if I replace my custom control with a UIButton the tap gesture recognizer does not trigger its action, but if I replace it with a slider it does.
edit: I made a small project in Xcode to play around in. Download here https://dl.dropboxusercontent.com/u/165243/TouchConcept.zip and try to change it so that
The UICustomControl does not know about the tap gesture recognizer
The UICustomControl is not cancelled when the user taps down on the yellow box
The UICustomControl does not inherit from UIButton (that is a solution that does not feel right and might give me more headaches later on)
The code:
// inherit from UIButton will give the wanted behavior, inherit from UIView (or UIControl) gives
// touchesCancelled by the gesture recognizer
#interface UICustomControl : UIView
#end
#implementation UICustomControl
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesBegan"); }
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesMoved"); }
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesEnded"); }
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesCancelled"); }
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(logTap:)];
[self.view addGestureRecognizer:tapRecognizer];
UIView *interceptingView = [[UICustomControl alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
interceptingView.userInteractionEnabled = YES;
interceptingView.backgroundColor = [UIColor yellowColor];
[self.view addSubview: interceptingView];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) logTap: (id) sender
{
NSLog(#"gesture recognizer fired");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You can configure the gesture recognizer to not cancel touches in the view it's attached using the "cancels touches in view" property:
myGestureRecognizer.cancelsTouchesInView = NO;
I'm a little bit late, but for those (like me) stumbling into this question, I used an alternative solution:
Use the delegate of the gesture recogniser.
UITapGestureRecognizer *tapGestRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissInfoBox:)];
tapGestRec.delegate = self;
[self.view addGestureRecognizer:tapGestRec];
Then do a sort of hit test in the shouldReceiveTouch delegate function when the gesture recogniser wants to handle/swallow a touch.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint location = [touch locationInView:self.view];
return !CGRectContainsPoint(self.myCustomControl.frame, location) && !CGRectContainsPoint(self.myOtherCustomControl.frame, location);
}
I did all this in my ViewController, this way the UIControl does not have to know about any sibling views and the gesture recogniser does not 'steal' taps from my custom controls and only handles 'uncaught' taps.
Also, this way you won't trigger both the gesture recogniser and the custom control, which would happen with cancelsTouchesInView.
BTW, maybe it works with UIButton because UIButton uses gesture recognisers internally? I think they understand each other, while UIControls and recognisers do not. Not sure though.
override gestureRecognizerShouldBegin(_:) in your UIControl subclass.
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer.isKind(of: UITapGestureRecognizer.self) {
return false
} else {
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
}
You should follow this tutorial
http://www.raywenderlich.com/1768/uiview-tutorial-for-ios-how-to-make-a-custom-uiview-in-ios-5-a-5-star-rating-view
It shows you How to make a custom uiview.
And then do follow this one
http://www.raywenderlich.com/29474/ipad-for-iphone-developers-101-in-ios-6-custom-input-view-tutorial
I have some imageViews on my View, now I want to change the background.color of the image in my imageView. By clicking second time on the same imageView it should change to the first image.
How I can do this ?
Keep a BOOL or enum ivar to keep track of the image state. When reacting to the tap, exchange the image.
You could have a custom subclass of image view to keep this state always associated with the right object.
// .h
typedef enum { StateOriginal, StateFlipped } FlipState;
#interface FlippableImageView : UIImageView
#property (nonatomic, assign) FlipState state;
#end
// in view controller
if (flipImageView.state == StateOriginal) {
flipImageView.image = imageFlipped;
flipImageView.state = StateFlipped;
}
else {
flipImageView.image = imageOriginal;
flipImageView.state = StateOriginal;
}
The class could also hold both images and run a nice animation...
You can have a Variable to check how many times your ImageView has been touched.
This can be changed in :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if (touch.view == imageView) {
// Make that variable ++;
if (variable == 2){
imageView.image = [UIImage imageNamed:#"a.png"];
}
}
}
I have a custom UIControl which, when tapped, goes into a confirm state and, when tapped again, performs the desired action.
I want to have this control go back into its initial state if the user interacts anywhere else on the screen. Is there a non-invasive way to achieve this?
Clarification: I consider code invasive if I can't contain it within this control. I'd like to give the dev of another app this code, which they could use to add the control to their app, without having to mess around with code anywhere else in the app. If this isn't possible, fine, but the question is how to accomplish this non-invasively.
You can think of a UITapGestureRecognizer placed on top of everything that triggers the dismiss only when the touch happens outside of you UIControl bounds.
Something like
- (void)presentConfirm {
// whatever
self.dismissRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismiss:)];
self.dismissRecognizer = self;
[self.view addGestureRecognizer:self.dismissRecognizer];
}
- (void)dismiss:(UIGestureRecognizer *)gestureRecognizer {
// do stuff
[self.view removeGestureRecognizer:self.dismissRecognizer];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint touchPoint = [touch locationInView:self.view];
return !CGRectContainsPoint(self.control.frame, touch));
}
Basically you're triggering the dismiss method only when the touch happens outside the UIControl frame (I assumed your control is referenced as self.control).
Also you're going to need a dismissRecognizer property declared as
#property (nonatomic, strong) UITapGestureRecognizer *dismissRecognizer;
and to prevent warnings you should also declare that your controller conforms to the UIGestureRecognizerDelegate protocol.
You could try to accomplish this with the touchesBegan method:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if(myUIControl.isInConfirmState){ // check if your control is in confirmed state
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if(!CGRectContainsPoint(myUIControl.frame, touchLocation)){
// set myUIControl to it's initial state
}
}
}
Hope this helps.
I have problems detecting touch events on my UIImageView. I have sub classed UIImageView and overrided canBecomeFirstResponder, touchedBegan and touchesEnded:
#implementation ImageViewNext
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.userInteractionEnabled = YES;
}
return self;
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches Began event");
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches Ended");
}
I have ensured that all the parent views have User Interaction Enabled.
But I don't get my events?
I have tried to find the solution here but without any luck. But in my search I found that some people use "UIGestureRecognizer" to get the same effect. So as a side question, what is the "right" solution use the override method or use UIGestureRecognizer? I don't like the solution where a normal button is given an image, so please keep those post away :)
Are you sure your initWithFrame: is called? UIViews have two designated initializers, initWithFrame: and initWithCoder, depending on how they are created.
I don't know why you're not getting the touch events, but to answer the other part of your question using a UIGestureRecognizer will significantly simplify your touch handling for any of the gestures for which there's a recognizer available. It's much simpler to use and you'll need less code.
Furthermore, if in the future you need to negotiate between different touch events (is it a tap of a drag type questions) then all that logic is handled trivially.