根据scrollView动态更改位置 [英] Dynamically change position based on scrollView

查看:88
本文介绍了根据scrollView动态更改位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个U形的 UIBezierPath ,它用作我的路径 > myImage.layer 进行动画处理。我也有一个scrollView。我的目标是制作一个自定义的拉动刷新动画。

I have a "U" shaped UIBezierPath which I use as the path for my myImage.layer to animate on. I also have a scrollView. My goal is to have a custom "Pull to Refresh" animation.

我遇到的问题是我希望我的 myImage.layer 根据scrollView滚动多少来更新。

The problem I am having is that I want my myImage.layer to update based on how much the scrollView scrolled.

当向下拖动scrollView时, myImage.layer 沿 U形动画路径。这是我创建为 UIBezierPath 的代码中的路径

As the scrollView is pulled down, the myImage.layer animates along a "U" shape path. This is the path in my code which I created as a UIBezierPath.

这是我计算scrollView下拉的距离的方式:

This is how I calculate how far the scrollView is pulled down:

func scrollViewDidScroll(scrollView: UIScrollView) {
    let offsetY = CGFloat(max(-(scrollView.contentOffset.y + scrollView.contentInset.top), 0.0))
    self.progress = min(max(offsetY / frame.size.height, 0.0), 1.0)

    if !isRefreshing {
        redrawFromProgress(self.progress)
    }
}

此功能可动态更新位置(无效):

This is the function to dynamically update the position (it is not working):

func redrawFromProgress(progress: CGFloat) {

    // PROBLEM: This is not correct. Only the `x` position is dynamic based on scrollView position.
    // The `y` position is static. 
    // I want this to be dynamic based on how much the scrollView scrolled.
    myImage.layer.position = CGPoint(x: progress, y: 50)

}

基本上,这就是我想要的:

Basically, this is what I want:


  • 如果scrollView滚动为0.0,则 myImage.layer 的位置应为CGPoint(x:0,y:0)或路径的起点。

  • If the scrollView scrolled is 0.0, then the myImage.layer position should be CGPoint(x: 0, y: 0) or the starting point of the path.

如果scrollView滚动为0.5(50%),则 myImage.layer 位置应为在路径的50%处,我不知道CGPoint的值在这里。

If the scrollView scrolled is 0.5 (50%), then the myImage.layer position should be at 50% of the path, I don't know what the CGPoint value would be here.

等等...

我尝试沿着 UIBezierPath 并根据滚动的scrollView的百分比为其分配CGPoint值,但不知道如何执行此操作。我还查看了此帖子,但我无法使其正常运行我。

I tried getting the CGPoint values along the UIBezierPath and based on the % of the scrollView scrolled, assign that CGPoint value to it but don't know how to do this. I also looked at this post but I can't get it to work for me.

编辑问题1:

通过使用此扩展程序,我当时能够获取 CGPoints 数组,其中包含基于我的 UIBezierPath 的10个值:

By using this extension, I was able to get an array of CGPoints which contain 10 values based on my UIBezierPath:

extension CGPath {
func forEachPoint(@noescape body: @convention(block) (CGPathElement) -> Void) {
    typealias Body = @convention(block) (CGPathElement) -> Void
    func callback(info: UnsafeMutablePointer<Void>, element: UnsafePointer<CGPathElement>) {
        let body = unsafeBitCast(info, Body.self)
        body(element.memory)
    }
    // print(sizeofValue(body))
    let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self)
    CGPathApply(self, unsafeBody, callback)
}

func getPathElementsPoints() -> [CGPoint] {
    var arrayPoints : [CGPoint]! = [CGPoint]()
    self.forEachPoint { element in
        switch (element.type) {
        case CGPathElementType.MoveToPoint:
            arrayPoints.append(element.points[0])
        case .AddLineToPoint:
            arrayPoints.append(element.points[0])
        case .AddQuadCurveToPoint:
            arrayPoints.append(element.points[0])
            arrayPoints.append(element.points[1])
        case .AddCurveToPoint:
            arrayPoints.append(element.points[0])
            arrayPoints.append(element.points[1])
            arrayPoints.append(element.points[2])
        default: break
        }
    }
    return arrayPoints
}

我也将上面称为 redrawFromProgress(progress:CGFloat)的函数重写了:

I also rewrote the function above called redrawFromProgress(progress: CGFloat) to this:

func redrawFromProgress(progress: CGFloat) {

    let enterPath = paths[0]
    let pathPointsArray = enterPath.CGPath
    let junctionPoints = pathPointsArray.getPathElementsPoints()
    // print(junctionPoints.count) // There are 10 junctionPoints

    // progress means how much the scrollView has been pulled down,
    // it goes from 0.0 to 1.0. 

    if progress <= 0.1 {

        myImage.layer.position = junctionPoints[0]

    } else if progress > 0.1 && progress <= 0.2 {

        myImage.layer.position = junctionPoints[1]

    } else if progress > 0.2 && progress <= 0.3 {

        myImage.layer.position = junctionPoints[2]

    } else if progress > 0.3 && progress <= 0.4 {

        myImage.layer.position = junctionPoints[3]

    } else if progress > 0.4 && progress <= 0.5 {

        myImage.layer.position = junctionPoints[4]

    } else if progress > 0.5 && progress <= 0.6 {

        myImage.layer.position = junctionPoints[5]

    } else if progress > 0.6 && progress <= 0.7 {

        myImage.layer.position = junctionPoints[6]

    } else if progress > 0.7 && progress <= 0.8 {

        myImage.layer.position = junctionPoints[7]

    } else if progress > 0.8 && progress <= 0.9 {

        myImage.layer.position = junctionPoints[8]

    } else if progress > 0.9 && progress <= 1.0 {

        myImage.layer.position = junctionPoints[9]

    }

}

如果我非常缓慢地拉下scrollView, myImage.layer 实际上会跟随路径。唯一的问题是,如果我快速拉下scrollView,则 myImage.layer 会跳到最后一点。可能是因为我编写上述 if语句的方式吗?

If I pull down the scrollView very slow, the myImage.layer actually follows the path. The only problem is that if I pull down on the scrollView very fast, then the myImage.layer jumps to the last point. Could it be because of the way I wrote the if statement above?

任何想法吗?

推荐答案

感谢@Sam Falconer使我意识到这一点:

Thanks to @Sam Falconer for making me aware of this:


您的代码依赖于足够频繁地调用scrollViewDidScroll委托回调以击中所有关键帧点。当您快速拉动滚动视图时,它不会足够频繁地调用该方法,从而导致跳转。

Your code is relying on the scrollViewDidScroll delegate callback to be called frequently enough to hit all of your keyframe points. When you pull quickly on the scroll view, it does not call that method frequently enough, causing the jump.

一旦我确认了这一点,他还提到了以下内容:

Once I confirmed this, he also helped by mentioning:


此外,您会发现CAKeyframeAnimation类非常有用。

Additionally, you will find the CAKeyframeAnimation class to be useful.

使用 CAKeyfraneAnimation ,我可以使用以下代码手动控制其值:

With CAKeyfraneAnimation I am able to manually control it's value with this code:

func scrollViewDidScroll(scrollView: UIScrollView) {
    let offsetY = CGFloat(max(-(scrollView.contentOffset.y + scrollView.contentInset.top), 0.0))
    self.progress = min(max(offsetY / frame.size.height, 0.0), 1.0)

    if !isRefreshing {
        redrawFromProgress(self.progress)
    }
}


func redrawFromProgress(progress: CGFloat) {

    // Animate image along enter path
    let pathAnimation = CAKeyframeAnimation(keyPath: "position")
    pathAnimation.path = myPath.CGPath
    pathAnimation.calculationMode = kCAAnimationPaced
    pathAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
    pathAnimation.beginTime = 1e-100
    pathAnimation.duration = 1.0
    pathAnimation.timeOffset = CFTimeInterval() + Double(progress)
    pathAnimation.removedOnCompletion = false
    pathAnimation.fillMode = kCAFillModeForwards

    imageLayer.addAnimation(pathAnimation, forKey: nil)
    imageLayer.position = enterPath.currentPoint
}

再次感谢帮助!

这篇关于根据scrollView动态更改位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆