Make hidden UIImage appear with long tap - ios

In my project I would like to make a hidden image view appear when it is tapped for more than 3 seconds. I know I need to use NSTimer, but I have never created a UIImageView touch event. How can I combine the TapGestureRecognizer with NSTimer to achieve what I want to do? I am completely new to touch events in iOS, and I am just beginning to explore this. So, any help would be appreciated. Thank you!
UPDATE:
I implemented the UILongPressGestureRecognizer as below, but now, the hidden image appears even if I press somewhere outside of the image. How can I make it appear only if pressing the hidden image itself?
- (void)viewDidLoad
{
[super viewDidLoad];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPress:)];
longPress.numberOfTouchesRequired = 1;
longPress.minimumPressDuration = 3;
[self.view addGestureRecognizer:longPress];
}
-(void)handleLongPress:(UILongPressGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan)
{
BrokenGlass.hidden = NO;
}
}

You don't want a UITapGestureRecognizer and a timer, you want a UILongPressGestureRecognizer.

The image is appearing because you are using the gesture recognizer in the entire view.
[**self.view** addGestureRecognizer:longPress];
The gesture will not trigger on a hidden element.
Here is my solution:
#interface ViewController () <UIGestureRecognizerDelegate>
#property (nonatomic, strong) UIImageView *imageView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// create the imageView
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
// enable the user interaction in the imageView (otherwise it will not receive events)
_imageView.userInteractionEnabled = YES;
// add as a subview of the main view
[self.view addSubview:_imageView];
// create the gesture recognizer
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
longPressGesture.delegate = self;
longPressGesture.minimumPressDuration = 3;
// add the gesture to the imageView
[_imageView addGestureRecognizer:longPressGesture];
}
#pragma mark - UIGestureRecognizerDelegate
- (void)longPressHandler:(UILongPressGestureRecognizer *)gestureRecognizer {
// show the image
_imageView.image = [UIImage imageNamed:#"cat.jpeg"];
}

Related

Add image to subclassed UIImageView

I am trying to add a tap gesture from a subclassed UIImageView and then control the tap from the View Controller. I am not getting any compiling errors but "addSubview" is not displaying any image. How can make the UIImageView to be displayed?
If I try to control the tap and pan gestures from the subclassed UIImageVIew I have no problems but I would like to control these functions from the View Controller
Relevant code looks like this.
UIImageView subclass    
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
//self.userInteractionEnabled = YES;
previewController = [[PreviewController alloc]init];
[previewController self];
[self addSubview:character];
// Tap Initialization code
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:(PreviewController *)self.previewController action:#selector(addCharacter:)];
[self addGestureRecognizer:tap];
self.userInteractionEnabled = YES;
}
return self;
}
View Controller
- (void)addCharacter:(UITapGestureRecognizer *)t
{
NSLog(#"add character");
imageNSArray = [NSMutableArray array];
uiImg = [UIImage imageNamed:#"homer.png"];
CGPoint loc = [t locationInView:self.view];
character = [[UIImageView alloc] initWithImage:uiImg];
character.center = loc;
[imageNSArray addObject:character];
//Locate the imageNSArray on frameImageView area only.
[self.view addSubview:character];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panCharacter:)];
[self.character addGestureRecognizer:pan];
character.userInteractionEnabled = YES;
}
put a view behind your subclassed imageView. Apply the gestureRecognition to the new View.
You can control your tap gesture from new view. Let me know if i am not clear, or if more info needed.
I think what you want is.. .you want to show a image when user taps on screen.
Do following steps:
1. drag & drop a tap gesture recognizer on default view of your view controller.
2. connect (ctrl +) gestureRecognizer with ViewController.m
Take a look at this code. (Just modified your code)
- (IBAction)onTap:(UITapGestureRecognizer *)sender {
NSLog(#"add character");
UIImage * uiImg = [UIImage imageNamed:#"sel.png"];
CGPoint loc = [sender locationInView:self.view];
UIImageView * character = [[UIImageView alloc] initWithImage:uiImg];
character.center = loc;
[self.view addSubview:character];
}
Let me know if this is not what you want…
Edit
better go for this code… No need to do above steps.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTap:)];
[self.view addGestureRecognizer:tap];
self.view.userInteractionEnabled = YES;
}
- (void)onTap:(UITapGestureRecognizer *)sender
{
NSLog(#"add character");
UIImage * uiImg = [UIImage imageNamed:#"sel.png"];
CGPoint loc = [sender locationInView:self.view];
UIImageView * character = [[UIImageView alloc] initWithImage:uiImg];
character.center = loc;
[self.view addSubview:character];
}

identify touches on subclass of UIViews created programmatically in iOS

I am creating an app with cards. In my mainViewController, I have this code:
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
where CardView is a subclass of UIView. Each Card is a unique cardView and in CardView.m I do have:
#implementation CardView
{
UIImageView *_backImageView;
UIImageView *_frontImageView;
CGFloat _angle;
}
#synthesize card = _card;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
self.backgroundColor = [UIColor clearColor];
[self loadBack];
self.userInteractionEnabled=YES;
}
return self;
}
- (void)loadBack
{
if (_backImageView == nil)
{
_backImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_backImageView.image = [UIImage imageNamed:#"Back"];
_backImageView.contentMode = UIViewContentModeScaleToFill;
[self addSubview:_backImageView];
}
}
and the implementations for other functions .
Since in order to win space, one card is placed on top of the others (half of teh card is visible and the rest is covered by the next card and so on), I want to identify touches on each card.
If I place that code in CardView:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Card is touched");
}
it is never called. If I place it in the GameView Controller it is called anywhere that I will touch, but I do not know how to identify which cardView is called. Can you give me a hint?
EDIT:
I decided to use gestures. Therefore in my mainViewController changed the code to this:
for (PlayerPosition p = startingPlayer.position; p < startingPlayer.position + 4; ++p)
{
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
cardView.userInteractionEnabled=YES;
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
[cardView addGestureRecognizer:recognizer];
}
but this is never called.
-(void)cardSelected:(UITapGestureRecognizer *)recognizer
{
NSLog(#"Card Selected with gestures");
}
Why is that?
EDIT 2:
I have also tried adding this:
self.cardContainerView.userInteractionEnabled=YES;
or changing this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
to this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self .cardContainerView action:#selector(cardSelected:)];
but none of them worked.
If you instead add a tap gesture recogniser to each card view you can get a callback to a method you specify and the gesture is connected to the view so you can directly get a reference to it.
Your CardView (which I guess is a subclass of UIView?) has 2 subviews, which are image views:
UIImageView *_backImageView;
UIImageView *_frontImageView;
You may want to set userInteractionEnabled to YES on one or both of them (depending on when the card should be tappable and when the subviews are shown and hidden).
You can also act as the delegate of the gesture (if you need to, or just temporarily to debug that the gesture is getting triggered but blocked by something other gesture).

iOS touching a UIImageView

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
container = [[UIView alloc] initWithFrame:self.view.frame];
UIImageView* main_im0 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon1.png"]];
[container addSubview:main_im0];
[self.view addSubview:container];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *tt = [touches anyObject];
UIImageView *touchedview=(UIImageView*)[tt view];
NSArray *views = [container subviews];
for (UIImageView* im in views)
{
UIImageView* focus=im;
if (touchedview==focus)
{
}
}
}
I have this piece of code that setting up an ImageView called main_im0 which is put into a UIView container which then put into view. When I clciked on the main_im0, I expect the touch function would hit the condition of touchedview==focus. However, I couldn't get that condition activated? what's wrong?
Please enable
main_im0.userInteractionEnabled = YES;
by default it's No for UIImageView
Jason, maybe I am misinterpreting your code but aren't you going a round about way of achieving a tap gesture on a UIView?
You can use:
// enable user interaction with this view
main_img0.userInteractionEnabled = YES;
// setup a tap gesture to call a method once triggered (like a javascript mouseClick event)
UITapGesture *tapGesture = [[UITapGesture alloc] initWithTarget:self selector:#selector(myMethodToDoSomething)];
[main_img0 addGestureRecognizer:tapGesture];
// if you are not using Automatic Reference Counting, then remember to release your allocated tapGesture object
[tapGesture release];
...somewhere later in your code....
// method to do something
-(void)myMethodToDoSomething
{
NSLog(#"This method executed");
}
Now when you tap on your main_img0 view, the method "myMethodToDoSomething" will execute
By the way, if you just want a custom looking button with your own photoshop designed image, you can simply create a UIButton with code and set the background image property of the UIButton. Like so:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
[button setImage:[UIImage imageNamed:#"mybutton.png"] forControlState:UIControlStateNormal];

Attach GestureRecogniser to multiple imageviews

Something strange I encountered today while attaching same gesture recogniser to multiple image views. It gets attached to only the last one, in other words, it can be attached to only one view!
I had to create multiple gesture recognisers to meet my requirements.
Following is what I have done. Am I doing correct? Is that's the only way to attach recognisers to the multiple imageviews?
Please note that I don't want to use UITableView or UIVIew and put all imageviews in it and attach gesture recogniser to only UITableView or UIVIew. I have all image scattered and I have to detect which image is being dragged. Thanks.
[imgView1 setUserInteractionEnabled:YES];
[imgView1 setMultipleTouchEnabled:YES];
[imgView2 setUserInteractionEnabled:YES];
[imgView2 setMultipleTouchEnabled:YES];
[imgView3 setUserInteractionEnabled:YES];
[imgView3 setMultipleTouchEnabled:YES];
[imgView4 setUserInteractionEnabled:YES];
[imgView4 setMultipleTouchEnabled:YES];
[imgView5 setUserInteractionEnabled:YES];
[imgView5 setMultipleTouchEnabled:YES];
[imgView6 setUserInteractionEnabled:YES];
[imgView6 setMultipleTouchEnabled:YES];
//Attach gesture recognizer to each imagviews
gestureRecognizer1 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer1.delegate = self;
gestureRecognizer2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer2.delegate = self;
gestureRecognizer3 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer3.delegate = self;
gestureRecognizer4 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer4.delegate = self;
gestureRecognizer5 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer5.delegate = self;
gestureRecognizer6 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer6.delegate = self;
[imgView1 addGestureRecognizer:gestureRecognizer1];
[imgView2 addGestureRecognizer:gestureRecognizer2];
[imgView3 addGestureRecognizer:gestureRecognizer3];
[imgView4 addGestureRecognizer:gestureRecognizer4];
[imgView5 addGestureRecognizer:gestureRecognizer5];
[imgView6 addGestureRecognizer:gestureRecognizer6];
Yes, one view per gesture recognizer. So if you want only one recognizer, put it on the superview, e.g.:
UILongPressGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
[self.view addGestureRecognizer:gestureRecognizer];
And then, in your handler, you can:
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
if (sender.state == UIGestureRecognizerStateBegan)
{
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:[UIImageView class]] && CGRectContainsPoint(view.frame, location))
{
UIImageView *image = (UIImageView *) view;
// ok, now you know which image you received your long press for
// do whatever you wanted on it at this point
return;
}
}
}
}
By the way, if you do that, you don't need to worry about enabling user interaction on the images, either.
Finally, you don't need to worry about specifying your gesture recognizer's delegate unless you're going to conform to UIGestureRecognizerDelegate, which this isn't. Also note that I'm using a local var for my recognizer because there's no reason to hang onto it.
Update:
While the above code works fine, perhaps even better would be a custom long press gesture recognizer that would fail if the long press didn't take place over an image (this way it's more likely to play well in case you have other gesture recognizers taking place in your view). So:
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface ImageLongPressGestureRecognizer : UILongPressGestureRecognizer
#property (nonatomic, weak) UIImageView *imageview;
#end
#implementation ImageLongPressGestureRecognizer
#synthesize imageview = _imageview;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.imageview = nil;
[super touchesBegan:touches withEvent:event];
CGPoint location = [self locationInView:self.view];
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:[UIImageView class]] && CGRectContainsPoint(view.frame, location))
{
self.imageview = (UIImageView *)view;
return;
}
}
self.state = UIGestureRecognizerStateFailed;
}
#end
then create your gesture recognizer accordingly, using this new subclass:
ImageLongPressGestureRecognizer *gestureRecognizer = [[ImageLongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
[self.view addGestureRecognizer:gestureRecognizer];
and then, as a nice little benefit of this subclassing, your main gesture recognizer is simplified, namely:
- (void)handleLongPress:(ImageLongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
// you can now do whatever you want with sender.imageview, e.g. this makes it blink for you:
[UIView animateWithDuration:0.5
animations:^{
sender.imageview.alpha = 0.0;
} completion:^(BOOL finished){
[UIView animateWithDuration:0.5
animations:^{
sender.imageview.alpha = 1.0;
}
completion:nil];
}];
}
}
You can't attach a gesture recognizer to more than one object (as you discovered). One solution to what you are doing might be to subclass UIImageView and have setup code in that class so each view creates its recognizer, etc.
I guess, first of all, you should make an array of views and array of recognizers (mutable array, if needed) and then populate it. It will help you to use cycles to avoid code duplication.
As for multiple view with one recognizer - no, it's not possible, answered here.

UIGestureRecognizer in UIView Object

I have created a UIViewController, which contains a UIView object.
I want the UIView object response to a single tap by 1 finger, and also pinch by 2 fingers.
I have clicked the "User Interaction Enabled" and the "Multiple touch" options in the xib.
I create both UITapGestureRecognizer, and UIPinchGestureRecognizer inside the UIView function initWithFrame:, as it is the only entry point for the UIView object. But the object doesn't response to the UIGestureRecognizer objects. Why? Or, where is the best place to put the UIGestureRecognizer objects?
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Your problem is that when you instantiate your custom view subclass in IB, initWithCoder: is called. If you had done it programmatically using initWithFrame: it would have worked properly.
Two ways you can fix this,
Move the gestures setup to a different method and call them in both initWithFrame: and initWithCoder:. I would recommend doing this if you intend to reuse this component and expose some kind of callbacks on gestures.
If you want to implement this once and have a lot of interacting with the controller element, add them in viewDidLoad.
I tried your code and it works for me.
Where are you setting the view? Maybe it has any other view in front, or its superview has userInteractionEnabled disabled.
I created a UIView subclass and added this code:
-(void) handleSingleTap:(UITapGestureRecognizer *)gr {
NSLog(#"handleSingleTap");
}
-(void) handleDoubleMove:(UIPinchGestureRecognizer *)gr {
NSLog(#"handleDoubleMove");
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Then, in my controller:
-(void) viewDidLoad {
[super viewDidLoad];
MyView * myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
myView.backgroundColor = [UIColor greenColor];
[self.view addSubview:myView];
[myView release];
}
And it works.
i think if you are working with .xib, initialization can also be done in
-(void)awakeFormNib;
Adding the GestureRecognizers is typically done in the viewDidLoad method of the UIViewController.

Resources