質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

0回答

985閲覧

Swift4 円弧状のスライダーのつまみの動き(時計回り)と角度を反転させたい

maplemee

総合スコア16

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/08/21 12:15

編集2021/08/24 10:58

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π となる
円の右を 0 として基準となり、360°周回すると 2π となる

逆正弦atan2(アークタンジェント2)

atan2は点(x, y)とx軸によって表されるベクトルの角度(θ)を返します。
よって位置から角度を求めることができます。

参考:2つの座標から角度や距離を求める

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問