Solutions using swipeUp()
and swipeDown()
are not ideal because they can potentially scroll past the target element due to the momentum of the swipe. After much searching and frustration I found a magical method on XCUICoordinate
:
func press(forDuration duration: TimeInterval, thenDragTo otherCoordinate: XCUICoordinate)
So we can do something like:
let topCoordinate = XCUIApplication().statusBars.firstMatch.coordinate(withNormalizedOffset: .zero)
let myElement = XCUIApplication().staticTexts["My Element"].coordinate(withNormalizedOffset: .zero)
// drag from element to top of screen (status bar)
myElement.press(forDuration: 0.1, thenDragTo: topCoordinate)
As far as checking whether something is visible goes, you want to use isHittable
in conjunction with exists
. see scrollDownToElement
in the extension below
Here's a handy extension that will scroll until an element is on screen and then scroll that element to the top of the screen :)
extension XCUIApplication {
private struct Constants {
// Half way accross the screen and 10% from top
static let topOffset = CGVector(dx: 0.5, dy: 0.1)
// Half way accross the screen and 90% from top
static let bottomOffset = CGVector(dx: 0.5, dy: 0.9)
}
var screenTopCoordinate: XCUICoordinate {
return windows.firstMatch.coordinate(withNormalizedOffset: Constants.topOffset)
}
var screenBottomCoordinate: XCUICoordinate {
return windows.firstMatch.coordinate(withNormalizedOffset: Constants.bottomOffset)
}
func scrollDownToElement(element: XCUIElement, maxScrolls: Int = 5) {
for _ in 0..<maxScrolls {
if element.exists && element.isHittable { element.scrollToTop(); break }
scrollDown()
}
}
func scrollDown() {
screenBottomCoordinate.press(forDuration: 0.1, thenDragTo: screenTopCoordinate)
}
}
extension XCUIElement {
func scrollToTop() {
let topCoordinate = XCUIApplication().screenTopCoordinate
let elementCoordinate = coordinate(withNormalizedOffset: .zero)
// Adjust coordinate so that the drag is straight up, otherwise
// an embedded horizontal scrolling element will get scrolled instead
let delta = topCoordinate.screenPoint.x - elementCoordinate.screenPoint.x
let deltaVector = CGVector(dx: delta, dy: 0.0)
elementCoordinate.withOffset(deltaVector).press(forDuration: 0.1, thenDragTo: topCoordinate)
}
}
Gist over here with added scrollUp
methods