Eric
Eric

Reputation: 16921

UIButtons dynamically added to UIScrollView not receiving touches

In my app, I have feature that allows the user to open a menu with thumbnails of all the app's pages, then tap on one and thus to a particular page. It's basically a UIScrollView to which I add a UIButton with a thumbnail image of each page.

The thumbnail menu works perfectly well if you open it on one of the pages in the middle of the app. However, there are many cases where, after opening the thumbnail menu, 90% of the scrollview's buttons don't receive touches. It usually happens when the user manually reaches the end of the app, then opens the thumbnail menu.

Here's the code:

@interface BookmarkManager : UIView{
    UIScrollView *thumbnailScrollView;
}
@property (strong) UIScrollView *thumbnailScrollView;


@implementation BookmarkManager

@synthesize thumbnailScrollView;

-(id)init{
    self = [super initWithFrame:CGRectMake(0, 0, 1024, 768)];
    if(self){
        [self setBackgroundColor:[UIColor clearColor]];

        thumbnailScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 120)];
        [thumbnailScrollView setBackgroundColor:[UIColor clearColor]];

        UIImageView *background = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/thumbnail_background.png", [[NSBundle mainBundle] resourcePath] ]]];
        [self addSubview:background];

        for(int i = 0; i < totalPages; i++){

            UIButton *pageThumbnail = [UIButton buttonWithType:UIButtonTypeCustom];
            [pageThumbnail setFrame:CGRectMake(0, 0, 125, 95)];
            [pageThumbnail setImage:[UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/p%d_thumb.png", [[NSBundle mainBundle] resourcePath], i]] forState:UIControlStateNormal];

            pageThumbnail.titleLabel.text = [NSString stringWithFormat:@"%d",i];

            pageThumbnail.center = CGPointMake(20 + (i * pageThumbnail.frame.size.width) + (i * 20) +(pageThumbnail.frame.size.width/2), 60);

            [thumbnailScrollView addSubview:pageThumbnail];
            [pageThumbnail addTarget:self action:@selector(thumbnailTapped:) forControlEvents:UIControlEventTouchDown];

        }

        [self addSubview:thumbnailScrollView];
        [thumbnailScrollView setContentSize:CGSizeMake(totalPages * 125 + (20*(totalPages+1)), 120)];

    }

    return self;
}


-(void)thumbnailTapped:(id)sender{

    UIButton *thumbnailButton = sender;
    int pageNumber = [thumbnailButton.titleLabel.text intValue];

    NSLog(@"tapped thumbnail for page: %d", pageNumber);
    [bookmarkManagerDelegate jumpToPage:pageNumber];

}

I've tried setting userInteractionEnabled = YES to pageThumbnail, tried thumbnailScrollView bringSubviewToFront:pageThumbnail, but that didn't work... The buttons are always perfectly well displayed, the problem is that there are many times where they don't receive touches (i.e. that NSLog never prints)... Why could that be?

EDIT: I implemented UIScrollView's scrollViewDidEndDecelerating, it gets called on the Simulator but never gets called on the device. Could it be that my button's images are too big?

Upvotes: 2

Views: 880

Answers (3)

Eric
Eric

Reputation: 16921

When I noticed that the scrollview's scrolling animation was super-smooth in the Simulator but slow on the device (an iPad 3), I tried implementing UIScrollViewDelegate.

On the Simulator, both scrollViewWillBeginDecelerating and scrollViewDidEndDecelerating were being called. On the device, scrollViewWillBeginDecelerating was being called but scrollViewDidEndDecelerating was never called.

The reason for this could be that, on the device, the scrollview has too much to process and is never done calculating where its content's final position is. Thus, it dismisses touches until it's done animating and repositioning the content. But this takes too long when the content is heavy.

So my solution was to kill the animation as soon as the scrollview begins decelerating. I simply implemented this:

-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
    CGPoint offset = scrollView.contentOffset;
    [scrollView setContentOffset:offset animated:NO];
}

Courtesy of this other StackOverflow answer.

Upvotes: 0

Javier Beltr&#225;n
Javier Beltr&#225;n

Reputation: 756

I've done something similar in the past, the only difference is the way you set the frame to your button. Maybe that is the problem

-(void)makeIconsView:(UIScrollView*)full :(NSArray*)images{
    int row=0;
    int column=0;
    for(int i = 0; i < images.count; ++i) {

        UIImage *thumb = [images objectAtIndex:i];
        UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];

        button.frame = CGRectMake(column*100+14, row*90+10, 84, 80);
        [button setImage:thumb forState:UIControlStateNormal];
        [button addTarget:self 
                   action:@selector(clickImage:) 
         forControlEvents:UIControlEventTouchUpInside];
        button.tag = i;
        button.layer.shadowColor = [UIColor colorWithWhite: 0.15 alpha: 0.55].CGColor;
        button.layer.zPosition = -8;
        button.layer.shadowOffset = CGSizeMake(0, 5);
        button.layer.shadowOpacity = 1;
        button.layer.shadowRadius = 2.0;
        button.clipsToBounds = NO;
        [full addSubview:button];

        if (column == 2) {
            column = 0;
            row++;
        } else {
            column++;
        }

    }

    [full setContentSize:CGSizeMake(320, (row+1) * 90 + 10)];   
}

Upvotes: 1

bkbeachlabs
bkbeachlabs

Reputation: 2171

I do the same thing in my app. Try using setBackgroundImage instead of setImage:. That works for me

Upvotes: 1

Related Questions