2

I have an iPhone app with a ScrollView which needs to respond to touch events.

In order to support touch events in the scrollview I needed to base it off a custom class which captured the touch events and passed them to the main view.

It works as expected when run in the 4.3 iPhone simulator. With the NSLOG entries I get output like this:

2011-10-17 09:28:06.782 SingleView[7000:b303] gameTable began
2011-10-17 09:28:06.784 SingleView[7000:b303] outside began
2011-10-17 09:28:09.976 SingleView[7000:b303] gameTable moved
2011-10-17 09:28:09.976 SingleView[7000:b303] outside moved
2011-10-17 09:28:09.977 SingleView[7000:b303] gameTable moved
2011-10-17 09:28:09.978 SingleView[7000:b303] outside moved
2011-10-17 09:28:10.019 SingleView[7000:b303] gameTable moved
2011-10-17 09:28:10.020 SingleView[7000:b303] outside moved
2011-10-17 09:28:10.046 SingleView[7000:b303] gameTable moved
2011-10-17 09:28:10.047 SingleView[7000:b303] outside moved
2011-10-17 09:28:10.077 SingleView[7000:b303] gameTable moved
2011-10-17 09:28:10.077 SingleView[7000:b303] outside moved
2011-10-17 09:28:10.143 SingleView[7000:b303] gameTable moved
2011-10-17 09:28:10.144 SingleView[7000:b303] outside moved
2011-10-17 09:28:12.433 SingleView[7000:b303] gameTable ended
2011-10-17 09:28:12.434 SingleView[7000:b303] outside ended

The gameTable is the event inside the custom class which just passes it to the outside view.

If you run the same app in the iOS 5 iPhone simulator you get output like this:

2011-10-17 09:41:46.319 SingleView[7077:f803] gameTable began
2011-10-17 09:41:46.322 SingleView[7077:f803] outside began
2011-10-17 09:41:47.851 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:47.852 SingleView[7077:f803] outside moved
2011-10-17 09:41:47.853 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:47.860 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:47.893 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:47.916 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:47.945 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:48.009 SingleView[7077:f803] gameTable moved
2011-10-17 09:41:48.062 SingleView[7077:f803] gameTable moved

Notice the outside moved event only fires once.

Here is the code. It's a single view project in Xcode.

//
//  XYZAppDelegate.h
//  SingleView
//

#import <UIKit/UIKit.h>

@class XYZViewController;

@interface XYZAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) XYZViewController *viewController;

@end



//
//  XYZAppDelegate.m
//  SingleView
//

#import "XYZAppDelegate.h"

#import "XYZViewController.h"

@implementation XYZAppDelegate

@synthesize window = _window;
@synthesize viewController = _viewController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[XYZViewController alloc] initWithNibName:@"XYZViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

@end


//
//  XYZViewController.h
//  SingleView
//

#import <UIKit/UIKit.h>
#import "GameTableScrollView.h"

@interface XYZViewController : UIViewController <UIScrollViewDelegate> {
    IBOutlet GameTableScrollView *outsideScrollView;

}

@property (nonatomic, retain) GameTableScrollView *outsideScrollView;

@end

    //
    //  XYZViewController.m
    //  SingleView
    //

    #import "XYZViewController.h"

    @implementation XYZViewController
    @synthesize outsideScrollView;

    #pragma mark - View lifecycle

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.

        [outsideScrollView setScrollEnabled:NO];
        [outsideScrollView setContentSize:CGSizeMake(480,555)];
    }


    // Handles the start of a touch
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"outside began");
    }

    // Handles the continuation of a touch.
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {  
        NSLog(@"outside moved");
    }


    // Handles the end of a touch event.
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"outside ended");
    }

    -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"outside cancelled");
    }

    @end

    //
    //  GameTableScrollView.h
    //

    #import <UIKit/UIKit.h>

    @interface GameTableScrollView : UIScrollView <UIScrollViewDelegate> {

    }

    @end

//
//  GameTableScrollView.m
//

#import "GameTableScrollView.h"

@implementation GameTableScrollView

#pragma mark - Touch 

// Handles the start of a touch
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"gameTable began");
    if (!self.dragging) {
        [self.nextResponder touchesBegan:touches withEvent:event];
    }
    else [super touchesBegan:touches withEvent:event];
}


// Handles the continuation of a touch.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{  
    NSLog(@"gameTable moved");
    if (!self.dragging) {
        [self.nextResponder touchesMoved:touches withEvent:event];
    }
    else [super touchesMoved:touches withEvent:event];
}


// Handles the end of a touch event.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"gameTable ended");
    if (!self.dragging) {
        [self.nextResponder touchesEnded:touches withEvent:event];
    } 
    else [super touchesEnded:touches withEvent:event];
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"gameTable cancelled");
    if (!self.dragging) {
        [self.nextResponder touchesCancelled:touches withEvent:event];
    } 
    else [super touchesCancelled:touches withEvent:event];
}

@end
Cœur
  • 37,241
  • 25
  • 195
  • 267
Johnne
  • 143
  • 10
  • The above code is just enough to reproduce the problem. Normally there is a UIImageView in the UIScrollView which the user will be dragging around. This is why I need to process the touch events. – Johnne Oct 17 '11 at 17:32
  • I am not sure if this is the answer, but you probably want to use GameTableScrollView in the XYZViewController.h file instead of using UIScrollView. Also, you may need to import the GameTableScrollView header, go into IB and change the class of the scroll view, and reconnect it back to the IBOutlet. – BP. Oct 17 '11 at 17:56
  • It was worth a shot,but sorry no luck. I have updated the code in the original post with your suggested changes so you can have a look. – Johnne Oct 17 '11 at 19:21
  • I have logged a bug with Apple as I feel this is not correct behavior. When (if) I get a response I will post the answer here. – Johnne Oct 20 '11 at 13:36
  • In the mean time I have found another way to accomplish it. I extended the UIScrollViewDelegate to include methods for my own versions of the touchevents. I then call these methods in my subclassed UIScrollView. – Johnne Oct 20 '11 at 13:40
  • I get the same issue with similar usage. Prior to iOS5 the touchesMoved event was passed to the parent view each time after being processed by the child view. Now, it is only fired once. I even tried adding an explicit call to nextResponder in the child to invoke the event in the parent view, but there was no nextResponder!! What ever happened to backwards compatibility... – Ellis Oct 30 '11 at 19:52
  • As mentioned before I logged a defect with Apple, they finally got back to me. And I quote: "After further investigation it has been determined that this is a known issue, which is currently being investigated by engineering.". They give the original bug ID #10213937. They give thanks for submitting the bug, but do not offer any workarounds. – Johnne Nov 04 '11 at 12:11
  • Perhaps `UIGestureRecognizer`s could be used to work around this. – Mark Adams Dec 09 '11 at 23:47
  • Johnne - I just hit this wall too. Thanks for the post - I was literally going crazy trying to figure out what was happening. Sure enough, when I tried it on the simulator in 4.3 (I'd been testing in 5.0) everything was fine. Any update on the bug or how you solved this? – crgt Dec 31 '11 at 07:11
  • I was having the same issue. then I saw this thread. IOS 5.0 Simulator had an issue with the touches. – Arcadian Jan 11 '12 at 07:00

1 Answers1

0

I recently posted an answer to a similar question.

In GameTableScrollView you have this code:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"gameTable began");
    if (!self.dragging) {
        [self.nextResponder touchesBegan:touches withEvent:event];
    }
    else [super touchesBegan:touches withEvent:event];
}

And to fix the problem you need to change this line:

[self.nextResponder touchesBegan:touches withEvent:event];

To this:

[[self.nextResponder nextResponder] touchesBegan:touches withEvent:event];

This will cause the GameTableScrollView to pass the touch event to the outside view.

Community
  • 1
  • 1
Steph Sharp
  • 11,462
  • 5
  • 44
  • 81