【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)

(参考)

stackoverflow.com

【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の実装」

f:id:nekokichi_yos2:20200428101641p:plain

f:id:nekokichi_yos2:20200428101652p:plain

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さん、ありがとうございました。

note.com

 

 

解説

 

今回使用する配列は、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の倍数の点(位置)を特定します。

 

例えると、下記の図になります。

f:id:nekokichi_yos2:20200426222359p:plain

しかし、スライダーは値の領域ですので、点を指定するのは非効率です。


なので、スライダーの範囲が広いほど、つまみの同じ移動量でもスライダーの値は大きく変化するので、点(5の倍数)を確実に拾うためには、細かくつまみを動かさないといけません。

 

結果、つまみを早く動かすと、値が飛んで表示されてしまうのです。

 

したがって、スライダーが値の領域である以上、領域ごとに5の倍数を指定すればいいのです。

 ↓

f:id:nekokichi_yos2:20200426222402p:plain

(スライダー値が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を表示

ということです。

f:id:nekokichi_yos2:20200426220925p:plain

 

ソースコード

 

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)"
        }
    }
    
}

 

結果