我试图在屏幕上移动一个子视图,但是我也希望为对象添加惯性或动量。
我的UIPanGestureRecognizer代码已经在下面了。
提前致谢。
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self addGestureRecognizer:panGesture];
(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.superview];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.superview];
if (recognizer.state == UIGestureRecognizerStateEnded) {
[self.delegate card:self.tag movedTo:self.frame.origin];
}
}
再次感谢。
答案 0 :(得分:5)
看看RotationWheelAndDecelerationBehaviour。有一个例子可以说明如何对线性摇摄和旋转运动进行减速。诀窍是看用户结束触摸时的速度是多少,然后以小的减速度继续朝这个方向移动。
答案 1 :(得分:1)
嗯,我不是专业人士,但是,检查多个答案后,我设法制作了自己的代码,我很高兴。
请告诉我如何改进它以及是否有任何不良做法。
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translatedPoint = [recognizer translationInView:self.postViewContainer];
CGPoint velocity = [recognizer velocityInView:recognizer.view];
float bottomMargin = self.view.frame.size.height - containerViewHeight;
float topMargin = self.view.frame.size.height - scrollViewHeight;
if ([recognizer state] == UIGestureRecognizerStateChanged) {
newYOrigin = self.postViewContainer.frame.origin.y + translatedPoint.y;
if (newYOrigin <= bottomMargin && newYOrigin >= topMargin) {
self.postViewContainer.center = CGPointMake(self.postViewContainer.center.x, self.postViewContainer.center.y + translatedPoint.y);
}
[recognizer setTranslation:CGPointMake(0, 0) inView:self.postViewContainer];
}
if ([recognizer state] == UIGestureRecognizerStateEnded) {
__block float newYAnimatedOrigin = self.postViewContainer.frame.origin.y + (velocity.y / 2.5);
if (newYAnimatedOrigin <= bottomMargin && newYAnimatedOrigin >= topMargin) {
[UIView animateWithDuration:1.2 delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^ {
self.postViewContainer.center = CGPointMake(self.postViewContainer.center.x, self.postViewContainer.center.y + (velocity.y / 2.5));
}
completion:^(BOOL finished) {
[self.postViewContainer setFrame:CGRectMake(0, newYAnimatedOrigin, self.view.frame.size.width, self.view.frame.size.height - newYAnimatedOrigin)];
}
];
}
else {
[UIView animateWithDuration:0.6 delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^ {
if (newYAnimatedOrigin > bottomMargin) {
self.postViewContainer.center = CGPointMake(self.postViewContainer.center.x, bottomMargin + self.postViewContainer.frame.size.height / 2);
}
if (newYAnimatedOrigin < topMargin) {
self.postViewContainer.center = CGPointMake(self.postViewContainer.center.x, topMargin + self.postViewContainer.frame.size.height / 2);
}
}
completion:^(BOOL finished) {
if (newYAnimatedOrigin > bottomMargin)
[self.postViewContainer setFrame:CGRectMake(0, bottomMargin, self.view.frame.size.width, scrollViewHeight)];
if (newYAnimatedOrigin < topMargin)
[self.postViewContainer setFrame:CGRectMake(0, topMargin, self.view.frame.size.width, scrollViewHeight)];
}
];
}
}
}
我使用了两个不同的动画,一个是默认惯性,另一个是用户当用户以高速度移动容器视图时。
在iOS 7下运行良好。
答案 2 :(得分:1)
我从接受的答案的实现中获得了灵感。这是 Swift 5.1版本。
逻辑:
用于计算减速度的主要功能:
// deceleration behaviour constants (change these for different deceleration rates)
private let timerDuration = 0.025
private let decelerationSmoothness = 0.9
private let velocityToAngleConversion = 0.0025
private func animateWithInertia(velocity: Double) {
_ = Timer.scheduledTimer(withTimeInterval: self.timerDuration, repeats: true) { [weak self] timer in
guard let this = self else {
return
}
let concernedVelocity = this.currentVelocity == 0.0 ? velocity : this.currentVelocity
let newVelocity = concernedVelocity * this.decelerationSmoothness
this.currentVelocity = newVelocity
var angleTraversed = newVelocity * this.velocityToAngleConversion * this.maximumRotationAngleInCircle
if !this.isClockwiseRotation {
angleTraversed *= -1
}
// exit condition
if newVelocity < 0.1 {
timer.invalidate()
this.currentVelocity = 0.0
} else {
this.traverseAngularDistance(angle: angleTraversed)
}
}
}
在handlePanGesture函数中具有辅助功能,上述功能的扩展和用法的完整工作代码。
// deceleration behaviour constants (change these for different deceleration rates)
private let timerDuration = 0.025
private let decelerationSmoothness = 0.9
private let velocityToAngleConversion = 0.0025
private let maximumRotationAngleInCircle = 360.0
private var currentRotationDegrees: Double = 0.0 {
didSet {
if self.currentRotationDegrees > self.maximumRotationAngleInCircle {
self.currentRotationDegrees = 0
}
if self.currentRotationDegrees < -self.maximumRotationAngleInCircle {
self.currentRotationDegrees = 0
}
}
}
private var previousLocation = CGPoint.zero
private var currentLocation = CGPoint.zero
private var velocity: Double {
let xFactor = self.currentLocation.x - self.previousLocation.x
let yFactor = self.currentLocation.y - self.previousLocation.y
return Double(sqrt((xFactor * xFactor) + (yFactor * yFactor)))
}
private var currentVelocity = 0.0
private var isClockwiseRotation = false
@objc private func handlePanGesture(panGesture: UIPanGestureRecognizer) {
let location = panGesture.location(in: self)
if let rotation = panGesture.rotation {
self.isClockwiseRotation = rotation > 0
let angle = Double(rotation).degrees
self.currentRotationDegrees += angle
self.rotate(angle: angle)
}
switch panGesture.state {
case .began, .changed:
self.previousLocation = location
case .ended:
self.currentLocation = location
self.animateWithInertia(velocity: self.velocity)
default:
print("Fatal State")
}
}
private func animateWithInertia(velocity: Double) {
_ = Timer.scheduledTimer(withTimeInterval: self.timerDuration, repeats: true) { [weak self] timer in
guard let this = self else {
return
}
let concernedVelocity = this.currentVelocity == 0.0 ? velocity : this.currentVelocity
let newVelocity = concernedVelocity * this.decelerationSmoothness
this.currentVelocity = newVelocity
var angleTraversed = newVelocity * this.velocityToAngleConversion * this.maximumRotationAngleInCircle
if !this.isClockwiseRotation {
angleTraversed *= -1
}
if newVelocity < 0.1 {
timer.invalidate()
this.currentVelocity = 0.0
this.selectAtIndexPath(indexPath: this.nearestIndexPath, shouldTransformToIdentity: true)
} else {
this.traverseAngularDistance(angle: angleTraversed)
}
}
}
private func traverseAngularDistance(angle: Double) {
// keep the angle in -360.0 to 360.0 range
let times = Double(Int(angle / self.maximumRotationAngleInCircle))
var newAngle = angle - times * self.maximumRotationAngleInCircle
if newAngle < -self.maximumRotationAngleInCircle {
newAngle += self.maximumRotationAngleInCircle
}
self.currentRotationDegrees += newAngle
self.rotate(angle: newAngle)
}
以上代码中使用的扩展名:
extension UIView {
func rotate(angle: Double) {
self.transform = self.transform.rotated(by: CGFloat(angle.radians))
}
}
extension Double {
var radians: Double {
return (self * Double.pi)/180
}
var degrees: Double {
return (self * 180)/Double.pi
}
}