Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/93.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 向UIPangestureRecognitor添加惯性_Ios_Uipangesturerecognizer_Momentum - Fatal编程技术网

Ios 向UIPangestureRecognitor添加惯性

Ios 向UIPangestureRecognitor添加惯性,ios,uipangesturerecognizer,momentum,Ios,Uipangesturerecognizer,Momentum,我试图在屏幕上移动一个子视图,这是可行的,但我也想给对象添加惯性或动量。 我已有的UIPangestureRecognitor代码如下 提前谢谢 UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [self addGestureRecognizer:panGesture]; (void)hand

我试图在屏幕上移动一个子视图,这是可行的,但我也想给对象添加惯性或动量。
我已有的UIPangestureRecognitor代码如下

提前谢谢

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];
    }
}

再次感谢。

看一看。有一个示例说明如何对线性平移和旋转运动进行减速。诀窍是,当用户结束触摸并以较小的减速继续朝那个方向移动时,速度是多少。

好吧,我不是专业人士,但通过检查多个答案,我成功地编写了自己的代码,我对此感到满意

请告诉我如何改善它,如果有任何不良做法,我用

- (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)];
                         }
         ];
    }
}
-(iAction)handlePan:(UIPangestureRecognitor*)识别器{
CGPoint translatedPoint=[识别器translationView:self.postViewContainer];
CGPoint velocity=[识别器速度视图:recognizer.view];
浮动底部边距=self.view.frame.size.height-containerViewHeight;
float topMargin=self.view.frame.size.height-scrollViewHeight;
如果([Recognitor state]==UIgestureRecognitzerStateChanged){
newYOrigin=self.postViewContainer.frame.origin.y+translatedPoint.y;
if(newYOrigin=topMargin){
self.postViewContainer.center=CGPointMake(self.postViewContainer.center.x、self.postViewContainer.center.y+translatedPoint.y);
}
[识别器setTranslation:CGPointMake(0,0)inView:self.postViewContainer];
}
if([Recognitor state]==UIgestureRecognitzerStateEnded){
__block float newYAnimatedOrigin=self.postViewContainer.frame.origin.y+(velocity.y/2.5);
if(newYAnimatedOrigin=topMargin){
[UIView animateWithDuration:1.2延迟:0
选项:UIViewAnimationOptionCurveEaseOut
动画:^{
self.postViewContainer.center=CGPointMake(self.postViewContainer.center.x,self.postViewContainer.center.y+(velocity.y/2.5));
}
完成:^(布尔完成){
[self.postViewContainer setFrame:CGRectMake(0,newYAnimatedOrigin,self.view.frame.size.width,self.view.frame.size.height-newYAnimatedOrigin)];
}
];
}
否则{
[UIView animateWithDuration:0.6延迟:0
选项:UIViewAnimationOptionCurveEaseOut
动画:^{
if(newYAnimatedOrigin>bottomMargin){
self.postViewContainer.center=CGPointMake(self.postViewContainer.center.x,bottomMargin+self.postViewContainer.frame.size.height/2);
}
if(新的iMatedOriginbottomMargin)
[self.postViewContainer setFrame:CGRectMake(0,底边距,self.view.frame.size.width,scrollViewHeight)];
if(新的iMatedOrigin
}

我使用了两种不同的动画,一种是默认的惯性,另一种是当用户高速抛出containerView时使用的


它在iOS 7下运行良好。

我从公认答案的实现中获得了灵感。这是一个Swift 5.1版本


逻辑:

  • 您需要计算角度随平移动作结束时的速度变化,并在无休止的计时器中持续旋转轮子,直到速度因减速率而降低。
  • 在计时器的每次迭代中不断降低当前速度 有一些因素(比如0.9)。
  • 保持较低的速度限制,以 使计时器失效并完成减速过程。

用于计算减速度的主要功能:

// 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)
        }
    }
}

这不是一个非常彻底的答案。我建议你仔细阅读并扩展它。
// 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
    }
}