swift : 平滑 UIView 的平移与 iOS 控制面板完全一样(跟随用户手指,使用平移)
swift : smoothing translation of UIView exactly as iOS control panel (following user finger, using pan)
我想像 iOS 中的控制中心一样用手指从下向上移动 UIView,尤其是 "smoothing" 与 [=22= 中的控制中心相同的移动方式] ] (速度看起来是有界的,当速度很高时,手指不需要走得太远就可以使 UIView 移动到顶部等)。
我的问题是:有没有我可以使用的任何预设或库,而不是自己进行自定义和不精确的计算?我不太擅长制作这种 "smooth" 效果,也许 UIKit 提供了一些可以在 panGestureRecognizer 处理程序中使用的东西来平滑 sender.translation 之后的移动转换?
现在我的 gestureRecognizer 处理程序是:
var y = sender.translation(in: view).y
let vy = sender.velocity(in: view).y
// we need to move constraintHeightInfoView.constant
// this is the height of my "ios control panel"
// i noticed the ios control panel move less if user tries
// to move it out of screen bounds, so i'm first calculating
// how much we are out of bound, in order to know "how much"
// we will reduce velocity
var outofbound:CGFloat = 0
if(constraintHeightInfoView.constant < minH)
{
outofbound = minH - constraintHeightInfoView.constant
}
else if(constraintHeightInfoView.constant > maxH)
{
outofbound = constraintHeightInfoView.constant - maxH
}
// velocity target in pt/s
// i noticed the smooth effect in ios control panel includes
// a velocity bound : if user pulls very fast the control panel,
// it is not following the user finger, but it's a bit slower :
// so there is a "vmax". When user release, i guess this vmax is
// used for the final animation too
let vtarget:CGFloat = 300
print("\(y), \(vy), ofb : \(outofbound)")
// Here i'm lost : im trying to say :
// if velocity above vtarget, reduce y so it match vtarget
if(abs(vy) > vtarget) {
let dv = abs(vy) - vtarget
y = // ????
}
switch sender.state {
case .began, .changed :
if case .began = sender.state {
initialY = constraintHeightInfoView.constant
}
let newH = (expandInfoViewInitialY ?? minH) - y
constraintHeightInfoView.constant = newH
view.layoutIfNeeded()
case .ended :
let heightTo:CGFloat
let blurAlpha:CGFloat
// 250 is the middle between top position and
// bottom position : if velocity is high, dont need to pull
// a lot to proceed the final release animation
// if vy is 3000, even very small movement proceed the
// animation. Otherwise, if very slow movement,
// user needs to go at least at the middle between top and bottom (250)
if(constraintHeightInfoView.constant > 250 - (-vy/300) * 50)
{
heightTo = maxH
blurAlpha = 1
}
else
{
heightTo = minH
blurAlpha = 0
}
// time of animation depends on remaining distance to bottom/top
let dy = abs(self.constraintHeightInfoView.constant - heightTo)
var t = dy / vtarget
print("end : d:\(dy), t:\(t)")
UIView.animate(withDuration:TimeInterval(t), delay:0,options:[.curveEaseOut], animations :{
self.constraintHeightInfoView.constant = heightTo
self.viewInfoBlurEffect.alpha = blurAlpha
self.view.layoutIfNeeded()
})
default :
break
}
尽管如此,我的转换仍然远不像 Apple Control Center 的那样...
如果您将其设置为从下到上和从上到下的动画效果,您将获得更满意的体验。现在 "freeze" 它并通过 UIViewPropertyAnimator 从平移手势识别器连接到它。这使它成为一个交互式动画。
例如,如果用户拖动超过一定数量,这将允许您 "finish" 动画。而且你将能够通过同一个动画师同时完成 tap-to-dismiss 和 drag-to-dismiss。
我想像 iOS 中的控制中心一样用手指从下向上移动 UIView,尤其是 "smoothing" 与 [=22= 中的控制中心相同的移动方式] ] (速度看起来是有界的,当速度很高时,手指不需要走得太远就可以使 UIView 移动到顶部等)。 我的问题是:有没有我可以使用的任何预设或库,而不是自己进行自定义和不精确的计算?我不太擅长制作这种 "smooth" 效果,也许 UIKit 提供了一些可以在 panGestureRecognizer 处理程序中使用的东西来平滑 sender.translation 之后的移动转换?
现在我的 gestureRecognizer 处理程序是:
var y = sender.translation(in: view).y
let vy = sender.velocity(in: view).y
// we need to move constraintHeightInfoView.constant
// this is the height of my "ios control panel"
// i noticed the ios control panel move less if user tries
// to move it out of screen bounds, so i'm first calculating
// how much we are out of bound, in order to know "how much"
// we will reduce velocity
var outofbound:CGFloat = 0
if(constraintHeightInfoView.constant < minH)
{
outofbound = minH - constraintHeightInfoView.constant
}
else if(constraintHeightInfoView.constant > maxH)
{
outofbound = constraintHeightInfoView.constant - maxH
}
// velocity target in pt/s
// i noticed the smooth effect in ios control panel includes
// a velocity bound : if user pulls very fast the control panel,
// it is not following the user finger, but it's a bit slower :
// so there is a "vmax". When user release, i guess this vmax is
// used for the final animation too
let vtarget:CGFloat = 300
print("\(y), \(vy), ofb : \(outofbound)")
// Here i'm lost : im trying to say :
// if velocity above vtarget, reduce y so it match vtarget
if(abs(vy) > vtarget) {
let dv = abs(vy) - vtarget
y = // ????
}
switch sender.state {
case .began, .changed :
if case .began = sender.state {
initialY = constraintHeightInfoView.constant
}
let newH = (expandInfoViewInitialY ?? minH) - y
constraintHeightInfoView.constant = newH
view.layoutIfNeeded()
case .ended :
let heightTo:CGFloat
let blurAlpha:CGFloat
// 250 is the middle between top position and
// bottom position : if velocity is high, dont need to pull
// a lot to proceed the final release animation
// if vy is 3000, even very small movement proceed the
// animation. Otherwise, if very slow movement,
// user needs to go at least at the middle between top and bottom (250)
if(constraintHeightInfoView.constant > 250 - (-vy/300) * 50)
{
heightTo = maxH
blurAlpha = 1
}
else
{
heightTo = minH
blurAlpha = 0
}
// time of animation depends on remaining distance to bottom/top
let dy = abs(self.constraintHeightInfoView.constant - heightTo)
var t = dy / vtarget
print("end : d:\(dy), t:\(t)")
UIView.animate(withDuration:TimeInterval(t), delay:0,options:[.curveEaseOut], animations :{
self.constraintHeightInfoView.constant = heightTo
self.viewInfoBlurEffect.alpha = blurAlpha
self.view.layoutIfNeeded()
})
default :
break
}
尽管如此,我的转换仍然远不像 Apple Control Center 的那样...
如果您将其设置为从下到上和从上到下的动画效果,您将获得更满意的体验。现在 "freeze" 它并通过 UIViewPropertyAnimator 从平移手势识别器连接到它。这使它成为一个交互式动画。
例如,如果用户拖动超过一定数量,这将允许您 "finish" 动画。而且你将能够通过同一个动画师同时完成 tap-to-dismiss 和 drag-to-dismiss。