20210913のSwiftに関する記事は5件です。

【Swift】FireStore × TableViewの実装(データの取得から反映までの格闘)

はじめに TableViewとFireStoreを掛け合わせた実装の際、データ取得から反映(特に反映)で詰まっていたので、次回実装時にスムーズに実装できるようにメモに残したい。 TL;DL 実装の際のポイント // tableView.reloadData()の実装のタイミングを間違うと表示されない tableView.reloadData() // こいつ!!! 実装例 ① 取得するデータを格納する配列を用意 ViewController.swift var memoDataArray: [MemoDataStore] = [] // ついでにFirestoreの下準備も let currentUser = Auth.auth().currentUser let db = Firestore.firestore() ② TableViewのデータの取得と更新 今回ここが抜けていたのが原因で表示されませんでした。 ViewController.swift override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 毎回データ更新してくれるように、viewWillAppearの中に記述する memoDataArray = getData() } func getData() -> [MemoDataStore] { let ref = db.collection("todos") ref.getDocuments { (snaps, err) in if let err = err { print("Error getting documents: \(err)") return } self.memoDataArray = snaps!.documents.map { document -> MemoDataStore in let data = MemoDataStore(document: document) return data }      // こいつ!!!! // データ取得が終わったタイミングでtableViewをリロードデータ self.tableView.reloadData() } return memoDataArray } class MemoDataStore: NSObject{ var todoText: String? var detailText: String? var dateText: String? init(document: QueryDocumentSnapshot) { let dic = document.data() self.todoText = dic["todo"] as? String self.detailText = dic["detail"] as? String self.dateText = dic["date"] as? String } } 配列に格納されたデータをTableViewに適応 ViewController.swift func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // カスタムセル使用しているが、今回は記載を省略 let cell = tableView.dequeueReusableCell(withIdentifier: "MemoCell") as! FirstViewTableViewCell cell.setCell(memo: memoDataArray[indexPath.row].todoText!, date: memoDataArray[indexPath.row].dateText!) return cell } これでFireStoreからデータを取得して、TableViewに表示するまでの実装できました。 まとめ tableView.reloadData()は、tableViewのデータを更新ためのメソッドであり、TableView上のデータにコード上で変更を加えた際は、変更が完了したタイミングで、reloadData()を忘れないこと 今回で言うと、FireStoreからデータが取得される前に、tableViewの表示が実装されて、データが配列に格納されていない状態を参照していたので、何も表示されなかった。 つまり、TableViewの表示のコードが読み込まれてから、用意した配列にデータが入っているため、何も表示されていないイメージ。 特に外部APIを叩いた際は、取得までに時間がかかり、非同期的にデータが取得されるため、iOSのライフサイクルに準拠して、実装することが大切
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

タップやスワイプの処理をさせない様にする(isUserInteractionEnabled)

今回の内容 .isUserInteractionEnabledでタップや長押しやスワイプ時の処理をさせない。 コードと簡単解説 これだけでviewに対するタップ、長押し、スワイプ時の処理を働かせることができなくなります。 view.isUserInteractionEnabled = false 使ってみる viewを5回タップしたら@objc func screenTap(){}の処理が働かなくなります。 import UIKit class ViewController: UIViewController { @IBOutlet weak var detectionResultLabel: UILabel! var tapCount = 0 override func viewDidLoad() { super.viewDidLoad() let tapDetection = UITapGestureRecognizer(target: self, action: #selector(screenTap)) view.addGestureRecognizer(tapDetection) } @objc func screenTap(){ if tapCount == 5{ self.view.isUserInteractionEnabled = false }else{ tapCount += 1 detectionResultLabel.text = "\(tapCount)回、画面をタップしました" } } } 終わり ご指摘、ご質問などありましたら、コメントまでお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】アプリ内でダークモード/ライトモードを設定したい

やりたいこと アプリ内で、OS設定によらずライトモード/ダークモードを設定したい。 設定方法 1行加えるだけで、即時にアプリ全体に反映されます。 ダークモードに設定する場合 ViewController.swift UIApplication.shared.windows.first?.overrideUserInterfaceStyle = .dark ライトモードに設定する場合 ViewController.swift UIApplication.shared.windows.first?.overrideUserInterfaceStyle = .light OS設定に従う場合 ViewController.swift UIApplication.shared.windows.first?.overrideUserInterfaceStyle = .unspecified 起動時に設定する場合は、initial view controllerの viewDidLoad() 内に書くと良いでしょう。 なお、AppDelegate内の application(_:didFinishLaunchingWithOptions:) 内に記載しても、うまく動作しません。(詳しく調べてはいませんが、おそらく、UIWindowオブジェクトがまだ構成されていないからではないかと思います。) 動作確認環境 Xcode: 12.5 iOS: 14.7.1 Swiftバージョン: Swift 5.4 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift] バージョン番号文字列の新旧比較をする。

やりたいこと x.y.z 形式のバージョン番号について、2つのバージョン番号間でどちらが新しいバージョンかをSwiftのコードで判定したい。 バージョン番号の比較例 2.0 > 1.5.1 1.6 > 1.5.24 1.10.1 > 1.7.55 1.5.3 < 1.5.11 ここでの前提として、バージョン番号はString型で定義されているものとします。 よくある間違い 検索してよく出てくる方法は、単純にcompareメソッドを使って文字列比較する方法です。 let result = ver1.compare(ver2) switch result { case .orderedAscending: print("ascending") case .orderedSame: print("same") case .orderedDescending: print("descending") } この方法はよく見かけるのですが、問題があります。 問題がある場合の例を以下に示します。 ver1 = "1.6" ver2 = "1.5.1" の場合  descending  正しい結果! ver1 = "1.10.1" ver2 = "1.7.55" の場合   ascending  ★間違った結果★  (1.10.1 は 1.7.55より新しいので、 decendingと出力されるべき。) 単純にcompareメソッドを使う比較方法では、各レベルの数字で比較する際に桁数が違うと正しい結果になりません。 上記の例では、文字列として比較されて 10 が 7 よりも先だと判定されてしまうため、上記のような誤った結果となってしまいます。 正しい方法 ではどうすればよいか。 「数字として比較する」オプションをつければ良いです。 let result = ver1.compare(ver2, options: [.numeric]) switch result { case .orderedAscending: print("ascending") case .orderedSame: print("same") case .orderedDescending: print("descending") } ver1 = "1.6" ver2 = "1.5.1" の場合  → descending 正しい結果! ver1 = "1.10.1" ver2 = "1.7.55" の場合   → descending 正しい結果! というわけで、compareでの比較時に .numeric オプションを忘れずにつけるようにしましょう。 動作確認環境 Xcode: 12.5 iOS: 14.7.1 Swiftバージョン: Swift 5.4 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift で Edit Distance (編集距離)

けんちょん本で学んだEdit Distance を Swift で書いたのでメモ 5.5動的計画法の例(2):編集距離 Edit Distance func minDistance(_ word1: String, _ word2: String) -> Int { let w1 = word1.map({ String($0) }) let w2 = word2.map({ String($0) }) var dp = [[Int]](repeating: [Int](repeating: Int.max, count: w2.count+1), count: w1.count+1) dp[0][0] = 0 for i in 0..<w1.count+1 { for j in 0..<w2.count+1 { if i-1 >= 0 && j-1 >= 0 { // noop if w1[i-1] == w2[j-1] { dp[i][j] = min(dp[i][j], dp[i-1][j-1]) } // replace else { dp[i][j] = min(dp[i][j], dp[i-1][j-1]+1) } } // delete if i-1 >= 0 { dp[i][j] = min(dp[i][j], dp[i-1][j]+1) } // insert if j-1 >= 0 { dp[i][j] = min(dp[i][j], dp[i][j-1]+1) } } } return dp[w1.count][w2.count] } REF Edit Distance - LeetCode
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む