'scrollViewDidScroll' does not catch movem

2019-08-05 13:33发布

问题:

I am writing program to move an UIView named "myView" while scrolling a UITableView, Look at the pic below:

"myView" will change its 'y-coordinate' along with the change of the contentoffset of tableview, the main code I write is:

func scrollViewDidScroll(scrollView: UIScrollView) {

        let off_y = scrollView.contentOffset.y
        let new_y = originalMyViewY - off_y

        if new_y <= 0 {
            print("bad new_y : \(new_y)")
            return
        }
        else {
            print("right new_y: \(new_y)")
        }

        var frame = self.myView.frame
        frame.origin.y = new_y
        self.myView.frame = frame                                    
}

This works perfectly when I scroll the tableview at a normal speed. However, if I scroll the tableview very fast, 'myview' will stop before reaching the top edge. This is what the log shows:

right new_y: 56.0
bad new_y : -92.5
bad new_y : -153.5
bad new_y : -206.0

It seems that the delegate method scrollViewDidScroll did not catch every scroll movement which led to a value jump.

I want to know the reason and how to fix it, thanks!

回答1:

The likely reason for this occurring is because the touches that are being received by your app are occurring faster than the refresh rate of the screen. Thus certain intermediate scroll points will be missed.

Refresh Rate

iOS devices typically run at 60hz or 60 frames per second. This means that every 1/60s or 0.016666 seconds the screen and digitizer will attempt to refresh the current state of touches.

If your finger moves faster than 0.5 display points in that time period than the next scrollViewDidScroll delegate method will report the newer location and never report the intermediate locations.

Solution

Instead of using the values directly from the delegate method, I would recommend "clamping" the result to your expected range. In this instance it looks like you don't want to receive negative numbers. Meaning if you want the origin y value to be at minimum 0 then you should take the value from the delegate and if it is less than 0 set your View's origin to 0 otherwise use your calculated result.

Example:

func scrollViewDidScroll(scrollView: UIScrollView) {

    let off_y = scrollView.contentOffset.y
    var new_y = originalMyViewY - off_y // make this mutable so we can change it in the <= 0 block

    if new_y <= 0 {
        print("y is less than 0, clamp y origin to 0")
        new_y = 0
    }
    else {
        print("right new_y: \(new_y)")
    }

    var frame = self.myView.frame
    frame.origin.y = new_y
    self.myView.frame = frame                                    
}

Note: This shouldn't result in jumpy resizing of your view as scrollView should already be reporting this scrollViewDidScroll at near the screen refresh rate.