NavCtrl + TableViewCtrl + ScrollView, TableView not getting touches - iphone

I've got a NavCtrl and on top of that I have a TableView/ctrl, then I have a scrollview.
I've got everything working, except I can't seem to get touches to hit the Cell's on the table view.
I've tried the nextResponder things and everything seems to be doing the right thing. Here's some snippets of code, keep in mind this code has been changed a lot for testing so it isn't "clean" yet :):
ScrollView *myScrollView = [[ScrollView alloc] initWithFrame:CGRectMake(0, 150, 320, 680)];
myScrollView.contentSize = CGSizeMake( 320 , 888);
myScrollView.pagingEnabled = NO;
myScrollView.bounces = NO;
myScrollView.directionalLockEnabled = YES;
myScrollView.canCancelContentTouches = NO;
myScrollView.delaysContentTouches = NO;
myScrollView.scrollEnabled = true;
myScrollView.userInteractionEnabled = YES;
myScrollView.scrollsToTop = NO;
mySettingsTableView = [[SettingsTableView alloc] init];
// Settings View Ctrl
SettingsTableViewCtrl *mySettingsTableViewCtrl = [[SettingsTableViewCtrl alloc] initWithStyle:UITableViewStyleGrouped];
mySettingsTableViewCtrl.tableView.scrollEnabled = NO;
mySettingsTableViewCtrl.tableView.delaysContentTouches = NO;
mySettingsTableViewCtrl.tableView.canCancelContentTouches = NO;
mySettingsTableViewCtrl.view.userInteractionEnabled = YES;
[mySettingsTableViewCtrl.view addSubview:myScrollView];
[mySettingsTableViewCtrl.view addSubview:mySettingsTableView];
* In subclass of scrollView *
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//if (![self yourMethodThatDeterminesInterestingTouches:touches withEvent:event])
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
Here's the NSLog from the touch events:
NSLog(#"Event: %#", event);
NSLog(#"Event NextResponder : %#\n", self.nextResponder);
2011-07-05 13:09:36.063 App[1924:14f03] Event: <UITouchesEvent: 0x802ea30> timestamp: 8503.71 touches: {(
<UITouch: 0x80e6f60> phase: Began tap count: 1 window: <UIWindow: 0x836eb00; frame = (0 0; 320 480); layer = <CALayer: 0x836ebb0>> view: <ScrollView: 0x83770b0; baseClass = UIScrollView; frame = (0 150; 320 680); clipsToBounds = YES; layer = <CALayer: 0x8376e00>; contentOffset: {0, 0}> location in window: {140, 249} previous location in window: {140, 249} location in view: {140, 35} previous location in view: {140, 35}
)}
2011-07-05 13:09:36.065 App[1924:14f03] Event NextResponder : <SettingsTableViewCtrl: 0x8377db0>

I have the same problem with my code, and solved it by adding UITapGestureRecognizer to the view to which I want to handle touches.
In your case you should also add a UITapGestureRecognizer to either scroll view if you want to handle touches on it or table view if you want to handle touches on it as;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tappedScrollView)];
[tapRecognizer setNumberOfTapsRequired:1];
[myScrollView addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
and what you want to do on touch should be done in a method as;
-(void)tappedScrollView
{
//TO-DO what you want after Scroll view touched, DO it here.
}
Similarly you can do it for your table view as;
UITapGestureRecognizer *tapRecognizerForTableView = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tappedTableView)];
[tapRecognizerForTableView setNumberOfTapsRequired:1];
[mySettingsTableView addGestureRecognizer:tapRecognizerForTableView];
[tapRecognizerForTableView release];
and what you want to do on touch should be done in a method as;
-(void)tappedTableView
{
//TO-DO what you want after Table view touched, DO it here.
}

Here's what I ended up, not perfect yet but working, any tuning advice would be much appreciated......
//AppDelegate.m
ScrollView *myScrollView = [[ScrollView alloc] initWithFrame:CGRectMake(0, 140, 320, 680)];
myScrollView.contentSize = CGSizeMake( 320 , 888);
myScrollView.pagingEnabled = NO;
myScrollView.bounces = NO;
myScrollView.directionalLockEnabled = YES;
myScrollView.canCancelContentTouches = NO;
myScrollView.delaysContentTouches = NO;
myScrollView.scrollEnabled = true;
myScrollView.userInteractionEnabled = YES;
myScrollView.scrollsToTop = NO;
mySettingsTableViewCtrl = [[SettingsTableViewCtrl alloc] initWithStyle:UITableViewStyleGrouped];
mySettingsTableViewCtrl.tableView.scrollEnabled = NO;
mySettingsTableViewCtrl.tableView.delaysContentTouches = NO;
mySettingsTableViewCtrl.tableView.canCancelContentTouches = NO;
mySettingsTableViewCtrl.view.userInteractionEnabled = YES;
[mySettingsTableViewCtrl.view addSubview:myScrollView];
[mySettingsTableViewCtrl.view addSubview:mySettingsTableView];
//ScrollView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
//SettingsTableView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];
CGPoint location = [touch locationInView:self];
NSIndexPath *indexPathAtHitPoint = [self indexPathForRowAtPoint:location];
if ( [self cellForRowAtIndexPath:indexPathAtHitPoint] ) {
[self.delegate tableView:self didSelectRowAtIndexPath:indexPathAtHitPoint];
} else {
[super touchesBegan:touches withEvent:event];
}
}
Now I have a DatePicker on top of the scrollview and its working but this code needs some additional code to manage it, i.e. detect when picker is "touched" and when its not there. But that shouldn't be too difficult (I think ;)
Thanks for help and comments,
D

I think it might be overkill to use a ScrollView for your purpose. You can "scroll" any view. Position it so part or all of it is out of bounds, set clipping mode on the parent view, and when you want to "scroll" it, change its frame to in-bounds, with animation in effect. That is, if you are scrolling the DatePicker in in response to some other event (like adding an item to the table that is associated with a date). If the user scrolls the date picker in by dragging then carry on.
Combining a scroll view and a table view will be tricky, since a table view is a scroll view. It's ambiguous which should handle scrolling gestures. It might be possible but you probably have to write extra code to make the ambiguity explicit.

Related

Touch events (touchesMoved) not working on UIViews inside UIScrollView

I have a UIView inside a UIScrollView, and the UIViewControllers for those views are not receiving the touch events. If I take the views out of the scroll view then it works.
UserInteraction is default ON of all views but it's still not working!
This must be possible and I'd be really grateful if someone could point me in the right direction!
Many Thanks,
You can use the following method
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[yourView addGestureRecognizer:panRecognizer];
And to handle it
-(void)move:(id)sender {
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateChanged){
// This will return you location in view
CGPoint currentPoint = [sender locationInView:self.view];
// This will return you location in Scrollview
CGPoint scrollPoint = [sender locationInView:[[sender view] superview]];
}
}
add the following code to implementation file then touches moved
#interface UIScrollView(Custom)
#end
#implementation UIScrollView(Custom)
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
}
#end

Detect touch of UIImage added as subview of UIScrollView

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 :)

Selecting images and then moving them

I am trying to figure out how to select an object first then I am able to move it. For example I want to select cow first by touching it then I am able to move it. The reason being is when I am touching the screen it is moving both cow and cow1. When I only want to move one cow at a time. Any help will be appreciated.
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
cow.center = CGPointMake(touchLocation.x, touchLocation.y);
cow1.center = CGPointMake(touchLocation.x, touchLocation.y);
}
Try using UIPanGestureRecognizer, like this:
#synthesize myImage; //UIImageView
- (void)viewDidLoad
{
[super viewDidLoad];
[self startMoveImage];
}
-(void) startMoveImage{
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[self.view addGestureRecognizer:panGestureRecognizer];
}
- (void)pan:(UIPanGestureRecognizer *)gesture
{
if ((gesture.state == UIGestureRecognizerStateChanged) ||
(gesture.state == UIGestureRecognizerStateEnded)) {
CGPoint position = [gesture locationInView:[myImage superview]];
[myImage setCenter:position];
}
}
There are many ways to implement this sort of thing. It depends on how your graphics are implemented, amongst other things, check out this Apple sample code:
http://developer.apple.com/library/ios/#samplecode/Touches/Introduction/Intro.html
The standard solution would be to add separate gesture recognizers for your two cow objects, e.g., in viewDidLoad:
UIPanGestureRecognizer *panCow1 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCow:)];
[cow1 addGestureRecognizer:panCow1];
// if non ARC, make sure to add a line that says
// [panCow1 release];
UIPanGestureRecognizer *panCow2 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCow:)];
[cow2 addGestureRecognizer:panCow2];
// if non ARC, make sure to add a line that says
// [panCow2 release];
And then you have your moveCow method look something like:
- (void)moveCow:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translate = [sender translationInView:self.view];
sender.view.center = CGPointMake(sender.view.center.x + translate.x, sender.view.center.y + translate.y);
}
}
I haven't tested this code, but you get the idea. This is how I generally move separate subviews around...

touchesBegan doesnt get detected

I have a viewcontroller like the following. But the touchsBegan doestnt get detected.
Can anyone plz tell me what is wrong.
- (id)init
{
if (self = [super init])
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
return self;
}
-(void) viewWillAppear:(BOOL)animated
{
overlay = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"overlay.png"]] autorelease];
[self.view addSubview:overlay];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Detect touch anywhere
UITouch *touch = [touches anyObject];
// Where is the point touched
CGPoint point = [touch locationInView:self.view];
NSLog(#"pointx: %f pointy:%f", point.x, point.y);
// Was a tab touched, if so, which one...
if (CGRectContainsPoint(CGRectMake(1, 440, 106, 40), point))
NSLog(#"tab 1 touched");
else if (CGRectContainsPoint(CGRectMake(107, 440, 106, 40), point))
NSLog(#"tab 2 touched");
else if (CGRectContainsPoint(CGRectMake(214, 440, 106, 40), point))
NSLog(#"tab 3 touched");
}
Touches are only called on UIViews. This means you have to subclass UIView and put your touchesBegan:withEvent: code in this subclass. You're using the code inside an UIViewController and this doesn't get any touches itself because it's not an object on screen. A controller is just for the application logic behind a view.
As chriB said touches are detected only on views and as u access it in a controller its not detected.
also FYI, the touches wont be detected on any views you add over the base view.
ie: if u have a view and u add a scroll view or a table view over it, then the touches wont be detected over this added regions. same goes for buttons, textfields etc. Touch is detected only wen its done exactly on the base view.

Move an UIImageView around in a UIScrollView

I'm creating an application where I want to let the user move (not pan) an UIImageView around by dragging it on the screen. Additionally, I want the user to be able to zoom the UIImageView in and out.
As such I've been using a custom UIScrollView that forwards single touches to the 'contentView':
#implementation JM_UIScrollView
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
NSSet *allTouches = [event allTouches];
NSLog(#"Checking for touches: %d", [allTouches count]);
if ([allTouches count] == 1) {
return YES;
}
return NO;
}
#end
Along with a custom UIImageView that implements touchesBegan and touchesMoved to determine where to move the UIImageView:
#implementation JM_UIImageView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
if ([allTouches count] == 0)
return;
UITouch *firstTouch = [[allTouches allObjects] objectAtIndex: 0];
CGPoint touchLoc = [firstTouch locationInView: [self superview]];
touchOffset= CGPointMake(touchLoc.x-self.center.x,touchLoc.y-self.center.y);
NSLog(#"Currently at: %3.3f x %3.3f", touchLoc.x, touchLoc.y);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
if ([allTouches count] == 0)
return;
UITouch *firstTouch = [[allTouches allObjects] objectAtIndex: 0];
CGPoint touchLoc = [firstTouch locationInView: [self superview]];
if ([allTouches count] == 1)
{
if ([firstTouch view] == self)
{
touchLoc.x -= touchOffset.x;
touchLoc.y -= touchOffset.y;
NSLog(#"Moved to: %3.3f x %3.3f", touchLoc.x, touchLoc.y);
self.center = touchLoc;
}
}
}
#end
This is then all glued together:
scrollView = [[JM_UIScrollView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
scrollView.delegate = self;
scrollView.bouncesZoom = YES;
scrollView.scrollEnabled = NO;
scrollView.backgroundColor = [UIColor redColor];
scrollView.clipsToBounds = YES;
UIImage *image = [UIImage imageNamed:#"berg.jpg"];
imageView = [[JM_UIImageView alloc] initWithFrame: CGRectMake(0, 0, 140, 230)];
imageView.image = image;
imageView.center = CGPointMake(200,300);
[imageView setUserInteractionEnabled: YES];
[scrollView addSubview: imageView];
scrollView.contentSize = CGSizeMake(140,230);
scrollView.minimumZoomScale = 0.2;
scrollView.maximumZoomScale = 1.1;
[window addSubview: scrollView];
// Override point for customization after application launch
[window makeKeyAndVisible];
The problem:
When I start the application, I can move the UIImageView just fine and fluently. I can zoom-in and still be able to move it around.
However, it seems that whenever I zoom back out to the maximum level I seem then unable to move the UIImageView around anymore. It will jump at times, but by a maximum of 10 pixels. Use of NSLog() shows the touchesBegan/touchesMoved methods on the JM_UIImageView are no longer called.
Does anyone have any idea on what I might be missing here?
EDIT:
Would also accept an answer for wether or not this is the only way of implementing pinch-zooming with the zoombounce animation.