How to add drag gesture to uiview in uiscrollview ios - ios

I'm trying to add the move gesture of uiview inside uiscrollview but I can not disable the srcoll event of uiscrollview. I have implemented the UIScrollview with paging enable in Main class. In another class, i added uiview in it, add gesture for it but i dont know how to disable scrolling of uiscrollview.
Please give me some advice. Thanks in advance.

You need to communicate from your UIView class with gesture in it by means of delegate to the main class asking scroll view to stop scrolling and later on enabling it. I have enclosed the code.
Your UIView.h file
#protocol MyUIViewProtocol <NSObject>
- (void)setScrollViewScrollEnabled:(BOOL)enabled;
#end
#interface MyUIView : UIView
#property (weak, nonatomic) id<MyUIViewProtocol> delegate;
#end
Your UIView.m file
#implementation MyUIView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor:[UIColor redColor]];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGestureMade:)];
[self addGestureRecognizer:panGesture];
}
return self;
}
- (void)panGestureMade:(UIPanGestureRecognizer *)recognizer
{
CGPoint pointsToMove = [recognizer translationInView:self];
[self setCenter:CGPointMake(self.center.x + pointsToMove.x, self.center.y + pointsToMove.y)];
[recognizer setTranslation:CGPointZero inView:self];
//Disable the scroll when gesture begins and enable the scroll when gesture ends.
if (self.delegate && [self.delegate respondsToSelector:#selector(setScrollViewScrollEnabled:)]) {
if (recognizer.state == UIGestureRecognizerStateBegan) {
[self.delegate setScrollViewScrollEnabled:NO];
}
else if (recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateEnded) {
[self.delegate setScrollViewScrollEnabled:YES];
}
}
}
Your main class file with scroll view in it.
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 568)];
[self.scrollView setBackgroundColor:[UIColor yellowColor]];
[self.scrollView setPagingEnabled:YES];
[self.scrollView setContentSize:CGSizeMake(320 * 3, 568)];
[self.view addSubview:self.scrollView];
MyUIView *view = [[MyUIView alloc] initWithFrame:CGRectMake(40, 100, 100, 100)];
view.delegate = self;
[self.scrollView addSubview:view];
}
- (void)setScrollViewScrollEnabled:(BOOL)enabled
{
[self.scrollView setScrollEnabled:enabled];
}
Hope this answer helps you.

Related

Add a UITapGestureRecognizer to a subview

I have been searching for hours ... Needless to say, I'm rather new in programming.
My goal is to display a subview programmatically and to remove it by tapping on it.
Already working: Displaying the subview and removing it by tapping on the view (code below).
Doesn't work - whatever I try: Removing the subview by tapping exclusively on the subview.
Thanks for your help!
My .h-file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
UIImageView *canvas;
UIImageView *square;
}
#property (nonatomic, strong) UIImageView *canvas;
#property (nonatomic, strong) UIImageView *square;
#property (nonatomic) NSUInteger numberOfTouchesRequired;
- (void)handleSingleTap: (UITapGestureRecognizer *)singleTapGestureRecognizer;
#end
My .m-file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize canvas;
#synthesize square;
- (void)handleSingleTap: (UITapGestureRecognizer *)singleTapGestureRecognizer {
NSLog(#"tapGesture");
[square removeFromSuperview];
}
- (void)loadView {
canvas = [[UIImageView alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]];
[canvas setImage:[UIImage imageNamed:#"canvas.png"]];
self.view = canvas;
square = [[UIImageView alloc] initWithFrame:CGRectMake(170, 320, 40, 40)];
[square setImage:[UIImage imageNamed:#"square.png"]];
[self.view setUserInteractionEnabled:YES];
[canvas addSubview:square];
NSLog(#"square");
}
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
NSLog(#"initialize");
singleTapGestureRecognizer.numberOfTapsRequired = 1;
NSLog(#"number of taps");
[self.view addGestureRecognizer:singleTapGestureRecognizer];
NSLog(#"square gestureRecognizer");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
If you want to remove a subview exclusively, when you tap on that view, then you will need to add tap gesture on that view
[square addGestureRecognizer:singleTapGestureRecognizer];
rather than
[self.view addGestureRecognizer:singleTapGestureRecognizer];
Edit
Also you will need to setUserInteractionEnabled to YES of UIImage because UIImage's setUserInteractionEnabled is default to NO.
[square setUserInteractionEnabled:YES];
Instead of
[self.view addGestureRecognizer:singleTapGestureRecognizer];
you should say
[square addGestureRecognizer:singleTapGestureRecognizer];

Using UITapGestureRecognizer inside a UIPageViewControllerDataSource not working

I have been trying to add a UITapGestureRecognizer in order to make it possible to change views using buttons inside my UIPageViewControllerDataSource.
I have added the subclasses
#interface OverallViewController : UIViewController <UIPageViewControllerDataSource,UIGestureRecognizerDelegate>
#property (strong, nonatomic) UIPageViewController *pageController;
Page navigation works fine for me but when trying the following UITapGestureRecognizer inside viewDidLoad I get nothing
UIView *topbar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 52)];
topbar.backgroundColor = [UIColor colorWithRed:(245/255.0) green:(245/255.0) blue:(245/255.0) alpha:1];
left = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"nav-profile-sm.png"]];
left.frame = CGRectMake(15, 15, 25, 25);
//End add top nav
[topbar addSubview:left];
[self.view addSubview:topbar];
//Add top navigation gestures
UITapGestureRecognizer *profiletap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(profileButtonTap)];
profiletap.numberOfTapsRequired = 1;
profiletap.delegate = self;
[left addGestureRecognizer:profiletap];
No response here
-(void) profileButtonTap {
NSLog(#"Profile button tapped");
}
And nothing when I try overriding gestureRecognizer
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint touchPoint = [touch locationInView:touch.view];
NSLog(#"Touch: %f,%f", touchPoint.x, touchPoint.y);
if (touchPoint.y < 52) {
return NO; // ignore the touch
}
return YES; // handle the touch
}
When I say that I get nothing I mean that nothing is logged, the methods do not seem to be called and I am unsure how I can solve or debug this further. Thanks for any help, ideas and suggestions!

make some drag-able UIView

I'd like to make some drag-able UIView.
But in case of following code, you can drag only one UIView you have added at the last.
I have this code.
LabelView class is subclass of UIView.
ViewControlle.h
#interface ViewController : UIViewController
#property (nonatomic, strong) LabelView* labelView;
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_labelView = [[LabelView alloc]initWithFrame:CGRectMake(100, 200, 200, 50)];
[self.view addSubview:_labelView];
UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(panAction:)];
[_labelView addGestureRecognizer:pan];
UIButton* addButton = [[UIButton alloc]initWithFrame:CGRectMake(270, 60, 40, 40)];
[addButton addTarget:self action:#selector(newLabelView) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:addButton];
}
- (void)panAction:(UIPanGestureRecognizer *)sender
{
CGPoint p = [sender translationInView:self.view];
CGPoint movedPoint = CGPointMake(_labelView.center.x + p.x, _labelView.center.y + p.y);
_labelView.center = movedPoint;
[sender setTranslation:CGPointZero inView:self.view];
}
- (void)newLabelView
{
_labelView = [[LabelView alloc]initWithFrame:CGRectMake(20, 60, 200, 50)];
[self.view addSubview:_labelView];
UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(panAction:)];
[_labelView addGestureRecognizer:pan];
}
You should apply the getsture for all the view. and then change the methods. _labelView.center = movedPoint; to [sender view].center = movedPoint;
- (void)panAction:(UIPanGestureRecognizer *)sender
{
CGPoint p = [sender translationInView:self.view];
CGPoint movedPoint = CGPointMake(_labelView.center.x + p.x, _labelView.center.y + p.y);
[sender view].center = movedPoint;
[sender setTranslation:CGPointZero inView:self.view];
}
You could create a mutable array, and then add each new view into that array. You do this by adding:
NSMutableArray *viewsArray = [[NSMutableArray alloc] init];
// after creating the custom view
[viewsArray addObject:customView];
Then, you can add a pan gesture on top of the views in the array, that way they are all using the pan gesture
for(LabelView *l in viewsArray) {
[l addGestureRecognizer:panGestureRecognizer];
}
this will add a UIPanGestureRecognizer to all the custom views in the array that you created in the beginning of this answer.
Hope this helps!

Get touches when userinteractionenabled is NO

There seems to be an answer, but when I try the method provided, it just doesn't work!
#interface MyView : UIView
#end
#implementation MyView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
NSLog(#"hitView:%#", hitView);
if (hitView == self) {
return nil;
}
return hitView;
}
- (void)testUserInteraction
{
UIViewController *vc = [[UIViewController alloc] init];
self.window.rootViewController = vc;
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 320, 568)];
myView.userInteractionEnabled = NO;
[vc.view addSubview:myView];
UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
subView.backgroundColor = [UIColor orangeColor];
[myView addSubview:subView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped)];
[subView addGestureRecognizer:tap];
}
When myView.userInteractionEnabled = YES; everything works fine. but when myView.userInteractionEnabled = NO; wherever I tapped the screen, it just output hitView:(null)
So is this method no longer working, or did I miss something?
yes, because you disabled user interaction.
When you call super-hittest,the return from super-hittest depends on userInteraction property.
If it is enabled then only, it returns either itself or some subView.
If not enabled, it will always return nil.
If I understand correctly, you want to know when a touch happens inside your custom UIView, while user interaction on the view is disabled. If this is so, throw the following inside the - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event method. This condition should read only touch events inside your custom view
if ([self pointInside:point withEvent:event]) {
NSLog(#"TOUCH IS INSIDE THE VIEW");
}

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.

Resources