swift3で書かれたgithubを参考にしております。
やりたいこと:以下の青い「Single Slider」を「左下と右下に位置する始点(0%)と終点(100%)」を、「始点(0%)を左上、終点(100%)を右上」に設置し、スライダーのつまみを真ん中の円の下側を潜るように反時計回りに半周して終わるように変更を加えたい。(そっくりそのまま逆さまの状態に)
やったこと
始点と終点をstartAngleとendAngleを210と-30から130と50に変更することで、左上と右上に移動させて円弧の描写をclockwise: falseで反転させることに成功。そこから先のスライダーの動きに関してどうやったら反時計回りでかつ0%から100%に辿り着けるようになるかまだ、解決策を考えている。
![
動きに関しては0%からはうまく始まるが、スライダーのつまみが100%の終点を超えて180度近く、先のところまで可動してしまう。円弧の計算の仕組みが難しいため、わかるまで苦戦中です。
SliderKit
1clockwiseをtrueだと時計回り, falseだと反時計回り 2 // UIBezierPathのインスタンス生成 3 let ovalBarPath = UIBezierPath() 4 // UIBezierPathを円弧に設定 5 ovalBarPath.addArc(withCenter: CGPoint(x: ovalBarRect.midX, y: ovalBarRect.midY), // 円弧の中心 6 radius: ovalBarRect.width / 2, // 半径 7 startAngle: -startAngle * CGFloat.pi/180, // 開始角度 8 endAngle: -endAngle * CGFloat.pi/180, // 終了角度 9 clockwise: true) // trueだと時計回り, falseだと反時計回り
SliderKit
1スライダーのつまみの動作に関する部分 2let selectionAngle: CGFloat = -1 * progress * (startAngle + abs(endAngle)) + startAngle 3 4 ///selectionThumb Drawing 5 context.saveGState() 6 // 中心点 7 context.translateBy(x: 45, y: 46) //45 8 // 回転する 9 context.rotate(by: -selectionAngle * CGFloat.pi/180) 10 11 let selectionThumbPath = UIBezierPath(ovalIn: CGRect(x: 36.3, y: -4.13, width: 8, height: 8)) 12 thumbColor.setFill() 13 selectionThumbPath.fill() 14 //最近保存された状態に設定 15 context.restoreGState()
やったこと②
以下の部分にて、selectionAngleからマイナスをなくし、CGFloat.pi/180を150にしたところ
context.rotate(by: **-**selectionAngle * CGFloat.pi/180)
↓
context.rotate(by: selectionAngle * CGFloat.pi/105)
少しだけ理想的な動きになりましたが、スライダーの操作がスライダーとシンクロしなくなってしまった。
文字数の制限で全て載せられないのですが、メインのファイルは以下です。
ViewController
1import UIKit 2 3class ViewController: UIViewController { 4 5 @IBOutlet weak var sliderView: PRGRoundRangeSlider! 6 //今回扱っているスライダー 7 @IBOutlet weak var sliderContainerView: UIView! 8 9 override func viewDidLoad() { 10 super.viewDidLoad() 11 〜省略〜 12 DispatchQueue.main.async { 13 self.setupSliderProgrammatically() 14 } 15 } 16 17 func setupSliderProgrammatically(){ 18 19 let sliderView = PRGRoundSlider( 20 frame: sliderContainerView.bounds, 21 value: 0.5, 22 strokeColor: SliderKit.darkBlueColor, //SliderKit 23 strokeWidth: 5, 24 gradientColor: SliderKit.darkPinkColor, //SliderKit 25 startAngle: 130,// 210, 26 endAngle: 50, //-30, 27 startText: "0%", 28 endText: "100%" 29 ) //クロージャー 30 { (value) in 31 return "(Int(value*100))%" //0.5→50%にしてreturn 32 } 33 sliderContainerView.addSubview(sliderView) 34 } 35}
追記:8/23 12:00
スライダーの動きの範囲に関しては、PRGRounderSlider.swiftのここの部分で変更できる可能性があるのでどうやって動いているかじっくり試行錯誤で変更を試している状況です。
PRGRounderSlider
1override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 2 super.touchesBegan(touches, with: event) 3 updateRotationWithTouches(touches) 4 5 } 6 7 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 8 super.touchesMoved(touches, with: event) 9 updateRotationWithTouches(touches) 10 } 11 12 func updateRotationWithTouches(_ touches: Set<NSObject>) { 13 if let touch = touches[touches.startIndex] as? UITouch { 14 let rotation = rotationForLocation(touch.location(in: self)) 15 16 // convert rad to angles 17 var realRotationAngle = -(rotation * 180 / CGFloat(Double.pi)) //M_PI 18 19 20 // We need to convert the rotation angle into paintcode oval angle 21 if realRotationAngle > -180 && realRotationAngle < -90 { 22 realRotationAngle = 180 + (180+realRotationAngle) 23 } 24 25 let prg = (realRotationAngle - startAngle)/(-1 * (startAngle + abs(endAngle))) 26 value = prg <= 0.5 ? max(0,prg) : min(1,prg) 27 } 28 }
8/23 15:40
質問:やはり上のコード部分(func updateRotationWithTouches)の仕組みが理解できません。
以下の部分ではmaxとminがあるのでここで、始点と終点の可動する制限を設定しているのでしょうか?
let prg = (realRotationAngle - startAngle)/(-1 * (startAngle + abs(endAngle))) value = prg <= 0.5 ? max(0,prg) : min(1,prg)
8/24 現状・メモ
タッチした回数が初回だった場合 if let touch = touches[touches.startIndex] as? UITouch { rotationにrotationForLocationの座標を渡す let rotation = rotationForLocation(touch.location(in: self)) ・・・続く
上のコードの引数:rotationForLocation(touch.location(in: self))について
fileprivate func rotationForLocation(_ location: CGPoint) -> CGFloat { タッチした部分のX座標の半分とy座標の半分をoffsetとして let offset = CGPoint(x: location.x - bounds.midX, y: location.y - bounds.midY) その座標を返す? return atan2(offset.y, offset.x) }
・・・続き // convert rad to angles var realRotationAngle = -(rotation * 180 / CGFloat(Double.pi)) // We need to convert the rotation angle into paintcode oval angle 両オペランド(両辺)がtrueだったとき if realRotationAngle > -180 && realRotationAngle < -90 { realRotationAngle = 180 + (180+realRotationAngle) //180 } let prg = (realRotationAngle - startAngle) / (-1 * (startAngle + abs(endAngle))) value = prg <= 0.5 ? max(0,prg) : min(1,prg) }
〜〜自分用メモ〜〜
円の右を 0 として基準となり、360°周回すると 2π となる
逆正弦atan2(アークタンジェント2)
atan2は点(x, y)とx軸によって表されるベクトルの角度(θ)を返します。
よって位置から角度を求めることができます。
あなたの回答
tips
プレビュー