【Swift】StoryBoard IDを遷移する時の注意点
どうも、ねこきち(@nekokichi1_yos2)です。
StoryBoardIDで遷移する時、下記のコードだとnilが返されて、エラーになります。
let vc = self.storyboard!.instantiateViewController(withIdentifier: "遷移先のStoryBoardID") as! 遷移先 self.present(vc, animated: true, completion: nil)
(オプショナルバインティングでもダメでした。)
直接storyboardを指定するのではなく、予めstoryboardを生成しておくと、うまくいきます。
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main) let vc = storyboard.instantiateViewController(withIdentifier: "遷移先のStoryBoardID") as! 遷移先 self.present(vc, animated: true, completion: nil)
(参考)
↓
【Swift】スワイプ動作を実装
どうも、ねこきち(@nekokichi1_yos2)です。
スワイプ 動作の実装は、UISwipeGestureRecognizer、を使用。
疑問
スワイプ動作を実装するUIGestureには、UIScreenEdgePanGestureRecognizer、がある。
だけど、ViewControllerに設置して、検知時の処理を登録しても、ちっとも反応してくれない。
何故だろうか?
import UIKit class Swipe_EdgePan: UIViewController { @IBOutlet weak var edge: UIScreenEdgePanGestureRecognizer! override func viewDidLoad() { super.viewDidLoad() edge.addTarget(self, action: #selector(edgepan)) } @objc func edgepan(_ recognizer:UIScreenEdgePanGestureRecognizer) { if recognizer.state == .recognized { print("aaa") } } }
結論
とりあえず、スワイプ動作は
- UIScreenEdgePanGestureRecognizer
ではなく
- UISwipeGestureRecognizer
で実装するように。
ソースコード
「コードでの実装」
import UIKit class Swipe_EdgePan: UIViewController { override func viewDidLoad() { super.viewDidLoad() let rightSwipe = UISwipeGestureRecognizer() rightSwipe.direction = .right rightSwipe.addTarget(self, action: #selector()) //#selector(関数名) let leftSwipe = UISwipeGestureRecognizer() leftSwipe.direction = .left leftSwipe.addTarget(self, action: #selector()) //#selector(関数名) self.view.addGestureRecognizer(rightSwipe) self.view.addGestureRecognizer(leftSwipe) } }
「StoryBoardの実装」
import UIKit class Swipe_EdgePan: UIViewController { @IBAction func rightSwipe(_ sender: UISwipeGestureRecognizer) { //左端から右へスワイプ print("right") } @IBAction func leftSwipe(_ sender: UISwipeGestureRecognizer) { //右端から左へスワイプ print("left") } }
【Swift】多次元配列を昇順でソート
どうも、ねこきち(@nekokichi1_yos2)です。
Int型の多次元配列をソートする方法が載ってなかったので、備忘録として記載。
バブルソート、というアルゴリズムを使うと、簡単に実装できました。
今回のバブルソートを実装する際、下記を参考にさせていただきました。
(donguriさん、ありがとうございました。)
↓
解説
今回使用する配列は、Int型の2次元配列です。
var array = [[2020,9,5],[2020,12,5],[2019,11,9],[2019,11,1],[2019,1,13],[2010,5,13],[2020,2,20],[2010,6,8]]
([年、月、日]の配列)
仕組みは、
- 隣り合う要素を比較
- 大小関係が成り立つかを調べる
- 成り立てば両方を入れ替える
- 1周して1度も成り立たなくなれば、終了
です。
repeat-while文は、条件式(flag)が
- trueなら継続
- falseなら抜ける
で、ループ度に条件式はfalseにリセットされ、入れ替えが起こればtrueになるのでまた1つ目の要素から大小関係の照合が行われ、1周ループしてfalseのままなら抜けます。
strideは、指定範囲(from~to)を指定数字刻み(by)でループするもので、今回の場合、
- 1,2,3,4,5,6,7
の数字がiに返されます。
(指定範囲の末尾は、to - 1、で、最後の数字は含まれません。)
swapAtは、引数に指定した配列内にある2つの要素を入れ替えます。
年のソート
stirdeにより、for文のiには1~7が入り、
- i番目
- i-1番目
の2つを比較します。
最初は0番目と1番目を比較し、最後は6番目と7番目を比較。
これで、年の昇順ソートが完成。
月のソート
もし月のソートだけ行えば、月を基準にソートされるので、年の昇順が崩れてしまいます。
ですから、年の順番を保ちつつ、月をソートする場合、年の条件式も加える必要があります。
既に年は昇順に並んでるので、後は比較対象である2つの要素が同じ年?、であるかを確認するだけです。
日のソート
既に年と月の昇順は成り立ってるので、比較対象が同じ年/月であるかを確かめればOKです。
ソースコード
//ループ判定 var flag = false var array = [[2020,9,5],[2020,12,5],[2019,11,9],[2019,11,1],[2019,1,13],[2010,5,13],[2020,2,20],[2010,6,8]] //年をソート func sortYear() { repeat { flag = false for i in stride(from: 1, to: array.count, by: 1) { if array[i][0] < array[i-1][0] { array.swapAt((i-1), i) flag = true } } } while flag } //月をソート func sortMonth() { repeat { flag = false for i in stride(from: 1, to: array.count, by: 1) { if array[i][0] == array[i-1][0] { if array[i][1] < array[i-1][1] { array.swapAt((i-1), i) flag = true } } } } while flag } //日をソート func sortDay() { repeat { flag = false for i in stride(from: 1, to: array.count, by: 1) { if array[i][0] == array[i-1][0] { if array[i][1] == array[i-1][1] { if array[i][2] < array[i-1][2] { array.swapAt((i-1), i) flag = true } } } } } while flag }
結果
//年のソート [[2010, 5, 13], [2010, 6, 8], [2019, 11, 9], [2019, 11, 1], [2019, 1, 13], [2020, 9, 5], [2020, 12, 5], [2020, 2, 20]] //年、月のソート [[2010, 5, 13], [2010, 6, 8], [2019, 1, 13], [2019, 11, 9], [2019, 11, 1], [2020, 2, 20], [2020, 9, 5], [2020, 12, 5]] //年、月、日のソート [[2010, 5, 13], [2010, 6, 8], [2019, 1, 13], [2019, 11, 1], [2019, 11, 9], [2020, 2, 20], [2020, 9, 5], [2020, 12, 5]]
【Swift】UISliderで5刻みに値を表示させる
どうも、ねこきち(@nekokichi1_yos2)です。
今回は、5を一定間隔とする、5刻みのスライダーを作ります。
(5、10、15、20、25、30....)
また、UISliderの幅を、0〜1000、とします。
解説
結論から言うと、
- if UISlider.value(スライダー値)/ 5 == 0 {}
を書けば、完成です。
しかし、下記のように不便なスライダーとなってしまいます。
↓
上記の問題は、5の倍数をうまく拾い切れていないこと、です。
0~1000の内、5の倍数は200個あります。
言い換えれば、1000個の内200個は5の倍数、です。
そして、UISlider.value(スライダー値)/ 5、は5の倍数の点(位置)を特定します。
例えると、下記の図になります。
↓
しかし、スライダーは値の領域ですので、点を指定するのは非効率です。
なので、スライダーの範囲が広いほど、つまみの同じ移動量でもスライダーの値は大きく変化するので、点(5の倍数)を確実に拾うためには、細かくつまみを動かさないといけません。
結果、つまみを早く動かすと、値が飛んで表示されてしまうのです。
したがって、スライダーが値の領域である以上、領域ごとに5の倍数を指定すればいいのです。
↓
(スライダー値が0~5なら5、5~10なら10、10~15なら15....)
方法は、if文で
- 5の倍数 → スライダー値
- 5の倍数でない → (スライダー値 / 5 + 1) x 5
です。
もし、スライダー値が5.0なら、そのまま表示。
もし、スライダー値が9.1なら、
- 9.1 / 5 = 1
- 1 + 1 = 2
- 2 x 5 = 10
- →10を表示
ということです。
↓
ソースコード
import UIKit class CustomSlider: UIViewController { @IBOutlet weak var slider: UISlider! @IBOutlet weak var label: UILabel! @IBAction func slider(_ sender: UISlider) { //5の倍数? if Int(sender.value) % 5 == 0 { //Labelに表示 label.text = "\(Int(sender.value))" } else { label.text = "\((Int(sender.value) / 5 + 1) * 5)" } } }
結果