运行循环直到UIPanGestureRecognizer结束

时间:2016-09-06 17:39:18

标签: ios swift cocoa-touch uigesturerecognizer

希望这对于mod来说并不太模糊。

我想进行类似于某些高保真音量控制的用户互动,您可以向左或向右移动拨盘来改变音量但不是转动拨号完全转动,而是向左或向右转动你转得越多,音量变化的速度就越快,直到你放开它并回到中间。

在我的应用程序中,我想使用UIPanGestureRecogniser,当用户从中间向上平移时音量增加,从中间越远,增加越快。当它们平移到屏幕的中点以下时,音量会下降,从你的中间越远越快。

我所困扰的领域是如何在锁定UI的情况下实现这一目标。我不能只使用gestureRecognizer动作选择器,因为只有在有移动时才会调用它,为了使这种交互起作用,用户通常会将手指放在一个位置,同时等待到达正确的音量。

我觉得我想设置一个在gesturerecogniser选择器外运行的循环,并让它监视一个类变量,当手势移动或结束时,它会更新。如果它在手势识别器选择器中执行此操作,它将继续运行....

如果这是一个嵌入式系统,我会设置一些基于中断的轮询,以检查输入控件是什么,并继续添加到音量,直到它回到中间 - 无法找到可比较的iOS在这里。

建议是受欢迎的,如果这太模糊,那就抱歉了 - 这是一个特定代码问题的框架方法问题。

2 个答案:

答案 0 :(得分:2)

有趣的问题我为你写了一个必须是你想要的样本:

Objective-C代码:

#import "ViewController.h"

@interface ViewController ()

@property float theValue;
@property NSTimer *timer;
@property bool needRecord;
@property UIView *dot;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.needRecord = NO;
    self.theValue = 0;

    UIView *circle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
    circle.layer.borderWidth = 3;
    circle.layer.borderColor = [UIColor redColor].CGColor;
    circle.layer.cornerRadius = 25;
    circle.center = self.view.center;
    [self.view addSubview:circle];

    self.dot = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    self.dot.backgroundColor = [UIColor redColor];
    self.dot.layer.cornerRadius = 20;
    self.dot.center = self.view.center;
    [self.view addSubview:self.dot];

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandle:)];

    [self.dot addGestureRecognizer:pan];

    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
}

-(void)panHandle:(UIPanGestureRecognizer *)pan{
    CGPoint pt = [pan translationInView:self.view];
//    NSLog([NSString stringWithFormat:@"pt.y = %f",pt.y]);
    switch (pan.state) {
        case UIGestureRecognizerStateBegan:
            [self draggingStart];
            break;
        case UIGestureRecognizerStateChanged:
            self.dot.center = CGPointMake(self.view.center.x, self.view.center.y + pt.y);
            break;
        case UIGestureRecognizerStateEnded:
            [self draggingEnned];
            break;
        default:
            break;
    }
}

-(void)draggingStart{
    self.needRecord = YES;
}

-(void)draggingEnned{
    self.needRecord = NO;
    [UIView animateWithDuration:0.5 animations:^{
        self.dot.center = self.view.center;
    }];
}

-(void)timerFire{
    if (self.needRecord) {
        float distance = self.dot.center.y - self.view.center.y;
//        NSLog([NSString stringWithFormat:@"distance = %f",distance]);
        self.theValue -= distance/1000;
        NSLog([NSString stringWithFormat:@"theValue = %f",self.theValue]);
    }
}

@end

我现在正在学习Swift,所以如果你需要,这是Swift代码:

class ViewController: UIViewController {

    var lbInfo:UILabel?;
    var theValue:Float?;
    var timer:NSTimer?;
    var needRecord:Bool?;
    var circle:UIView?;
    var dot:UIView?;

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        needRecord = false;
        theValue = 0;

        lbInfo = UILabel(frame: CGRect(x: 50, y: 50, width: UIScreen.mainScreen().bounds.width-100, height: 30));
        lbInfo!.textAlignment = NSTextAlignment.Center;
        lbInfo!.text = "Look at here!";
        self.view.addSubview(lbInfo!);

        circle = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50));
        circle!.layer.borderWidth = 3;
        circle!.layer.borderColor = UIColor.redColor().CGColor;
        circle!.layer.cornerRadius = 25;
        circle!.center = self.view.center;
        self.view.addSubview(circle!);

        dot = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40));
        dot!.backgroundColor = UIColor.redColor();
        dot!.layer.cornerRadius = 20;
        dot!.center = self.view.center;
        self.view.addSubview(dot!);

        let pan = UIPanGestureRecognizer(target: self, action: #selector(ViewController.panhandler));
        dot!.addGestureRecognizer(pan);

        timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: #selector(ViewController.timerFire), userInfo: nil, repeats: true)
    }

    func panhandler(pan: UIPanGestureRecognizer) -> Void {
        let pt = pan.translationInView(self.view);
        switch pan.state {
        case UIGestureRecognizerState.Began:
            draggingStart();
        case UIGestureRecognizerState.Changed:
            self.dot!.center = CGPoint(x: self.view.center.x, y: self.view.center.y + pt.y);
        case UIGestureRecognizerState.Ended:
            draggingEnded();
        default:
            break;
        }
    }

    func draggingStart() -> Void {
        needRecord = true;
    }

    func draggingEnded() -> Void {
        needRecord = false;
        UIView.animateWithDuration(0.1, animations: {
            self.dot!.center = self.view.center;
        });
    }

    @objc func timerFire() -> Void {
        if(needRecord!){
            let distance:Float = Float(self.dot!.center.y) - Float(self.view.center.y);
            theValue! -= distance/1000;
            self.lbInfo!.text = String(format: "%.2f", theValue!);
        }
    }
}

希望它可以帮到你。

如果您仍需要一些建议,请将其保留在此处,我会检查后者。

答案 1 :(得分:1)

这听起来像是一个有趣的挑战。我不完全理解高保真的事情。但我想象的是一个圆形旋钮,在9点钟处的小点几乎在边缘。向右旋转旋钮时,点向12点移动,音量增加,点距离9点钟越远,加速越快。只要点高于9,它就会继续增加,只是增加的加速度会发生变化。

向左转时,点向6点钟移动,音量减小,加速度取决于9点钟的径向距离。如果这个假设是正确的,我认为以下方法可行。

我会用一点三角学来解决这个问题。要获得加速度,您需要从9点钟轴(负x轴)开始的角度。正角度增加音量,负角度减小音量,加速度取决于旋转度。此角度还将为您提供一个变换,您可以将其应用于视图以更改点的位置。我不认为这将采取任何太花哨的代码明智。在这种情况下,我已经使最大旋转为90度,或pi / 2弧度。如果您实际上可以更多地旋转旋钮,则需要更改一些代码。

var volume: Double = 0
var maxVolume: Double = 100
var increasing : Bool

var multiplier: Double = 0
var cartesianTransform = CGAffineTransform()
let knobViewFrame = CGRect(x: 50, y: 50, width: 100, height: 100)
let knobRadius: CGFloat = 45
let knob = UIView()
var timer : NSTimer?

func setTransform() {

    self.cartesianTransform = CGAffineTransform(a: 1/knobRadius, b: 0, c: 0, d: -1/knobRadius, tx: knobViewFrame.width/2, ty: knobViewFrame.height * 1.5)
   // admittedly, I always have to play with these things to get them right, so there may be some errors. This transform should turn the view into a plane with (0,0) at the center, and the knob's circle at (-1,0) 
}

func panKnob(pan: UIPanGestureRecognizer) {

    let pointInCartesian = CGPointApplyAffineTransform(pan.locationInView(pan.view!), cartesianTransform)
    if pan.state == .Began {
        increasing = pointInCartesian.y > 0
        timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: #selector(increaseVolume), userInfo: nil, repeats: true)

    }
    let arctangent = CGFloat(M_PI) - atan2(pointInCartesian.y, pointInCartesian.x)

    let maxAngle = increasing ? CGFloat(M_PI)/2 : -CGFloat(M_PI)/2

    var angle: CGFloat

    if increasing {
        angle = arctangent > maxAngle ? maxAngle : arctangent
    } else {
        angle = arctangent < maxAngle ? maxAngle : arctangent
    }

    knob.transform = CGAffineTransformMakeRotation(angle)

    self.multiplier = Double(angle) * 10

     if pan.state == .Ended || pan.state == .Cancelled || pan.state == .Failed {
        timer?.invalidate()
        UIView.animateWithDuration(0.75, delay: 0, options: .CurveEaseIn, animations: {self.knob.transform = CGAffineTransformIdentity}, completion: nil)
    }


}

func increaseVolume() {
    let newVolume = volume + multiplier
    volume = newVolume > maxVolume ? maxVolume : (newVolume < 0 ? 0 : newVolume)
    if volume == maxVolume || volume == 0 || multiplier == 0 {
        timer?.invalidate()
    }
}

我还没有测试过上面的内容,但这似乎是一个很酷的谜题。乘数仅在角度改变时改变,并且在定时器有效时音量不断添加乘数。如果您希望音量在没有角度变化的情况下连续加速,请将乘数更改移动到计时器的选择器,并将角度保持为类变量,以便了解加速它的速度。

编辑:你可以在没有变换的情况下完成它,只需获得点和locationInView之间的增量即可。