Ios 运行一个循环,直到UIPangestureRecognitor结束

Ios 运行一个循环,直到UIPangestureRecognitor结束,ios,swift,cocoa-touch,uigesturerecognizer,Ios,Swift,Cocoa Touch,Uigesturerecognizer,希望这对mods来说不是太模糊 我想做一个用户交互,类似于某些高保真音响上的音量控制,你可以向左或向右移动一个刻度盘来改变音量,但不是将刻度盘旋转一整圈,而是将它稍微向左或向右旋转,旋转的次数越多,音量变化的速度就越快,直到你放开它,它会弹回到中间 在我的应用程序中,我想使用一个UIPangestureRecognitor,当用户从中间向上平移时,音量会上升,距离中间越远,增长越快。当他们在屏幕中点下方平移时,音量下降,距离中间越远,速度也越快 我遇到的问题是如何在不锁定UI的情况下实现这一点。

希望这对mods来说不是太模糊

我想做一个用户交互,类似于某些高保真音响上的音量控制,你可以向左或向右移动一个刻度盘来改变音量,但不是将刻度盘旋转一整圈,而是将它稍微向左或向右旋转,旋转的次数越多,音量变化的速度就越快,直到你放开它,它会弹回到中间

在我的应用程序中,我想使用一个UIPangestureRecognitor,当用户从中间向上平移时,音量会上升,距离中间越远,增长越快。当他们在屏幕中点下方平移时,音量下降,距离中间越远,速度也越快

我遇到的问题是如何在不锁定UI的情况下实现这一点。我不能只使用手势识别器动作选择器,因为只有在有动作时才会调用它,为了让这种交互发挥作用,用户通常会将手指放在一个位置,同时等待达到正确的音量

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

如果这是一个嵌入式系统,我只需要设置某种基于中断的轮询来检查输入控件的位置,并不断添加到卷中,直到它回到中间位置-在这里找不到与iOS类似的


欢迎您提出建议,抱歉,如果这太模糊了,mods-这更像是一个框架方法问题,而不是一个特定的代码问题。

有趣的问题我为您编写了一个示例,一定是您想要的:

目标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!);
        }
    }
}
希望它能帮助你


如果你还需要一些建议,就把它放在这里,我会稍后再查。

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

目标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!);
        }
    }
}
希望它能帮助你


如果你还需要一些建议,就把它放在这里,我以后再查。

这听起来是一个有趣的挑战。我不完全理解高保真的东西。但我所描绘的是一个圆形的旋钮,在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()
    }
}
var卷:双精度=0
var maxVolume:Double=100
var增加:布尔
var乘数:Double=0
var cartesianTransform=CGAffineTransform()
设knobViewFrame=CGRect(x:50,y:50,宽度:100,高度:100)
设knobRadius:CGFloat=45
让旋钮=UIView()
变量计时器:NSTimer?
func setTransform(){
self.cartesianTransform=CGAffineTransform(a:1/Knobadius,b:0,c:0,d:-1/Knobadius,tx:knobViewFrame.width/2,ty:knobViewFrame.height*1.5)
//不可否认,我总是要处理这些问题才能使其正确,因此可能会有一些错误。此转换应将视图转换为一个平面,其中(0,0)为中心,旋钮的圆为(-1,0)
}
func pan旋钮(pan:UIPangestureRecognitor){
让PointEntertesian=cgpointApplyAffinetTransform(pan.locationInView(pan.view!),cartesianTransform)
如果pan.state==。开始{
递增=点表示符号y>0
timer=NSTimer.scheduledTimerWithTimeInterval(0.1,目标:self,选择器:#选择器(增加音量),userInfo:nil,repeats:true)
}
设反正切=CGFloat(M_PI)-atan2(pointenvernatesian.y,pointenvernatesian.x)
设maxAngle=增加?CGFloat(M_PI)/2:-CGFloat(M_PI)/2
变量角度:CGFloat
如果增加{
角度=反正切>最大角度?最大角度:反正切
}否则{
角度=反正切<最大角度?最大角度:反正切
}
旋钮.transform=CGAffineTransformMakeRotation(角度)
自倍增=双(角度)*10
如果pan.state==.end | | pan.state==.Cancelled | | pan.state==.Failed{
计时器?.invalidate()
UIView.animateWithDuration(0.75,延迟:0,选项:.CurveEaseIn,动画:{self.knob.transform=CGAffineTransformity},完成:nil)
}
}
func increaseVolume(){
让newVolume=体积+乘数
volume=newVolume>maxVolume?maxVolume:(newVolume<0?0:newVolume)
如果体积==maxVolume | |体积==0 | |乘数==0{
计时器?.invalidate()
}
}
我还没有测试过上面的内容,但这似乎是一个很酷的难题。倍增器仅在角度更改时更改,并且在计时器有效时,体积会不断添加倍增器。如果希望体积在不改变角度的情况下连续加速,请将乘数更改移动到计时器的选择器,并将角度保持为类变量,以便了解加速速度

编辑:您可能不需要转换,只需获取增量即可完成此操作