LoginSignup
3
2

UIKitでたまに遭遇するけど忘れがちなTipsメモ

Last updated at Posted at 2021-12-02

UIKitで、たまに遭遇するけど忘れがちな部分をメモっておきます。

ViewやCell等でControlイベントが取れない場合のチェックリスト

  • イベントを取りたいViewの isInteractionEnabled がtrueになってるか?
  • イベントを取りたいViewの alpha が0になっていないか?
    • backgroundColor = .clearにして、UIView.alphaは0より大きく設定する
  • イベントを取りたいViewの上に、透明系のViewが被さってて、それがイベントを奪っていないか?
    • storyboardでUIViewを作成すると、デフォルトで isInteractionEnabled = true になっているので、注意!
    • storyboardでなくても、コードで追加したUIViewも同様なので、注意!
    • イベントを透過させたいUIViewは、isInteractionEnabledfalseにする
  • 親のViewの大きさが意図せず小さくなっていないか?
    • 親Viewからはみ出てる領域では、tapなどのイベントが取れない
    • AutoLayoutの制約が足りてないとwidthやheightが0になる事がある
  • そもそもoverride func hitTest()は取れてるか?
    • pointInside()
  • 参考: https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events

UICollectionViewのselection管理

UICollectionViewやUITableViewには、セルの選択状態を管理する機能が備わっています。

  • isSelectedは、collectionView側で管理されている
  • スクロールでCellが見えなくなり、(Cellが再利用され)、スクロールで再度表示した際にはcell.isSelectedが復元されている
  • collectionView.indexPathsForSelectedItems()で、選択状態のindexPathの配列が取れる

Cellは画面外にスクロールすると再利用されるのに、選択状態は見えない範囲も全て管理してるのが、意外といえば意外でした。

UITextFieldのshouldChangeCharactersInをRangeで扱う

public func textField(_ textField: UITextField, shouldChangeCharactersIn nsRange: NSRange, replacementString string: String) -> Bool {
    guard let text = textField.text,
          let range = Range(nsRange, in: text) else {
        return false
    }
    let newText = text.replacingCharacters(in: range, with: string)
    /* ... */
}

NavigationBarをスクロールの方向で隠す

比較的簡単でTwitterっぽい自然な感じの動きにしてみました。
バウンスに反応しないようisTrackingを見ています。

class MyViewController {

  @IBOutlet private var collectionView: UICollectionView!
  private var lastContentOffset: CGPoint = .zero
  private var lastTracking = false

  override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.delegate = self
  }

  // 画面遷移前には表示に戻しておく
  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.setNavigationBarHidden(false, animated: true)
  }
}

extension MyViewController: UIScrollViewDelegate {

  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let dy = scrollView.contentOffset.y - lastContentOffset.y
    lastContentOffset = scrollView.contentOffset
    let didTouchUp = lastTracking && !scrollView.isTracking
    lastTracking = scrollView.isTracking
    // 下にスクロールしたら隠す
    if scrollView.isTracking && dy > 5 {
      navigationController?.setNavigationBarHidden(true, animated: true)
    }
    // 指を離したタイミングで上にスクロールしていたら表示
    if didTouchUp && dy < -10 {
      navigationController?.setNavigationBarHidden(false, animated: true)
    }
  }
}

Darkモード対応やアイコンなど

  • まずLight/Darkで色名を共通化し、Asset Colorで定義する (自動的に切り替わる)
    • コードからもAssetの色を使う
  • 単色のIconのLight/Darkの変化や色バリエーションは、コードでtintColorで設定する(管理するIconの枚数が減る)
    • Light/Darkでイメージを変える場合は、Dark Appearanceに登録する
  • UIColorの置き換えが無理な場合は、UIView.traitCollectionDidChange()で切り替えタイミングを検出できるので、そこで更新する
    • 現在のモードは、UITraitCollection.current.userInterfaceStyleで判別できる
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2