Swift備忘録




SwiftのUILongPressGestureRecognizerは二回呼ばれる

let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.longPressedButton))
button.addGestureRecognizer(longPressRecognizer)


こんな感じで登録したlongPressedButton関数は二回呼ばれる。
アラートなど呼び出す時は二度呼ばれてしまうので注意が必要。
UIGestureRecognizerのstateで下記のように切り分ける。

@objc func longPressedButton(sender:UIGestureRecognizer) {

    if (sender.state == .began){
      print("長押し開始のタイミング")      
      /*
      何かアラート処理を書いたり
      */
    } else if (sender.state == .ended) {
      print("長押し終了のタイミング")      
    }
}



Swift4でUITalbeViewの最下部にスクロールしたい

最下部にスクロールするにはscrollToRowを使うが、tableView.reloadData() 後すぐに実行すると正しくスクロールできない。
タイマーでタイミングを少しずらすことでうまくいった。
//tableViewに追加する処理をした後

tableView.reloadData()

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    self.tableView.scrollToRow(at: IndexPath(row: self.items.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}




Swiftで一度だけ実行したい時に使えるファンクション

viewDidLayoutSubviewsなど何度も呼び出されてしまうファンクション内で一度だけ実行したい処理があった場合に以下のような書き方をしていた。
これだと毎回フラグを宣言、条件分岐するのは面倒だし、可読性がよくないなと思っていた。

var isFirst = false

override func viewDidLayoutSubviews() { // updateLayout
  if !isFirst {
     isFirst = true

    // 一度だけ実行したい処理を書く
  }  
}


クロージャーとlazyを使ったスマートな書き方を見つけたので参考にさせていただいた。
https://qiita.com/codelynx/items/f0243d631f2448e89026

private lazy var onceUpdateLayout: (()->())? = {
  // 一度だけ実行したい処理を書く
    return nil
}()

override func viewDidLayoutSubviews() { // updateLayout
  onceUpdateLayout?()
}


処理をファンクションに分離できたため何をしているのかがわかり可読性が上がった。

他にも色々な書き方がいろいろあるようだ。

クロージャー(Closures)は名前のない関数で変数として扱われる。
lazy(lazy stored property)は、参照されるときに初めて初期値が設定されるプロパティ。



Swiftでメソッドチェーン実装、返り値を使わない時のwarningを消したい

メソッドチェーンを実装しようとした時に、返り値を使わない場合が多々ある。
Xcodeは関数の返り値を使わないと丁寧にwarningを出してくれるが、warningだらけになってしまうので対処したかった。
結論としては、@discardableResult アノテーションを付ければよいだけだった。

@discardableResult
func vibrate() -> Self {
    AudioServicesPlaySystemSound(1519)
    return self
}