元柔道整復師エンジニアBlog

- 元柔道整復師エンジニアBlog -

『 エンジニアをリングする。』

Info.plistのディレクトリ変更後のBuildPath設定について

Info.plistの階層を変更した後にBuildすると、以下のように発生する。

error: Build input file cannot be found: '/Users/username/***/Info.plist' (in target '***' from project '***')
Build input file cannot be found: '***/Info.plist'

そのため、Info.plistのディレクトリを変更した場合

Project Editor のBuild Settings > info.plist File を現在のInfo.plistファイルパスに変更すれば解消される。

例)Appというグループにinfo.plistを格納した場合
⇒ project名/App/info.plist

【Swift】TableViewCellの重複タップを防ぎたい(二度押し防止)

TableViewCellを複数回連続で選択した場合において、Cell選択時のイベント処理が複数回呼ばれてしまう。

前提

  • TableViewCellタップ時にVCへ遷移する。
  • tableView:didSelectRowAt内では、負荷の大きいサーバー通信後にVCへの画面遷移を行っている。

今回はTableViewCellを複数回選択した場合に、遷移先のVCが複数回呼ばれ重なって表示されてしまう。

原因としては、
Cell選択時のtableView:didSelectRowAtで負荷の大きいサーバー通信により、
後続処理である画面遷移までに遅延が発生し、その間に次のCell選択イベントが呼ばれてしまっている。

そのため、初回のCell選択イベントが終了する前に
複数のCell選択イベントが発生しているため、これを単一にする必要がある。

対策

その対策として、
初回のCell選択イベント発生時に、イベント終了までTableView全体のタッチイベントを無効にさせる。
isMultipleTouchEnabled または isUserInteractionEnabled
➡ 今回は、タッチイベントのみ対象とすればいいからisMultipleTouchEnabledを使用してみる。

初回または戻ってきた場合において、タッチイベントを有効にしておく。

override func viewWillAppear(_ animated: Bool) {
    tableView.isMultipleTouchEnabled = true
}


現在のCell選択イベント発生の有無により、タッチイベント操作を確定する。

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    // Cell選択イベント発生の有無を確認する
    if !tableView.isMultipleTouchEnabled {return}

    // タッチイベントを無効にする
    tableView.isMultipleTouchEnabled = false
    
    // 負荷の大きいサーバー通信処理を仮定して、0.5秒後に実行させる
    DispatchQueue.main.asyncAfter(wallDeadline: .now() + 0.5) {
        let vc = NextViewController.init(nibName: "NextViewController", bundle: nil)
        self.navigationController?.pushViewController(vc, animated: true)
    } 
}

イベント発生有の場合 ➡ 処理を中断する。
イベント発生無の場合 ➡ 処理続行しタッチイベントを無効にする。処理終了時にタッチイベントを有効に戻す。

isUserInteractionEnabledの場合であったても同様。

isMultipleTouchEnabled

When set to true, the view receives all touches associated with a multi-touch sequence and starting within the view's bounds. When set to false, the view receives only the first touch event in a multi-touch sequence that start within the view's bounds. The default value of this property is false.


trueに設定すると、ビューはマルチタッチシーケンスに関連付けられていてビューの境界内から始まるすべてのタッチを受け取ります。 falseに設定すると、ビューは、ビューの境界内で始まるマルチタッチシーケンスの最初のタッチイベントのみを受け取ります。このプロパティのデフォルト値はfalseです。

isMultipleTouchEnabled - UIView | Apple Developer Documentation

isUserInteractionEnabled

When set to false, touch, press, keyboard, and focus events intended for the view are ignored and removed from the event queue. When set to true, events are delivered to the view normally. The default value of this property is true.


falseに設定すると、ビューを対象としたtouch、press、keyboard、およびfocusイベントは無視され、イベントキューから削除されます。 trueに設定すると、イベントは通常通りビューに配信されます。このプロパティのデフォルト値はtrueです。
isUserInteractionEnabled - UIView | Apple Developer Documentation

まとめ

実際に動きも確認できた。

参考

【Swift】CollectionViewで複数の画像を横スクロールさせたい

複数の画像を1行で横スクロールできるように並べたい。 https://cdn-ak.f.st-hatena.com/images/fotolife/C/CHU-BURA/20190706/20190706194915_original.png 調べるとCollectionViewで再現できるみたいで、
けっこう参考になる記事がたくさんあって思いのほか簡単に再現できた。

そこで触れてみた感触としてTableViewと似ているが、
個人的にTableViewとは少し違うなと感じたCollectionViewの部分を書いていく。

スクロール方向の指定

TableViewとは異なり、縦または横スクロールが可能。
指定しない場合は、デフォルトの縦スクロールとなる。

delegate側で設定できるものだと思っていたが、基本的にはcell登録時にUICollectionViewFlowLayoutに対して設定するみたい。

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal // 横スクロール
collectionView.collectionViewLayout = layout

Cellサイズの設定

先ほど同様にcell登録時のUICollectionViewFlowLayoutに対して設定できる。

let layout = UICollectionViewFlowLayout()
let size = collectionView.frame.height
layout.itemSize = CGSize(width: size, height: size)
collectionView.collectionViewLayout = layout

または、TableView同様にdelegateであるUICollectionViewDelegateFlowLayoutでも設定できる。

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = collectionView.frame.height
        return CGSize(width: size, height: size)
    }
}

まとめ

簡単に縦、横画面のものを作ってみた。実際にはこんな感じ

20190706185626

参考

【Swift】UILabelで単位部分(◯秒・◯cm)の一部文字サイズを変更したい

UILabelとかで「10秒」や「10cm」といった、
ある単位を含む文字を表示させたい場合において、その単位部分のみ文字サイズを小さく表示させたいということがある。
→「〇〇」「〇〇cm」など

そのUILabelを一箇所でしか使用しない場合であれば、
以下のように直接Attributeで一部サイズを変更すれば問題なさそう。

しかし、
・複数箇所で使用する場合
・「10秒」「20秒」「30秒」といった値が可変する場合
・「10秒」「10cm」「10kg」といった単位が可変する場合
などでは上記の方法は現実的ではない....

そこでUILabelを拡張して、
上記の条件でも単位部分の文字サイズを変更できるようにしてみた。 ここでは、単位サイズを値の半サイズで表示させている。

Swift4

extension UILabel {
    func addUinit(unit: String, size: CGFloat) {
        guard let label = self.text else {
            return
        }
        // 単位との間隔
        let mainString = NSMutableAttributedString(string: label + " ")
        let unitString = NSMutableAttributedString(
            string: unit,
            attributes: [.font: UIFont.systemFont(ofSize: size)])
        let attributedString = NSMutableAttributedString()
        attributedString.append(mainString)
        attributedString.append(unitString)
        
        self.attributedText = attributedString
    }
}

// cm
label1.text = "10"
label1.addUinit(unit: "cm", size: label1.font.pointSize / 2)
print(label1.text!) // 10 cm

// 秒
label2.text = "60"
label2.addUinit(unit: "秒", size: label2.font.pointSize / 2)
print(label2.text!) // 60 秒

Swift3

Swift3では、 .font: UIFont.systemFont(ofSize: size)部分がNSAttributedString.Key.font : UIFont.systemFont(ofSize: size)になりそう。

参考

【Swift】Swift3から日付比較(Date)に演算子が扱えるみたいだった

Swift3で日付比較したい場面に出くわして、

いつものようにDateのcompareあたりを使おうとしてたら、
いつものようにNSComparisonResult(とくにorderedAscendingorderedDescending)でどちらがa < b、a < bとかがわからなくなって確認してたら....

とある記事で、
日付の比較に比較演算子が使えるのを今更しったわけです...
それもSwift3かららしく.....今更ながらお恥ずかしい😓

ってなわけで、さっそく使ってみる。

やること

  • 簡単な3つの日付を配列datesに加えて、それを降順で並び替える。
let calendar = Calendar.current
let date1 = calendar.date(from: DateComponents(year: 2017))!
let date2 = calendar.date(from: DateComponents(year: 2018))!
let date3 = calendar.date(from: DateComponents(year: 2019))!

var dates = [date1, date2, date3]

print(dates)
// [2016-12-31 15:00:00 +0000, 2017-12-31 15:00:00 +0000, 2018-12-31 15:00:00 +0000]

これまで(Swift2以前)

orderedAscendingorderedDescendingとで、
一見、どちらがa > bで、どちらがa < bなのかかがわかりづらい。

dates.sort { (d1, d2) -> Bool in
    return d1.compare(d2) == .orderedDescending 
}

print(dates)
// [2018-12-31 15:00:00 +0000, 2017-12-31 15:00:00 +0000, 2016-12-31 15:00:00 +0000]

Swift3以降

比較演算子が扱えることによって直感的でわかりやすくなった。

dates.sort { (d1, d2) -> Bool in
    return d1 > d2
}

print(dates)
// [2018-12-31 15:00:00 +0000, 2017-12-31 15:00:00 +0000, 2016-12-31 15:00:00 +0000]

参考