根据scrollView动态更改位置 [英] Dynamically change position based on 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 thepath
.
如果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屋!