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

【Swift】enumで文字列を受け取って文字列で返す

どういうことか たとえば "りんご" という文字列を受け取ると "アップル" を返してくれる。 受け取るほう enum EnglishName: String { case apple = "アップル" case grape = "グレープ" case orange = "オレンジ" init?(fruit: String) { switch fruit { case "りんご": self = .apple case "ぶどう": self = .grape case "みかん": self = .orange default: return nil } } } 送るほう let japaneseName = "りんご" var translate = EnglishName(fruit: japaneseName).rawValue print(translate) // "アップル" .rawValue をきちんと付けないとStringにならなくてハマる(ハマった)。 func で引数受け取って書くこともできるけど、可読性を考えるとこのほうが良いのかなと思いました。 おわり(´・ω・`)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Genericsパラメータを持つUIViewController/UIViewのStoryboardName/nibNameをいい感じに取得する

背景 StoryboardやXibをコードから呼び出す場合、その名前を文字列で指定する必要があります 文字列をベタ書きで指定するのは以下のデメリットがあります タイポする可能性が高まる タイポに気づけなければ無駄に時間を使ってしまう クラス名の更新時に呼び出しの文字列を変更し忘れて、StoryboardやXibが読み込めなくなる 読み込めなくなっていることをコンパイル時に気づけないので最悪そのままリリースしてバグになる その対策として以下のようなExtensionを活用している方も多いと思います(私もその一人です) extension NSObject { static var className: String { String(describing: self) } var className: String { Self.className } } 参考: https://qiita.com/tattn/items/bdce2a589912b489cceb この解決方法の課題 XibやStoryboardが紐付いているUIViewController/UIViewにGenericsパラメータがついている場合、正しく読み込めません 理由は、Genericsパラメータまでが className に含まれてしまうため、StoryboardNameやnibNameとずれてしまうからです class HogeViewController<T>: UIViewController {} print(HogeViewController<String>.className) // HogeViewController<String> // 本当は HogeViewController の部分だけがほしい 解決方法 Genericsパラメータの手前までをnibNameとして切り出します ※サンプルはUIViewControllerだけですが、UIViewも同様のExtensionを生やせばOKです extension UIViewController { static var nibName: String { let startIndex = className.startIndex let endIndex = className.firstIndex(of: "<") ?? className.endIndex return String(className[startIndex ..< endIndex]) } } 上記ロジックはクラス名に < が含まれていると意図しないところでnibName切れてしまいますが 私が試行錯誤した限りは < をGenericsパラメータ以外の中途半端な位置に入れるとコンパイルエラーがでるので大丈夫だと思います もし、抜け穴がありましたらコメントください 結果 Genericsパラメータがある場合 class HogeViewController<T>: UIViewController {} print(HogeViewController<String>.className) // HogeViewController<String> print(HogeViewController<String>.nibName) // HogeViewController Genericsパラメータがない場合 class FugaViewController: UIViewController {} print(FugaViewController.className) // FugaViewController print(FugaViewController.nibName) // FugaViewController
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift - FireBaseStore,ActiveLabelを用いて、ハッシュタグ機能を作成する

この記事の目標 ハッシュタグ機能を持ったアプリを作成する 前提条件 Xcode : 12.5 Swift : 5.4 使用したCocoaPods pod 'Firebase' pod 'Firebase/Firestore' pod 'ActiveLabel' 前準備 前準備として、入力した文字列を投稿し、投稿した文字列が一覧で表示されるアプリケーションを作成します。 データベースには、FireStore Databaseを使用しています。 以下の画像が、イメージ画像です。 コードは、以下のようになっています。 ViewController.swift import UIKit import FirebaseFirestore class ViewController: UIViewController { //文字列を入力するtextFieldに紐づいています。 @IBOutlet weak var textField: UITextField! //投稿するボタンに紐づいています。 @IBAction func send(_ sender: Any) { //入力された文字列をfireStoreのデータベースに保存します。 Firestore.firestore().collection("collection").document().setData(["text" : textField.text!]) //データを保存したら、画面遷移を行います。 let tableVC = storyboard?.instantiateViewController(identifier: "tableVC") as! TableViewController navigationController?.pushViewController(tableVC, animated: true) } } TableViewController.swift import UIKit import FirebaseFirestore class TableViewController: UIViewController,UITableViewDelegate, UITableViewDataSource { //取得したデータが入る配列 var textArray = [String]() //StoryBoard上のtableViewと紐づけています。 @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() //プロトコルの委任 tableView.delegate = self tableView.dataSource = self //データの取得、およびtableViewを読み込むメソッドを使用 loadData() } //tableのセルの数は、取得するデータ数に設定 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { textArray.count } //取得してきたデータを、セル内のラベル(textLabelに反映 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //storyBoard上のセルと、紐付けを行なっています。 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let textLabel = cell.contentView.viewWithTag(1) as! UILabel //取得してきた文字列をセルに反映しています。 textLabel.text = textArray[indexPath.row] return cell } //セルの高さは、適当に設定しています。 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return view.frame.size.height / 10 } //セクションの数は1です func numberOfSections(in tableView: UITableView) -> Int { return 1 } //fireStoreから、データを取得する関数 func loadData(){ Firestore.firestore().collection("collection").addSnapshotListener { snapShot, error in if let snapShotDoc = snapShot?.documents{ for doc in snapShotDoc { let data = doc.data() if let text = data["text"] as? String { print(text) //取得してきたデータを、配列に入れていく。 self.textArray.append(text) } } //データを取得し終わったら、tableViewを更新する。 self.tableView.reloadData() } } } } 本題 ハッシュタグの実装! ここからハッシュタグの実装に移ります。目標は、以下の画像のようにタップしたハッシュタグのデータだけを抽出して画面に表示させるようにします。 手順1 データベースに、ハッシュタグ別のデータを保存する。 ハッシュタグの投稿一覧を表示させるためには、選択されたハッシュタグに紐づいた投稿だけをデータベースから持ってこなければなりません。まずはそのために、ハッシュタグ別で管理されたデータをデータベースに格納する処理を記述します。 ViewController.swift import UIKit import FirebaseFirestore class ViewController: UIViewController { //文字列を入力するtextFieldに紐づいています。 @IBOutlet weak var textField: UITextField! //投稿するボタンに紐づいています。 @IBAction func send(_ sender: Any) { //入力された文字列を引数に、下記に記述したメソッドを呼んでいます。 Firestore.firestore().collection("collection").document().setData(["text" : textField.text!]) //追加部分 ================================================================== let hashTagText = textField.text as NSString? do{ let regex = try NSRegularExpression(pattern: "#\\S+", options: []) //見つけたハッシュタグを、for文で回す。 for match in regex.matches(in: hashTagText! as String, options: [], range: NSRange(location: 0, length: hashTagText!.length)) { //見つかったハッシュタグを引数に入れデータベースに保存 Firestore.firestore().collection(hashTagText!.substring(with: match.range)).document().setData(["text" : self.textField.text!]) } }catch{ } //追加部分 ================================================================== //データを保存したら、画面遷移を行います。 let tableVC = storyboard?.instantiateViewController(identifier: "tableVC") as! TableViewController navigationController?.pushViewController(tableVC, animated: true) } 以上の記述で、入力された文字列の中に、ハッシュタグがあった場合は、ハッシュタグの数だけ別のコレクションでデータベースに保存ができるようになりました。FireStoreの中身は、下記のようになり、ちゃんとハッシュタグ別でデータが格納されていることがわかります。 手順2 ハッシュタグをタップしたら、ハッシュタグ別一覧画面に遷移できるようにする。 手順2では、以下のことを行います。 ・一覧画面に表示されるハッシュタグを、タップできるようにする ・ハッシュタグをタップしたら、ハッシュタグ別一覧ページに画面遷移を行う。 まずは、遷移先のHashTagViewControllerを作成します。 この時点では、画面遷移元のTableViewControllerから、ハッシュタグの情報を取得するためのインスタンスを作成しておきます。 HashTagViewController.swift import UIKit class HashTagViewController: UIViewController { //TableViewControllerから、タップしたハッシュタグの情報を取得するため var hashTag = String() override func viewDidLoad() { super.viewDidLoad() } } 次に、ハッシュタグをタップしたら、ハッシュタグ別一覧ページに画面遷移を行う記述をします。 UILabelにハッシュタグを認識させるために、「ActiveLabel」というライブラリを使用します。 TableViewController.swift import UIKit import FirebaseFirestore import ActiveLabel //ライブラリを追加 ・ ・ ・ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //storyBoard上のセルと、紐付けを行なっています。 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let textLabel = cell.contentView.viewWithTag(1) as! ActiveLabel // ActiveLabelに変更する //取得してきた文字列をセルに反映しています。 textLabel.text = textArray[indexPath.row] //ハッシュタグをタップしたらどうなるかの処理を追加記述 ============================== textLabel.handleHashtagTap { hashTag in //storyBoard上のhashTagViewControllerをインスタンス化 let hashVC = self.storyboard?.instantiateViewController(identifier: "hashVC") as! HashTagViewController //hashVCへ、タップしたハッシュタグの情報を渡す hashVC.hashTag = hashTag //画面遷移 self.navigationController?.pushViewController(hashVC, animated: true) } //ハッシュタグをタップしたらどうなるかの処理を追加記述 ============================== return cell } 手順3 ハッシュタグ別に投稿一覧を表示するページを作成する 手順3では、以下の処理を記述します。 ・タップされたハッシュタグの情報をもとに、fireStoreからデータを抽出する ・抽出したデータを、HashTagViewControllerに表示させる ・HashTagViewController上でハッシュタグをタップした場合は、表示内容を更新する。 記述は、以下のようになります。 TableViewController.swift import UIKit import ActiveLabel import FirebaseFirestore class HashTagViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource { //TableViewControllerから、タップしたハッシュタグの情報を取得するため var hashTag = String() var textArray = [String]() @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() //プロトコルの委任 tableView.delegate = self tableView.dataSource = self //取得したハッシュタグのデータをもとに、fireStoreからデータを取得し画面に反映させる。 loadData(hashTag: hashTag) } //tableのセルの数は、取得するデータ数に設定 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { textArray.count } //取得してきたデータを、セル内のラベル(textLabelに反映 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //storyBoard上のセルと、紐付けを行なっています。 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let textLabel = cell.contentView.viewWithTag(1) as! ActiveLabel // ActiveLabelに変更する //取得してきた文字列をセルに反映しています。 textLabel.text = textArray[indexPath.row] //ハッシュタグをタップしたら、そのハッシュタグに適したデータを再ロードする textLabel.handleHashtagTap { hashTag in self.loadData(hashTag: hashTag) } return cell } //セルの高さは、適当に設定しています。 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return view.frame.size.height / 10 } //セクションの数は1です func numberOfSections(in tableView: UITableView) -> Int { return 1 } //fireStoreから、データを取得する関数 func loadData(hashTag:String){ //collectionの引数をhashTagにすることによって、選択したハッシュタグに関連するデータだけを抽出する。 Firestore.firestore().collection("#\(hashTag)").addSnapshotListener { snapShot, error in self.textArray = [] if let snapShotDoc = snapShot?.documents{ for doc in snapShotDoc { let data = doc.data() if let text = data["text"] as? String { print(text) //取得してきたデータを、配列に入れていく。 self.textArray.append(text) } } //データを取得し終わったら、tableViewを更新する。 self.tableView.reloadData() self.navigationItem.title = "#\(hashTag)" } } } } 以上で、記述は終了です。 最後に 読んでくださりありがとうございました! もし変な記述や、間違った記述があったらコメントしていただけると幸いです! 参考資料 Udemy 【iOS14対応】未経験者がiPhoneアプリ開発者になるための全て iOS Boot Camp ハッシュタグを判別するための記述の際に参考にした記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】PhotoLibraryからpeopleアルバムを取得したかった話

結論 現状、取得できない。 stack overflowで見つけた記事によると、"Peopleアルバムにアクセスできないのはバグor意図的に隠されてる可能性がある"とのこと。 取得できそうに見えるけどできない 通常、fetchAssetCollections(with:subtype:options:)でPhotoLibraryから取得したいアルバムを指定する。 with(内部引数はtype)には、ユーザが作成したアルバムか、システムが作成したアルバムかを指定。subtypeに具体的なアルバムの指定をする。 let collections = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumSyncedFaces, options: nil) collections.enumerateObjects { collection, _, _ in // 表示可能なアルバム名をprint print(collection.localizedTitle!) } お...?subtypeに.albumSyncedFacesとやらを指定できるっぽい...Peopleって顔認識だろうしこれっぽい...いける!!! 表示可能なアルバム名 Panoramas Videos Favorites Time-lapse Hidden Recently Deleted Recents Bursts Slo-mo Recently Added Selfies Screenshots Portrait Live Photos Animated Long Exposure Unable to Upload Peopleがない... 調べてみると.albumSyncedFacesというのは、Peopleに似た古い機能っぽい。 昔、iPhotoという、iTunesを介してiOSデバイスに同期する機能?があり、その中で、顔認識を使用して写っている人物をグループ化してアルバムにしていたらしい。おそらくこのアルバムのことを指している。 まとめ 元々は「iPhoneに保存されている写真から特定の人物の写真だけを抽出して〜...」と色々とやってみたいことがあり、Create MLで特定人物の顔を学習させたModelを生成すればいいのかなと思ったが、精度をあげるには色々と調整したり面倒なので、いっそのことPeopleアルバムから取得すればいいじゃん!って思ったが、そううまいこといかないようで残念。 まあ、悪意のある開発者が、第三者の端末の写真から特定人物の写真だけ裏で取得して...みたいなことが簡単にできるとセキュリティ的によくないだろうし使えないままで正解なのかもしれない。 参考 PHAssetCollectionType早見表 iOS Photos Framework: is it possible to access photos from “People” smart folder? Access “People ” album and getting images from it [duplicate]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】PhotoLibraryからPeopleアルバムを取得したかった話

結論 現状、取得できない。 stack overflowで見つけた記事によると、"Peopleアルバムにアクセスできないのはバグor意図的に隠されてる可能性がある"とのこと。 取得できそうに見えるけどできない 通常、fetchAssetCollections(with:subtype:options:)でPhotoLibraryから取得したいアルバムを指定する。 with(内部引数はtype)には、ユーザが作成したアルバムか、システムが作成したアルバムかを指定。subtypeに具体的なアルバムの指定をする。 let collections = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumSyncedFaces, options: nil) collections.enumerateObjects { collection, _, _ in // 表示可能なアルバム名をprint print(collection.localizedTitle!) } お...?subtypeに.albumSyncedFacesとやらを指定できるっぽい...Peopleって顔認識だろうしこれっぽい...いける!!! 表示可能なアルバム名 Panoramas Videos Favorites Time-lapse Hidden Recently Deleted Recents Bursts Slo-mo Recently Added Selfies Screenshots Portrait Live Photos Animated Long Exposure Unable to Upload Peopleがない... 調べてみると.albumSyncedFacesというのは、Peopleに似た古い機能(アルバム)を指しているっぽい。 昔、iPhotoという、iTunesを介してiOSデバイスに同期する機能?があり、その中で、顔認識を使用して写っている人物をグループ化してアルバムにしていたらしい。 ドキュメントにも似たようなことが書かれてた A Faces group synced to the device from iPhoto. https://developer.apple.com/documentation/photokit/phassetcollectionsubtype/albumsyncedfaces まとめ 元々は「iPhoneに保存されている写真から特定の人物の写真だけを抽出して〜...」と色々とやってみたいことがあり、Create MLで特定人物の顔を学習させたModelを生成すればいいのかなと思ったが、精度をあげるには色々と調整したり面倒なので、いっそのことPeopleアルバムから取得すればいいじゃん!って思ったが、そううまいこといかないようで残念。 まあ、悪意のある開発者が、第三者の端末の写真から特定人物の写真だけ裏で取得して...みたいなことが簡単にできるとセキュリティ的によくないだろうし使えないままで正解なのかもしれない。 参考 PHAssetCollectionType早見表 iOS Photos Framework: is it possible to access photos from “People” smart folder? Access “People ” album and getting images from it [duplicate]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む