Search code examples
iphoneobjective-cuiscrollviewaccessibilityvoiceover

UIAccessibility VoiceOver announces wrong page number for UIScrollView


I am trying to make an existing app as accessible as possible for voice over.

Currently, I have a uiviewcontroller thats basically a paging photo view with a uipagecontrol below the uiscrollView (tourScrollView) that indicates the current image/page being viewed.

here's the code that calculate's the current page:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    CGFloat pageWidth = scrollView.frame.size.width;
    self.tourScrollView.isAccessibilityElement = NO;
    scrollView.isAccessibilityElement = NO;
    int currentPage = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    pageControl.currentPage = currentPage;
}

the page calculation code works perfect.

There are a total of 5 images being shown.

With voice over enabled, when the scroll view scrolls, instead of going

page 1 of 5
page 2 of 5
page 3 of 5
page 4 of 5
page 5 of 5

it goes like this.

page 1 of 6
page 2 of 6
page 3 of 6
page 5 of 6
page 6 of 6

Here's the code where the images are added to the scrollView

-(void)addImagesToScrollview{

    NSArray *welcomeImages = [[NSArray alloc] initWithObjects:[UIImage imageNamed:@"img-01.png"],
                              [UIImage imageNamed:@"img-02.png"],
                              [UIImage imageNamed:@"img-03.png"],
                              [UIImage imageNamed:@"img-04.png"],
                              [UIImage imageNamed:@"img-05.png"],nil];

    CGRect scrollViewFrame = tourScrollView.frame;
    CGFloat scrollViewWidth = scrollViewFrame.size.width;
    CGFloat scrollViewHeight = scrollViewFrame.size.height;
    CGFloat imageX;
    for (int i = 0; i<[welcomeImages count]; i++) {

        int index = i;
        imageX = (scrollViewWidth*index) + (scrollViewWidth - IMAGE_WIDTH)/2.0;

        CGRect boarderViewRect = CGRectMake(imageX, 20.0f, IMAGE_WIDTH, IMAGE_HEIGHT);

        UIView *whiteBorderView = [[UIView alloc] initWithFrame:boarderViewRect];
        whiteBorderView.backgroundColor = [UIColor whiteColor];

        UIImageView *imageView = [[UIImageView alloc]initWithImage:[welcomeImages objectAtIndex:i]];
        CGRect imageRect = CGRectInset(boarderViewRect, IMAGE_INSET, IMAGE_INSET);
        imageView.frame = imageRect;

        CGRect descriptionRect = CGRectMake((scrollViewWidth*index) + 20.0f, imageRect.origin.y + imageRect.size.height+10, 280, 90);
        CGSize maximumLabelSize = CGSizeMake(descriptionRect.size.width,120);
        descriptionRect.size = [[self descriptionForIndex:i] sizeWithFont:[UIFont systemFontOfSize:16.0] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeTailTruncation];
        UILabel *imageDescription = [[UILabel alloc] initWithFrame:descriptionRect];
        imageDescription.text = [NSString stringWithFormat:@"%@",[self descriptionForIndex:i]];
        imageDescription.numberOfLines = 0;
        imageDescription.backgroundColor = [UIColor clearColor];
        imageDescription.font = [UIFont systemFontOfSize:16.0];
        imageDescription.textColor = [UIColor colorWithRed:(119.0/255.0) green:(119.0/255.0) blue:(119.0/255.0) alpha:1.0];
        imageDescription.textAlignment = UITextAlignmentCenter;
        imageDescription.shadowColor = [UIColor whiteColor];
        imageDescription.shadowOffset = CGSizeMake(0,1);

        [tourScrollView addSubview:whiteBorderView];
        [tourScrollView addSubview:imageView];
        [tourScrollView addSubview:imageDescription];

        if (i == [welcomeImages count]-1) {
            tourScrollView.contentSize = CGSizeMake(imageView.frame.origin.x + scrollViewWidth -((scrollViewWidth - IMAGE_WIDTH)/2.0), scrollViewHeight); 
        }
    }
}

I'd appreciate if someone points me to the right direction to make voice over say the correct page numbers.

update: Enabling/disabling pagingEnabled makes no difference. I think voiceOver overrides the paging calculations I do based on the scrollview size.


Solution

  • Here's what fixed it:

    removed the commented code, and added a line for the content size outside the for loop

    -(void)addImagesToScrollview{
    
    NSArray *welcomeImages = [[NSArray alloc] initWithObjects:[UIImage imageNamed:@"img-01-welcome.png"],
                              [UIImage imageNamed:@"img-02-welcome.png"],
                              [UIImage imageNamed:@"img-03-welcome.png"],
                              [UIImage imageNamed:@"img-04-welcome.png"],
                              [UIImage imageNamed:@"img-05-welcome.png"],nil];
    
    CGRect scrollViewFrame = tourScrollView.frame;
    CGFloat scrollViewWidth = scrollViewFrame.size.width;
    CGFloat scrollViewHeight = scrollViewFrame.size.height;
    CGFloat imageX;
    for (int i = 0; i<[welcomeImages count]; i++) {
    
        int index = i;
        imageX = (scrollViewWidth*index) + (scrollViewWidth - IMAGE_WIDTH)/2.0;
    
        CGRect boarderViewRect = CGRectMake(imageX, 20.0f, IMAGE_WIDTH, IMAGE_HEIGHT);
    
        UIView *whiteBorderView = [[UIView alloc] initWithFrame:boarderViewRect];
        whiteBorderView.backgroundColor = [UIColor whiteColor];
    
        UIImageView *imageView = [[UIImageView alloc]initWithImage:[welcomeImages objectAtIndex:i]];
        CGRect imageRect = CGRectInset(boarderViewRect, IMAGE_INSET, IMAGE_INSET);
        imageView.frame = imageRect;
    
        CGRect descriptionRect = CGRectMake((scrollViewWidth*index) + 20.0f, imageRect.origin.y + imageRect.size.height+10, 280, 90);
        CGSize maximumLabelSize = CGSizeMake(descriptionRect.size.width,120);
        descriptionRect.size = [[self descriptionForIndex:i] sizeWithFont:[UIFont systemFontOfSize:16.0] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeTailTruncation];
        UILabel *imageDescription = [[UILabel alloc] initWithFrame:descriptionRect];
        imageDescription.text = [NSString stringWithFormat:@"%@",[self descriptionForIndex:i]];
        imageDescription.numberOfLines = 0;
        imageDescription.backgroundColor = [UIColor clearColor];
        imageDescription.font = [UIFont systemFontOfSize:16.0];
        imageDescription.textColor = [UIColor colorWithRed:(119.0/255.0) green:(119.0/255.0) blue:(119.0/255.0) alpha:1.0];
        imageDescription.textAlignment = UITextAlignmentCenter;
        imageDescription.shadowColor = [UIColor whiteColor];
        imageDescription.shadowOffset = CGSizeMake(0,1);
    
        [tourScrollView addSubview:whiteBorderView];
        [tourScrollView addSubview:imageView];
        [tourScrollView addSubview:imageDescription];
    
    //        if (i == [welcomeImages count]-1) {
    //            tourScrollView.contentSize = CGSizeMake(imageView.frame.origin.x + scrollViewWidth -((scrollViewWidth - IMAGE_WIDTH)/2.0), scrollViewHeight); 
    //        }
        }
        tourScrollView.contentSize = CGSizeMake(320.0*[welcomeImages count], scrollViewHeight);
    }
    

    I am still not sure why VO would mess up the page numbers. The scrollview behavior, before and after the fix is still the same (bouncing and paging work identical).

    will update the answer if I get to know more about this issue.