2016-09-06 5 views
0

改造者にとってあまりあいまいではないでしょう。UIPanGestureRecognizerが終了するまでループを実行してください。

ボリュームを変更するためにダイヤルを左右に動かすいくつかのハイファイの音量コントロールに似たユーザーのやりとりを作りたいと思っていますが、ダイヤルを回すのではなく、あなたが離れるまで、音量を変えるほうを早くして、それを真ん中に戻します。

私のアプリでは、UIPanGestureRecogniserを使用したいと思います。ユーザーが途中からパンを上げていくと、音量が上がり、途中から遠くになるほど速くなります。彼らは画面の中点よりも下にパンすると音量が下がりますが、途中からさらに速くなります。

私が抱えているのは、UIをロックアウトしてこのようにする方法です。 gestureRecognizerアクションセレクタを使用することはできません。これは、動きがあるときにのみ呼び出されるためです。このインタラクションが機能するためには、適切な音量に達するのを待っている間に指を1つの場所に保持することがよくあります。

gesturerecogniserセレクタの外側でループを実行して、ジェスチャの移動または終了時に更新されるクラス変数を監視したいと思っています。それがジェスチャー認識デバイスセレクタでこれを行うなら、それは実行し続けるでしょう。

これは組み込みシステムであれば、それが途中まで戻ってくるまでのボリュームはここではiOSと同等のものを見つけることができません。

これはあまりにも漠然としていれば残念ですが、申し訳ありませんが、特定のコードが問題であるというフレームワーク方法論の問題です。

+1

組み込みのものを使用する代わりに、カスタムジェスチャ認識器を作成する必要があります。あなたは '' ' - (void)touchesBegan:(NSSet *)のようなメソッドにアクセスします:withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)はwithEvent:(UIEvent *)イベントに触れます。 - (void)touchesEnded:(NSSet *)はwithEvent:(UIEvent *)イベントに接触します。 - (void)touchesCancelled:(NSSet *)はwithEvent:(UIEvent *)イベントに触れる;より細かい制御を可能にする '' ' – pnavk

答えて

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 

私は今スウィフトを学んでいるので、あなたが必要な場合は、これはスウィフトコード:

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!); 
     } 
    } 
} 

ご希望の場合はお手数ですが、

アドバイスが必要な場合は、ここにお任せください。後で確認します。

+0

私が欲しいと思うような表面にありがとう - 私は変換する必要があるそれはSwiftに(これは問題ではない、私は過去にObjCをやった)ので、明日に戻ってきます。 NSTimerを割り込みとして使用することは意味があります。 – SimonBarker

+0

私はスウィフトコードを掲示しています。私はちょうどスウィフトを学び始めました。何か間違っていたら、一緒に進んでみましょう。 –

+0

ここには良いアイデアがあると思いますが、重要な独立したアイディアがあります:(1)grを管理するための簡単なボイラープレートのもの( 'translationInView:'を提供することを覚えています)、(2)スケーリングされたgr入力に基づいて、一定の時間間隔で動かされる距離を変化させることによって何かの「速度」を変化させる。 – danh

1

これは楽しいチャレンジのようです。私はハイファイの事を完全に理解していません。しかし、私が描いているのは、ほぼ9時のところに小さな点がある円形のつまみです。ノブを右に回すと、ドットが12オクロックに移動し、音量が上がります。ドットが9時からさらに早く加速されます。ドットが9を超えている限り、それは増加を続け、増加の加速度が変化するだけです。

左に曲がると、ドットが6時に向かって移動し、音量が小さくなり、加速度は9時からの半径方向の距離に依存します。この仮定が正しいとすれば、私は以下が働くと思う。

私はこれを少しの三角法で解決するだろう。加速を得るには、9 o'oclock軸(負のx軸)からの角度が必要です。正の角度は音量を増加させ、負の角度は音量を減少させ、加速度は回転の程度に依存する。この角度は、ドットの位置を変更するためにビューに適用できる変換も提供します。私はこれがあまりにも賢明なコード賢明な何かを取るとは思わない..この場合、最大回転を90度またはπ/ 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の間のデルタを取得するだけで、変換せずに行うことができます。

関連する問題