Paulo Andrade

Keeper of Secrets.

twitter github stackoverflow linkedin email
UI Testing and NSScrollView
Apr 30, 2019
2 minutes read

If your Mac app uses NSScrollViews and you’re writing UI tests, eventually you’ll have the need to click a cell, button or view that is outside the scrollview’s view port.

Turns out the solution is quite simple but since this is a fairly common problem I felt it was worthy of a blog post.1.

@implementation XCUIElement (Additions)

- (BOOL)scrollRevealing:(XCUIElement *)element
{
    if(self.elementType != XCUIElementTypeScrollView){
        return NO;
    }

    CGRect scrollFrame = [self frame];
    CGRect elementFrame = [element frame];

    // figure out if we need to scroll up or down
    CGFloat direction = elementFrame.origin.y < scrollFrame.origin.y ? 1 : -1;
    
    while(!CGRectContainsRect(scrollFrame, elementFrame)){
        [self scrollByDeltaX:0 deltaY:direction * 50];
        
        CGRect newElementFrame = [element frame];
        if (CGRectEqualToRect(elementFrame, newElementFrame)) {
            // scrolling did not move the element
            // either the element is not on this scroll view or
            // we've scrolled the entire scrollview
            break;
        }
        elementFrame = newElementFrame;
    }
    
    return CGRectContainsRect(scrollFrame, elementFrame);
}

@end

The implemention is fairly easy to understand but here’s a quick run down:

  1. First we make sure we’re running on a XCUIElement of the scrollview type. This will only work if that’s the case. I tried finding a way to get the enclosing scroll view and make this work on any element but couldn’t. If you know of a way to do that please let me know!
  2. Then we need to figure out if the element is above or below the view port so we can scroll in that direction. Element frames are always in screen coordinates with the origin at the bottom left.
  3. Finally we scroll in increments of 50 points until either the element is visible (element’s frame is inside the scroll view’s frame)) or the element stops moving. If the element’s frame doesn’t change when we scroll, either the we’ve reach the top or bottom of the scroll view or the element is not contained by it.

Happy testing!


  1. At least I wish I’d found one when I had to implement this… ↩︎



Back to posts