I have the following hierarchy:
VC -> UIView -> UISCrollView
the UIView happens to be the delegate of the UIScrollView, and owns the UIScrollView as an instance variable.
UIView .h:
#import <UIKit/UIKit.h>
#import "ImageScrollView.h"
#interface Scroller : UIView <UIScrollViewDelegate>{
ImageScrollView *scroller;
}
#end
.m:
#import "Scroller.h"
#implementation Scroller
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
DLog(#"");
[self initScroll];
}
return self;
}
-(void)initScroll{
DLog(#"");
scroller = [[ImageScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 200, 200)];
scroller.delegate = self;
[self addSubview:scroller];
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// Update the page when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.frame.size.width;
int page = floor((scroller.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
DLog(#"%d",page);
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
DLog(#"");
}
Note: as of now, none of the delegate methods get called. I see the UIScrollView on the iphone screen as a black box, however.
#import "ImageScrollView.h"
#implementation ImageScrollView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
DLog(#"%f %f",frame.size.width,frame.size.height);
[self configUI];
[self configSelf];
}
return self;
}
-(void)configUI{
self.backgroundColor = [UIColor blackColor];
self.contentSize = CGSizeMake(1000, 400);
self.showsHorizontalScrollIndicator = YES;
}
-(void)configSelf{
self.scrollEnabled = YES;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
DLog(#"");
for (UITouch *touch in touches) {
CGPoint _location = [touch locationInView:touch.view];
[self processTouch:_location];
}
[self.delegate scrollViewDidEndDecelerating:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
DLog(#"");
for (UITouch *touch in touches) {
CGPoint _location = [touch locationInView:touch.view];
[self processTouch:_location];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
DLog(#"");
for (UITouch *touch in touches) {
CGPoint _location = [touch locationInView:touch.view];
[self processTouch:_location];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
Note: none of the touchesEnded or touchesMoved methods get called either in the IUScrollViewSubclass.
What am I doing wrong?
This code works however if I assign the view controller as the delegate to the uiscrollview
If I implement your code, it works fine.
To check more closely what is happening, I added this line:
NSLog (#"%#",NSStringFromSelector(_cmd));
at the start of every method.
If I pan inside the scrollView this is the log:
ScrollViewTest1[4155:707] scrollViewDidScroll:
ScrollViewTest1[4155:707] 0
ScrollViewTest1[4155:707] scrollViewDidScroll:
ScrollViewTest1[4155:707] 0
ScrollViewTest1[4155:707] scrollViewDidScroll:
ScrollViewTest1[4155:707] 0
ScrollViewTest1[4155:707] scrollViewDidEndDecelerating:
Your touches methods don't get called as the touches are intercepted by the scrollView's built-in panGestureRecognizer.
If I tap inside the scrollView this is the log:
ScrollViewTest1[4155:707] touchesBegan:withEvent:
ScrollViewTest1[4155:707] processTouch:
ScrollViewTest1[4155:707] scrollViewDidEndDecelerating:
ScrollViewTest1[4155:707] touchesEnded:withEvent:
ScrollViewTest1[4155:707] processTouch:
Touches called as the scrollView isn't grabbing the touch events for it's own gestureRecognizer.
Related
I have UIScrollView with many UIImageViews.I need to drag and drop image from UIScrollView to another view. Outside the scrollView touch is worked. But inside scroll view touch is not worked. I used touchesBegan,touchesMoved etc method. Please help me.
-(IBAction)selectBut:(id)sender
{
scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(x,y,w,h)];
scrollView.userInteractionEnabled = YES;
int y = 0;
for (int i = 0; i < [myArray count]; i++) {
UIImageView *image = [[UIImageView alloc]initWithFrame:CGRectMake(0, y, 75, 30)];
image.userInteractionEnabled = YES;
y=y+35;
[scrollView addSubview:image];
}
[self.view addSubview:scrollView];
[scrollView setContentSize:CGSizeMake(150, 300)]
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch tapCount] == 1) {
NSLog(#"One touch !!");
}
}
you need to customize the UIImageView with your own view inherited from UIImageView. Provide touch methods in that customized subclass and add it on your UIScrollView..
Subviews of UIScrollview will never call touchesBegan method directly. You need to customized with subview to get touchesBegan properties of that added subview/customized view.
I mean to say take subclass of ImageView like
CustomImageView *imageView = [[CustomImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[imageView setUserInteractionEnabled:YES];
[scrollView addSubview:imageView];
[imageView release];
CustomImageView class is should be inherited from UIImageView like
#interface CustomImageView : UIImageView
{
}
in # .m File
#import "CustomImageView.h"
#implementation CustomImageView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"%s", __FUNCTION__);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch tapCount] == 2) {
drawImageView.image = nil;
return;
}
}
Add a Tap gesture recognizer to the scroll view for enabling touch inside scroll view:
UITapGestureRecognizer *singlTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(methodName:)];
singlTap.cancelsTouchesInView = NO; //Default value for cancelsTouchesInView is YES, which will prevent buttons to be clicked
[scrollViewName addGestureRecognizer:singlTap];
then write your code in specified method.
-(void)methodName:(UITapGestureRecognizer *)gesture
{
//your code here
}
I don't know if this help you. But try to set the exclusiveTouch property of the UIImageView to YES.
Did you add your scrollview as an IBOutlet? If its from xib and as you say touches are available outside the scroll and not within, I guess you might have accidentally unchecked userInteractionEnabled for the scrollview which inhibits touch events. And you have added UIImageViews as subviews to UIScrollView. For UIImageView you have to first set userInteractionEnabled to YES and then add any gestures if necessary to get events or use the touchesBegan, touchesMoved methods. Unless you set interaction enabled you wont receive touch events on UIImageView. If the parent view I mean the UIScrollView's interaction is disabled you wont get touches on UIImageView too. Hope this helps :)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Drawing on the iPhone in objective c
I have written this...
In the .h file:
#import <UIKit/UIKit.h>
#interface DrawView : UIView {
CGPoint gestureStartPoint,currentPosition;
CGContextRef c;
UIBezierPath *currentPath;
}
#property(nonatomic,retain)UIBezierPath *currentPath;
#end
And in the .m file:
#import "DrawView.h"
#implementation DrawView
#synthesize currentPath;
- (id)initWithFrame:(CGRect)frame {
[self drawRect:CGRectMake(0, 0, 320, 480)];
self = [super initWithFrame:frame];
if (self) {
currentPath = [[UIBezierPath alloc]init];
currentPath.lineWidth=3;
}
return self;
}
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] set];
[currentPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
currentPosition = [touch locationInView:self];
[currentPath addLineToPoint:(currentPosition)];
[self setNeedsDisplay];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self];
[currentPath moveToPoint:(gestureStartPoint)];
}
- (void)dealloc {
[super dealloc];
}
#end
I want to let the user draw an image on the iPhone screen and then use that image for the game... but this doesn't draw anything...
This is because your currentPath is not allocated.
If you instantiate the view in the resource [UIView initWithFrame:] will never be called.
Implement [UIView initWithCoder:] and allocate it there.
- (void)commonInit
{
currentPath = [[UIBezierPath alloc] init];
currentPath.lineWidth=3;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self commonInit];
}
return self;
}
Alternatively you can make the path in the touchesBegan if it is nil.
- (UIBezierPath*)currentPath
{
if (currentPath = nil)
{
currentPath = [[UIBezierPath alloc] init];
currentPath.lineWidth=3;
}
return currentPath;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self];
[[self currentPath] moveToPoint:(gestureStartPoint)];
}
However, there are some problems in the code.
You call [self drawRect:CGRectMake(0, 0, 320, 480)] before self = [super initWithFrame:frame]. You must not call any method there. You can write your code only inside
if (self ! =nil)
{
// your code
}
currentPath leaks. Release it in dealloc.
Never call drawRect directly. Call [UIView setNeedsDisplay] instead.
I have an UIViewController which contains a UITableView (subclassed) and another UIView (subclassed). They are on the same hierarchy level but the UIView is added last so it is the frontmost.
I overrid touchesBegan/Moved/Ended to intercept the Gestures from the top UIView: my goal is to get the selected UITableViewCell and, if double tapped, create an ImageView to be dragged around.
I appear to get it done but now I cannot scroll the UITableView anymore, even though I forward the touch events.
Here are the methods for the UIView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BO");
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesBegan:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"Mo");
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
copyObj.center = tPoint;
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesMoved:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
if ([touch tapCount] == 2)
{
[self desubCopyView];
}
CGPoint tPoint = [touch locationInView:self];
copyObj.center = tPoint;
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesEnded:touches withEvent:event];
//[cell.imageView touchesEnded:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesEnded:touches withEvent:event];
}
}
And here are those in the UITableViewCell
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
if ([touch tapCount] == 2)
{
CGPoint tPoint = [touch locationInView:self];
NSLog(#"CellX %lf CY %lf", tPoint.x, tPoint.y);
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView * newView = [[UIImageView alloc] initWithImage:viewImage];
[dragArea addSubview:newView];
dragArea.copyObj = newView;
[newView release];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
dragArea.copyObj.transform = CGAffineTransformMakeScale(1.3, 1.3);
[UIView commitAnimations];
tPoint = [self convertPoint:tPoint toView:dragArea];
dragArea.copyObj.center = tPoint;
}
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"MOV %#", self.imageView.image);
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"ENDED");
[super touchesEnded:touches withEvent:event];
}
And in my UITableView I have simply:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BEGTB");
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"MOVTB");
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"ENDTB");
[super touchesEnded:touches withEvent:event];
}
I am surely missing something but I do not know what
I would highly recommend looking into UITapGestureRecognizer instead of handling the touchesX events yourself.
I found a workaround for this, override the methods for touch gestures in my custom UITableView in order to make it scroll programMatically as I drag upon it an object.
Here is the 'solution'.
I still believe there is another simpler way to do this but I did not find it, so posting this and marking it as an 'answer' might help someone else.
I have a great problem since last 2 days. I'm working with a multiple Touch enabled view. My UIViewController has 8-10 imageview. I want to detect touch on each view separately, but multiple view at a time. Touch is detected on all image view, but problem is here-
Suppose I have Tap on a image view and hold down this image view and now tap another image view, second image view is detected touch successfully but it also trigger to first image view which is previously touched and hold down. But I don't want it. So please any one help me. Code level help is appreciated.
NB. My UIViewController also implemented TouchesMoved Methods for swiping purpose.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *touch in event.allTouches) {
if(CGRectContainsPoint([imView1 frame], [touch locationInView:self.view])){
NSLog(#"imView 1 touched");
}
if(CGRectContainsPoint([imView2 frame], [touch locationInView:self.view])){
NSLog(#"imView 2 touched");
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *touch in event.allTouches) {
if(CGRectContainsPoint([imView1 frame], [touch locationInView:self.view])){
NSLog(#"imView 1 touch moved");
}
if(CGRectContainsPoint([imView2 frame], [touch locationInView:self.view])){
NSLog(#"imView 2 touch moved");
}
}
}
Try subclassing a UIImageView and handle the touches code in that subclass. If you need to pass the touch to the parent for some reason, you can handle the touch then send it on to the parent which would be your view controller.
EDIT:
have your custom class like this
#interface CustomImageView : UIImageView {}#end
#implementation CustomImageView
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touch began %d",self.tag);
[self.nextResponder touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touch moved %d",self.tag);
[self.nextResponder touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touch ended %d",self.tag);
[self.nextResponder touchesEnded:touches withEvent:event];
}
#end
and have your parrent code something like this
#implementation trashmeTouchIpadViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"parent Touch Began");
//do parent stuff
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"parent Touch ended");
//do parent stuff
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setMultipleTouchEnabled:YES];
UIImage *i1 = [UIImage imageNamed:#"img1.jpg"];
CustomImageView *v1 = [[CustomImageView alloc] initWithImage:i1];
v1.frame = CGRectMake(100, 100, 200, 200);
v1.userInteractionEnabled = YES;
v1.multipleTouchEnabled = YES;
v1.tag = 111;
[self.view addSubview:v1];
[v1 release];
UIImage *i2 = [UIImage imageNamed:#"img2.jpg"];
CustomImageView *v2 = [[CustomImageView alloc] initWithImage:i2];
v2.frame = CGRectMake(500, 100, 200, 200);
v2.userInteractionEnabled = YES;
v2.multipleTouchEnabled = YES;
v2.tag = 999;
[self.view addSubview:v2];
[v2 release];
}
I've created a custom class AnimalView which is a subclass of UIView containing a UILabel and a UIImageView.
#interface AnimalView : UIView {
UILabel *nameLabel;
UIImageView *picture;
}
Then I added in several AnimalView onto the ViewController.view. In the touchesBegan:withEvent: method, I wanted to detect if the touched object is an AnimalView or not. Here is the code for the viewController:
#implementation AppViewController
- (void)viewDidLoad {
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:...
[self.view addSubview scrollview];
for (int i = 0; i<10; i++) {
AnimalView *newAnimal = [[AnimalView alloc] init];
// customization of newAnimal
[scrollview addSubview:newAnimal;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
UIView *hitView = touch.view;
if ([hitView isKindOfClass:[AnimalView class]]) {
AnimalView *animal = (AnimalView *)hitView;
[animal doSomething];
}
}
However, nothing happened when I clicked on the animal. When I checked the class of hitView by NSLog(#"%#", [hitView class]), it always shows UIView instead of AnimalView. Is it true that the AnimalView changed to a UIView when it is added onto the ViewController? Is there any way I can get back the original class of a custom class?
It's possible that AnimalView doesn't have user interaction enabled and is ignoring the touches. Try setting [myView setUserInteractionEnabled:YES];
Does an AnimalView have UIView subviews within it? They might be the ones picking up the touch events. You could disable the user interaction of those views by inverting the method described by David on all of the subviews of your AnimalView.
If you have subviews in your AnimalView, you can iterate through the superview hierarchy to see if the touch was handled by a subview of your AnimalView by using code like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
UIView *hitView = touch.view;
AnimalView *animal = nil;
while (hitView != nil) {
if ([hitView isKindOfClass:[AnimalView class]]) {
animal = (AnimalView *) hitView;
break;
}
hitView = [hitView superview];
}
if (animal) {
[animal doSomething];
}
}