Jaggy, Laggy, low-fps scrolling on iPhone 13 Pro (Pro Motion Display) for .offset

I created a horizontally scrolling view modifier that essentially wraps the contents and applies a horizontal offset based on a DragGesture GestureState.

It looks a little bit like this

@GestureState private var dragOffset: CGFloat = 0
content  
.offset(x: dragOffset, y: 0)
  .simultaneousGesture(DragGesture(minimumDistance: 20, coordinateSpace: .global)
  .updating($dragOffset) { (value, gestureState, transaction) in
                            gestureState = value.translation.width
                        })

The problem I am seeing is that on the Pro Motion Display of the iPhone 13 Pro, the "offset" view does not smoothly slide along with your finger as it drags. Instead is is very jaggy or laggy, probably rendered with maybe 15 fps or so. On the iPhone 11 Pro I used before, the drag is very smooth and on the iOS 15 simulator it is smooth as well.

What is wrong here? Thanks for help!

Post not yet marked as solved Up vote post of scurra Down vote post of scurra
4.3k views
  • I posted this as FB9657760

Add a Comment

Replies

I have the same stutter scrolling on a pro-motion device when using a regular UIScrollview and a content offset. Older devices work fine, turning off pro-motion on the iphone 13 stops it too.

  • Please file a radar as well and reference FB9657760 to make this more prominent, thanks! So this seems to be bug indeed and not much one can do about it. :(

Add a Comment

If anyone comes here for a solution. I still believe this is a bug in DragGesture but there is workaround possible though CADisplayLink. (See here: https://developer.apple.com/documentation/quartzcore/optimizing_promotion_refresh_rates_for_iphone_13_pro_and_ipad_pro)

Unfortunately, you will have to create a helper object to setup a display link, because you can't have @objc functions in structs. I created:

@available(iOS 15.0, *)
class RefreshRateHelper {

    static let shared = RefreshRateHelper()
    
    private var displayLink:CADisplayLink? = nil

   init() {
        displayLink = CADisplayLink(target: self, selector: #selector(ignore))
        displayLink?.add(to: .current, forMode: .default)
    }

    @objc
    func ignore(link: CADisplayLink) {
    }

    func preferredFrameRateRange(_ range:CAFrameRateRange){
        displayLink?.preferredFrameRateRange = range
    }        

}

This very hacky. It basically is a globally accessible display link that you give a preferred framerate. I ended up setting a high framerate in .updating and resetting it to .default in onEnded.

I hope this helps someone.

  • This works! Also remember to set the info.plist key "CADisableMinimumFrameDurationOnPhone" to true.

Add a Comment

I don’t think it’s related to pro display. There’s a lag and lower frame rate with DragGesture compared to PanGesture. Filled FB9781201

I'm seeing a similar issue.

FWIW I'm experiencing this just using the offset() in SwiftUI, no scroll views or gestures involved. On the iPhone 13 pro the updates run horribly (fps seems to degrade over time?), yet iPhone X and other non 120hz phones are updating at rock solid 60fps.

Same on iOS 17.