希望这对于mod来说并不太模糊。
我想进行类似于某些高保真音量控制的用户互动,您可以向左或向右移动拨盘来改变音量但不是转动拨号完全转动,而是向左或向右转动你转得越多,音量变化的速度就越快,直到你放开它并回到中间。
在我的应用程序中,我想使用UIPanGestureRecogniser,当用户从中间向上平移时音量增加,从中间越远,增加越快。当它们平移到屏幕的中点以下时,音量会下降,从你的中间越远越快。
我所困扰的领域是如何在锁定UI的情况下实现这一目标。我不能只使用gestureRecognizer动作选择器,因为只有在有移动时才会调用它,为了使这种交互起作用,用户通常会将手指放在一个位置,同时等待到达正确的音量。
我觉得我想设置一个在gesturerecogniser选择器外运行的循环,并让它监视一个类变量,当手势移动或结束时,它会更新。如果它在手势识别器选择器中执行此操作,它将继续运行....
如果这是一个嵌入式系统,我会设置一些基于中断的轮询,以检查输入控件是什么,并继续添加到音量,直到它回到中间 - 无法找到可比较的iOS在这里。
建议是受欢迎的,如果这太模糊,那就抱歉了 - 这是一个特定代码问题的框架方法问题。
答案 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之间的增量即可。