- 投稿日:2021-01-24T23:20:05+09:00
ちゃんと紐づけてるのにunrecognized selector sent to instanceエラーが出る時の対処方法
このエラーに数日悩まされたので備忘録として残します。
Bar Button Itemタップ時の処理を実装していたところ、
以前までは正常に実装できていたものが、「unrecognized selector sent to instance」のエラーで前に進めなくなった。調べてみると、いくつか要因があるとのこと
- identity inspectorのClassの設定が間違っている
- Inherit Module From Targetにチェックが入っていない(自分はこれが原因だった)
- タップ時のアクションを示す関数にて、引数(例:_ sender: UIBarButtonItem)が設定されていない
(2が原因の場合、Inherit Module From Targetにチェックを入れるか、もしくは、Moduleに手打ちすればよいとのこと)
参考になればいいな
- 投稿日:2021-01-24T22:58:54+09:00
【Swift】UITextFieldのキーボードを閉じる方法
UITextFieldに入力時、キーボードタイプをNumber PadにするとReturnキーが無くて
textFieldShouldReturn
で閉じることが出来なくて困ったのでキーボードを閉じる方法を調べました。キーボードを閉じる方法 その①
UITextFieldDelegate
のtextFieldShouldReturn
にキーボードを閉じる処理を書いて、キーボードのリターンキーを押すとキーボードが閉じてくれる方法func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true }その方法で困った事
キーボードタイプがNumber Padだとリターンキーが無くて困った。
textFieldShouldReturn
が使えないじゃないか、、、キーボードを閉じる方法 その②
InputAccesoryView
に完了ボタンを追加して、ボタンをトリガーにしてキーボードを閉じてもらう方法こんな感じでキーボード上に完了ボタンを出現させる。
@IBOutlet weak var numberPadTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() //inputAccesoryViewに入れるtoolbar let toolbar = UIToolbar() //完了ボタンを右寄せにする為に、左側を埋めるスペース作成 let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) //完了ボタンを作成 let done = UIBarButtonItem(title: "完了", style: .done, target: self, action: #selector(didTapDoneButton)) //toolbarのitemsに作成したスペースと完了ボタンを入れる。実際にも左から順に表示されます。 toolbar.items = [space, done] toolbar.sizeToFit() //作成したtoolbarをtextFieldのinputAccessoryViewに入れる numberPadTextField.inputAccessoryView = toolbar //キーボードタイプを番号のみに指定 numberPadTextField.keyboardType = .numberPad } //完了ボタンを押した時の処理 @objc func didTapDoneButton() { numberPadTextField.resignFirstResponder() }キーボードを閉じる方法 その③
ViewControllerにTap Gesture Recognizerを配置して、画面をタップされたらキーボードを閉じる方法
1.
Tap Gesuture Recognizer
をViewController
に配置2.
Tap Gesuture Recognizer
をcontrol
+ ドラッグでViewController
と紐付け3.紐付けしたTap Actionにキーボードを閉じる処理を書く
@IBAction func didTapView(_ sender: UITapGestureRecognizer) { //キーボードを閉じる処理 view.endEditing(true) }なんで
view.endEditing(true)
がキーボードを閉じてくれるのか?公式ドキュメントより引用
Causes the view (or one of its embedded text fields) to resign the first responder status.
ビュー(またはその埋め込みテキストフィールドの1つ)にファーストレスポンダーステータスを辞任させます。
ということで
view.endEditing()
の引数にtrueを入れると、キーボードが現れている場合はtextField.resignFirstResponder()
してくれるのでキーボードを閉じてくれます。キーボードを閉じる方法 その④
その③の画面タップという方法は同じですが、実装がスマートなパターン
Tap Gesture Recognizer
を配置したり、ファイルに紐付けたりしなくて良いです。これを宣言するだけ。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) }サンプルデモ
キーボード上の
inputAccesoryView
の完了ボタンと画面タップでキーボードを閉じています。
まとめ
些細なことから調べ出したら色んな方法を知ることが出来た。
これも参考にさせてもらった記事のおかげです。
ありがとうございました。
是非、参考元の記事も見に行ってみてください☺︎また何か間違いやより良い方法がありましたら、優しく教えていただけると幸いです?♂️
参考
UTextFieldのソフトキーボードを数値のみにして完了ボタンを追加する
[Swift]キーボード以外をタッチするとキーボードが下がる方法
Apple公式ドキュメント: endEditing(_:)
- 投稿日:2021-01-24T17:44:39+09:00
AppStoreConnectで連絡先情報が保存されない
- 投稿日:2021-01-24T16:38:07+09:00
[iOS] git pushでLintチェックを行う(Git Hooks + SwiftLint)
こんにちは。都内でiOSエンジニアをしている @zrn-ns です。
弊社のプロジェクトではSwiftLintを利用しており、最低限のコーディング規約についてはSwiftLintでチェックする事ができるようになっています。
SwiftLintはビルドフェーズに含めることも想定されており、公式のガイドに従うだけでビルド時にLintチェックを行い、規約に沿った記述を徹底させることができます。
しかし今回諸事情があり、ビルドフェーズではなく、Gitでリモートリポジトリにpushする直前でLintチェックを行うようにしてみました。
なぜビルドフェーズではなく、Push時にチェックするのか
ビルドフェーズで実行する方式だとビルド時に多少のオーバーヘッドが発生してしまいます(差分チェックを行ってくれるようなので、ほぼ気にならないレベルですが)。
また弊社社内ではXcode派とAppCode派が存在します。AppCodeではSwiftLintのプラグインが存在するため、Lintチェックをビルドフェーズに含める方式だと、2重でLintチェックが行われてしまうことになります。これらの問題を解消するため、ビルドフェーズでLintチェックを行うのをやめ、リモートリポジトリにコードをpushするタイミングでLintチェックを行うようにしてみました。
どうやってPush時に任意のスクリプトを実行するのか
Gitには、各アクション(commit, push, merge等)の実行の前後に任意のスクリプトを実行するためのGit Hooksという仕組みがあります。
Git Hooksでは、commitやpush, mergeの前後のタイミングで、各Gitリポジトリの
.git/hooks/
ディレクトリ下に配置したスクリプトが自動的に実行されます。
例えば、pushの直前でスクリプトを実行したい場合、.git/hooks/pre-push
というスクリプトが存在すれば、それが自動的に呼び出されます。Git Hooksを利用して、push時にSwiftLintを実行する
今回はGit Hooksの設定を全メンバー間で共有できるようにし、全メンバーがpush前にLintチェックを行うことを強制できるようにします。
スクリプトの作成
.git/
ディレクトリ配下はgitで管理されない(コミットできない)ため、.git/hooks/pre-push
にスクリプトを記述するとメンバー間で設定を共有することができません。そこで、git管理下にGit Hooks用のスクリプトを入れておくための専用のディレクトリを作成し、そこに配置したpre-pushスクリプトを参照するようにgitの設定を変更します。
cd {プロジェクトディレクトリ} # git hooksを入れるためのディレクトリとスクリプトを作成 mkdir .githooks touch .githooks/pre-push # スクリプトに実行権限を与える chmod 755 .githooks/pre-push場合によってはLintチェックを無視してpushしたい場合もあるかと思うので、Lintチェックでエラーがあった場合、pushを続行するか選択できるようにします。
pre-pushのスクリプトの中身は下記のようにします。(※swiftlintのバイナリへのパスは適宜書き換えてください)
.git/hooks/pre-push#!/bin/bash set -eu cd `dirname $0` echo -n 'Linting... ' lint_result=$(swiftlint --reporter emoji --quiet --path ../ --config ../.swiftlint.yml) if [ -n "$lint_result" ]; then # Lintで警告が見つかった場合、push操作を続けるかユーザに判断させる echo 'Some issues found.' echo '=====================================' echo "$lint_result" | sed -e "s/^/>> /" echo '=====================================' echo '' echo -n 'Push anyway?[y/N]: ' exec < /dev/tty read YN if [ "$YN" = "y" ]; then echo 'Ok. continue.' else echo 'Push aborted.'; exit 1; fi else echo 'No issues found!' fi exit 0最後に、Git Hooksの読み込み先ディレクトリを変更します。
この設定は各自の環境で実行する必要があるので、環境構築手順書に追加するのが良いかと思います。# Git Hooksのディレクトリを.githooksディレクトリに変更 git config --local core.hooksPath .githooks/動作確認
実際に動作させてみた結果は下記のような感じになります。
規約違反なし
% git push origin HEAD Linting... No issues found! Enumerating objects: 33, done. Counting objects: 100% (33/33), done. Delta compression using up to 8 threads Compressing objects: 100% (27/27), done. Writing objects: 100% (33/33), 8.73 KiB | 2.18 MiB/s, done. Total 33 (delta 5), reused 0 (delta 0) remote: Resolving deltas: 100% (5/5), done. To github.com:zrn-ns/swiftlint_on_pre_push.git * [new branch] HEAD -> main %規約違反あり -> push続行
% git push origin HEAD Linting... Some issues found. ===================================== >> /path/to/project/AppDelegate.swift >> ⚠️ Line 36: Files should have a single trailing newline. >> ⚠️ Line 34: Limit vertical whitespace to a single empty line. Currently 2. >> /path/to/project/SwiftLintOnPrePush/SceneDelegate.swift >> ⚠️ Line 52: Files should have a single trailing newline. >> ⚠️ Line 50: Limit vertical whitespace to a single empty line. Currently 2. ===================================== Push anyway?[y/N]: y Ok. continue. Enumerating objects: 33, done. Counting objects: 100% (33/33), done. Delta compression using up to 8 threads Compressing objects: 100% (27/27), done. Writing objects: 100% (33/33), 8.73 KiB | 2.18 MiB/s, done. Total 33 (delta 5), reused 0 (delta 0) remote: Resolving deltas: 100% (5/5), done. To github.com:zrn-ns/swiftlint_on_pre_push.git * [new branch] HEAD -> main %規約違反あり -> push中止
% git push origin HEAD Linting... Some issues found. ===================================== >> /path/to/project/AppDelegate.swift >> ⚠️ Line 36: Files should have a single trailing newline. >> ⚠️ Line 34: Limit vertical whitespace to a single empty line. Currently 2. >> /path/to/project/SwiftLintOnPrePush/SceneDelegate.swift >> ⚠️ Line 52: Files should have a single trailing newline. >> ⚠️ Line 50: Limit vertical whitespace to a single empty line. Currently 2. ===================================== Push anyway?[y/N]: n Push aborted. error: failed to push some refs to 'git@github.com:zrn-ns/swiftlint_on_pre_push.git' %サンプルプロジェクト
サンプルプロジェクトはこちらにアップしました。
FIXME
今回の手法では、Git Hooksのディレクトリをgit管理下に置くことで、全メンバーがスクリプトを共有する方法をとっています。
この方法は、スクリプトに修正が入った際にも環境間の同期が取れて便利な半面、各メンバーが独自のGit Hooksスクリプトを配置できなくなってしまいます。
(masterへの直pushを防止したりするためにもGit Hooksは便利に使えるので、個人でスクリプトを配置したい場面は多いと思います)何か良いアイディアがあれば教えていただけると幸いです?
まとめ
push前にSwiftLintのチェックを行う方法を解説しました。
この方法を使えば、各ビルドごとにLintチェックのオーバーヘッドがかかることを防げますし、PullRequestの作成前に問題に気づくことができるので、PullRequest作成後にコードを微調整するコストをかなり減らせるはずです。
ただし、新規メンバーが参入した場合には、ビルドごとにLintチェックを掛けてあげたほうが定着は早そうなので、その際はビルドフェーズに追加する方法でLintチェックを行ったほうが良いのかなと思います。
謝辞
下記のサイトを参考にさせていただきました。
- 投稿日:2021-01-24T16:38:07+09:00
GitHooksを利用し、リモートリポジトリにPushするときにSwiftLintを実行する
こんにちは。都内でiOSエンジニアをしている @zrn-ns です。
弊社のプロジェクトではSwiftLintを利用しており、最低限のコーディング規約についてはSwiftLintでチェックする事ができるようになっています。
SwiftLintはビルドフェーズに含めることも想定されており、公式のガイドに従うだけでビルド時にLintチェックを行い、規約に沿った記述を徹底させることができます。
しかし今回諸事情があり、ビルドフェーズではなく、Gitでリモートリポジトリにpushする直前でLintチェックを行うようにしてみました。
なぜビルドフェーズではなく、Push時にチェックするのか
ビルドフェーズで実行する方式だとビルド時に多少のオーバーヘッドが発生してしまいます(差分チェックを行ってくれるようなので、ほぼ気にならないレベルですが)。
また弊社社内ではXcode派とAppCode派が存在します。AppCodeではSwiftLintのプラグインが存在するため、Lintチェックをビルドフェーズに含める方式だと、2重でLintチェックが行われてしまうことになります。これらの問題を解消するため、ビルドフェーズでLintチェックを行うのをやめ、リモートリポジトリにコードをpushするタイミングでLintチェックを行うようにしてみました。
どうやってPush時に任意のスクリプトを実行するのか
Gitには、各アクション(commit, push, merge等)の実行の前後に任意のスクリプトを実行するためのGit Hooksという仕組みがあります。
Git Hooksでは、commitやpush, mergeの前後のタイミングで、各Gitリポジトリの
.git/hooks/
ディレクトリ下に配置したスクリプトが自動的に実行されます。
例えば、pushの直前でスクリプトを実行したい場合、.git/hooks/pre-push
というスクリプトが存在すれば、それが自動的に呼び出されます。Git Hooksを利用して、push時にSwiftLintを実行する
今回はGit Hooksの設定を全メンバー間で共有できるようにし、全メンバーがpush前にLintチェックを行うことを強制できるようにします。
スクリプトの作成
.git/
ディレクトリ配下はgitで管理されない(コミットできない)ため、.git/hooks/pre-push
にスクリプトを記述するとメンバー間で設定を共有することができません。そこで、git管理下にGit Hooks用のスクリプトを入れておくための専用のディレクトリを作成し、そこに配置したpre-pushスクリプトを参照するようにgitの設定を変更します。
cd {プロジェクトディレクトリ} # git hooksを入れるためのディレクトリとスクリプトを作成 mkdir .githooks touch .githooks/pre-push # スクリプトに実行権限を与える chmod 755 .githooks/pre-push場合によってはLintチェックを無視してpushしたい場合もあるかと思うので、Lintチェックでエラーがあった場合、pushを続行するか選択できるようにします。
pre-pushのスクリプトの中身は下記のようにします。(※swiftlintのバイナリへのパスは適宜書き換えてください)
.git/hooks/pre-push#!/bin/bash set -eu cd `dirname $0` echo -n 'Linting... ' lint_result=$(swiftlint --reporter emoji --quiet --path ../ --config ../.swiftlint.yml) if [ -n "$lint_result" ]; then # Lintで警告が見つかった場合、push操作を続けるかユーザに判断させる echo 'Some issues found.' echo '=====================================' echo "$lint_result" | sed -e "s/^/>> /" echo '=====================================' echo '' echo -n 'Push anyway?[y/N]: ' exec < /dev/tty read YN if [ "$YN" = "y" ]; then echo 'Ok. continue.' else echo 'Push aborted.'; exit 1; fi else echo 'No issues found!' fi exit 0最後に、Git Hooksの読み込み先ディレクトリを変更します。
この設定は各自の環境で実行する必要があるので、環境構築手順書に追加するのが良いかと思います。# Git Hooksのディレクトリを.githooksディレクトリに変更 git config --local core.hooksPath .githooks/動作確認
実際に動作させてみた結果は下記のような感じになります。
規約違反なし
% git push origin HEAD Linting... No issues found! Enumerating objects: 33, done. Counting objects: 100% (33/33), done. Delta compression using up to 8 threads Compressing objects: 100% (27/27), done. Writing objects: 100% (33/33), 8.73 KiB | 2.18 MiB/s, done. Total 33 (delta 5), reused 0 (delta 0) remote: Resolving deltas: 100% (5/5), done. To github.com:zrn-ns/swiftlint_on_pre_push.git * [new branch] HEAD -> main %規約違反あり -> push続行
% git push origin HEAD Linting... Some issues found. ===================================== >> /path/to/project/AppDelegate.swift >> ⚠️ Line 36: Files should have a single trailing newline. >> ⚠️ Line 34: Limit vertical whitespace to a single empty line. Currently 2. >> /path/to/project/SwiftLintOnPrePush/SceneDelegate.swift >> ⚠️ Line 52: Files should have a single trailing newline. >> ⚠️ Line 50: Limit vertical whitespace to a single empty line. Currently 2. ===================================== Push anyway?[y/N]: y Ok. continue. Enumerating objects: 33, done. Counting objects: 100% (33/33), done. Delta compression using up to 8 threads Compressing objects: 100% (27/27), done. Writing objects: 100% (33/33), 8.73 KiB | 2.18 MiB/s, done. Total 33 (delta 5), reused 0 (delta 0) remote: Resolving deltas: 100% (5/5), done. To github.com:zrn-ns/swiftlint_on_pre_push.git * [new branch] HEAD -> main %規約違反あり -> push中止
% git push origin HEAD Linting... Some issues found. ===================================== >> /path/to/project/AppDelegate.swift >> ⚠️ Line 36: Files should have a single trailing newline. >> ⚠️ Line 34: Limit vertical whitespace to a single empty line. Currently 2. >> /path/to/project/SwiftLintOnPrePush/SceneDelegate.swift >> ⚠️ Line 52: Files should have a single trailing newline. >> ⚠️ Line 50: Limit vertical whitespace to a single empty line. Currently 2. ===================================== Push anyway?[y/N]: n Push aborted. error: failed to push some refs to 'git@github.com:zrn-ns/swiftlint_on_pre_push.git' %サンプルプロジェクト
サンプルプロジェクトはこちらにアップしました。
FIXME
今回の手法では、Git Hooksのディレクトリをgit管理下に置くことで、全メンバーがスクリプトを共有する方法をとっています。
この方法は、スクリプトに修正が入った際にも環境間の同期が取れて便利な半面、各メンバーが独自のGit Hooksスクリプトを配置できなくなってしまいます。
(masterへの直pushを防止したりするためにもGit Hooksは便利に使えるので、個人でスクリプトを配置したい場面は多いと思います)何か良いアイディアがあれば教えていただけると幸いです?
まとめ
push前にSwiftLintのチェックを行う方法を解説しました。
この方法を使えば、各ビルドごとにLintチェックのオーバーヘッドがかかることを防げますし、PullRequestの作成前に問題に気づくことができるので、PullRequest作成後にコードを微調整するコストをかなり減らせるはずです。
ただし、新規メンバーが参入した場合には、ビルドごとにLintチェックを掛けてあげたほうが定着は早そうなので、その際はビルドフェーズに追加する方法でLintチェックを行ったほうが良いのかなと思います。
謝辞
下記のサイトを参考にさせていただきました。
- 投稿日:2021-01-24T15:15:38+09:00
【SwiftUI】opacityの謎
はじめに
SwiftUIではViewに対して.opacity(Double)で不透明度を指定することができます。
引数Doubleの値が0で透明、1で不透過、0.5では50%の透過度ということになります。公式ドキュメント
https://developer.apple.com/documentation/swiftui/view/opacity(_:)ただ、透明の時は、タップした時に動作させるonTapGestureが反応しなくなることを発見しました。
その存在が消されてしまうのでしょうか?検証
スライダーでサンプルを作成し実験してみました。
ボタンを押す男性をタップすると、上の表示がON/OFF切り替わるサンプルです。
このサンプルは次のような動きを示しました。
- opacityが0以外の時はON/OFFが切り替わる
- opacityが0の時はON/OFFが切り替わらない
以下、ソースコード
ContentView.swiftstruct ContentView: View { @State private var opacity: Double = 1.0 @State private var isOn: Bool = false var body: some View { VStack { Spacer() Text(isOn ? "ON" : "OFF") .font(.title) .fontWeight(.semibold) .frame(width: 200, height: 100) .background(isOn ? Color.red : Color.green) .foregroundColor(isOn ? .white : .black) Spacer() Image("tapButtonImage") .resizable() .aspectRatio(contentMode: .fit) .opacity(opacity) .frame(width: 200) .onTapGesture { isOn.toggle() } Slider(value: $opacity) Text("opacity: \(opacity)") Spacer() } .padding() } }opacityが0の時は、画像の存在そのものが消されている?とも考えましたが、画像が存在しなくなったら、スペースが詰められ、以下のような配置になるはずです。
なので枠として存在することになっています。タップ判定させたい場合
ちなみに、透明でもタップ判定できないかと調べていたところ、contentShapeを使うと反応することが分かりました。
以下、contentShapeを追加したコードです。ContentView.swiftImage("tapButtonImage") .resizable() .aspectRatio(contentMode: .fit) .opacity(opacity) .frame(width: 200) // contentShape追加 .contentShape(Rectangle()) .onTapGesture { isOn.toggle() }こちらを参考にしました。
https://stackoverflow.com/questions/57258371/swiftui-increase-tap-drag-area-for-user-interaction透明でもタップ判定させたい場合、contentShapeを使えば、願う動作を実装できます。
おわりに
透明部分をタップして動作させたい!と思いopacityを0にしてコードを書いたのがこの現象に気づくきっかけでした。
引数を0.00001など限りなく0に近づければ、人間には透明に見えるが、機械からは透明ではないと判定されるので、問題ないっちゃ問題ないのですが。。
ですが、contentShapeという解決策も見つかったので、もし同じような現象で苦しんでいる方の参考になれば幸いです。最後まで読んでくださりありがとうございました。
- 投稿日:2021-01-24T12:08:06+09:00
競プロで使える便利なエクステンション一覧(Swift)
私は競プロの記事をいくつか投稿しています。
- 競プロで使える便利なエクステンション一覧(Swift) ←イマココ
- 競プロで使えるアルゴリズム関数一覧(Swift)
オススメの読み方は、まず自分で考えることです。
ちょっとえらそうですが、自分で武器を増やしていくのが競プロの面白さのひとつだと思っています。
どうしてもわからないときに、私の記事が参考になると嬉しいですはじめに
本記事は Swift/Kotlin愛好会 Advent Calendar 2020 の11日目の記事です。
空いているので埋めました。競プロで使える便利な拡張メソッドとコンピューテッドプロパティを紹介します。
注意
- 本記事で紹介しているソースコードが正しいかは保証できていません。
- 本記事は随時更新予定です。
環境
- OS:macOS Big Sur 11.1
- Xcode:12.3 (12C33)
- Swift:5.3.2
5.2.1(2021/01/24現在のAtCoder)でも動作する競プロで使える便利なエクステンション一覧
桁分割
説明
数値の桁を分割し、桁数の降順に整列した配列で返します。
ソースコード
private extension Numeric where Self: LosslessStringConvertible { var digits: [Int] { string.digits } } private extension LosslessStringConvertible { var string: String { String(self) } } private extension StringProtocol { var digits: [Int] { compactMap { $0.wholeNumberValue } } }
出力例
1.digits -> [1] 32.digits -> [3, 2] 445.digits -> [4, 4, 5] 62096.digits -> [6, 2, 0, 9, 6]
合計
TBD
Decimal→Int変換
説明
Decimal
をInt
に変換します。私は
pow()
の戻り値をInt
で欲しいときに使います。
ソースコード
import Foundation private extension Decimal { var intValue: Int { NSDecimalNumber(decimal: self).intValue } }
出力例
pow(2, 3).intValue -> 8 pow(10, 6).intValue -> 1000000
10進数→2進数変換
説明
10進数の数値を2進数の文字列に変換します。
digit
に必要十分な大きさの値を渡さないとオーバーフローするので注意です。まだAtCoderでは使ったことがありません。
ソースコード
private extension FixedWidthInteger { func binaryString(digit: Int) -> String { var result: [String] = [] for i in 0..<(Self.bitWidth / 8) { let byte = UInt8(truncatingIfNeeded: self >> (i * 8)) let byteString = String(byte, radix: 2) let padding = String(repeating: "0", count: 8 - byteString.count) result.append(padding + byteString) } return String(result.reversed().joined().suffix(digit)) } }
出力例
0.binaryString(digit: 2) -> "00" 1.binaryString(digit: 2) -> "01" 2.binaryString(digit: 4) -> "0010" 8.binaryString(digit: 8) -> "00001000" 255.binaryString(digit: 8) -> "11111111" 256.binaryString(digit: 8) -> "00000000" // !!!: オーバーフロー 256.binaryString(digit: 12) -> "000100000000"
おわりに
以上、 Swift/Kotlin愛好会 Advent Calendar 2020 の11日目の記事でした。
参考リンク
- 投稿日:2021-01-24T08:55:30+09:00
【競プロ日記/Swift】深さ優先探索(部分和問題)
部分和問題
問題
配列A[A1、A2………An]が与えられる。その配列内からいくつか選択肢、その和がkと等しいか判定せよ。
※制約
・1 <= n <= 20
・-10⁸ <= a(i乗) <= 10⁸
・-10⁸ <= k <= 10⁸解答
//以下入力値例 let array = [2,3,8] let sumValue = 18 func equalToSumValue(count: Int, sum: Int) -> Bool { // 選択管理 if count == array.count { print("count:\(count),sum:\(sum)") return sum == sumValue } // 値未選択時 if equalToSumValue(count: count + 1, sum: sum) { return true; } // 値選択時 if equalToSumValue(count: count + 1, sum: sum + array[count]) { return true; } return false } func solve() { if equalToSumValue(count: 0, sum: 0) { print("correct") } else { print("incorrect") } } solve() //出力結果 //count:3,sum:0 //count:3,sum:8 //count:3,sum:3 //count:3,sum:11 //count:3,sum:2 //count:3,sum:10 //count:3,sum:5 //count:3,sum:13 //incorrect解説
上記はbit全検索を再帰関数を用いて行う処理になります。
図で表すと下記のような流れで、全検索していきます。処理の流れとしては配列の後ろの値から選択していくので、図の一番下から上に向かって処理が行われていきます。
参考
https://drken1215.hatenablog.com/entry/2020/01/05/185000
https://qiita.com/drken/items/e77685614f3c6bf86f44
- 投稿日:2021-01-24T07:09:23+09:00
【iOS】Firebaseど素人のFirebase入門 Firestore編
【iOS】Firebaseど素人のFirebase入門 認証編に続き、今回はFirestoreを学びながらサンプルアプリ作りに挑戦してみました。
作ったサンプルアプリのデモ
Firestore上のデータがTableView上に表示されるアプリです。
・
Set Button
を押すと、Firestoreに情報が書き込み or 更新(setdata)されます。
・Add Button
を押すと、Firestoreに情報が追加(addDocument)されます。
・Firestoreからドキュメントを取得を押す
を押すと、Firestoreに保存されている情報を取得(getDocuments)して、TableViewに反映します。・
リアルタイムアップデート
スイッチをOnにすると、Firestore上の情報が更新される度に自動的にTextViewを更新します。まずはFirebaseに登録
1.Firebase公式ドキュメントに沿って、Firebase登録とFirebase SDKをアプリに追加する。
Firebase を iOS プロジェクトに追加する2.Firebaseに追加したアプリのコンソールからCloud Firestoreを使用できるように設定する
AppDelegateでFirestoreの初期化
AppDelegate.swiftにFirebaseをインポート
AppDelegate.swiftimport Firebase
didFinishLaunchingWithOptions
内でFirestoreの初期化AppDelegate.swiftfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() let db = Firestore.firestore() print(db) return true }ViewControllerにもFirebaseをインポート
作成を進めるファイルにもFirebaseをインポートする。
FirestoreViewController.swiftimport Firebaseデータの書き込み or 更新
データの書き込み or 更新には
setData(documentData: [String : Any], completion:)
を使用します。db.collection("users").document("HiroshiTachi").setData([ "name": name, "age": age, ]) { error in if let error = error { print("ドキュメントの書き込みに失敗しました:", error) } else { print("ドキュメントの書き込みに成功しました") } }
setData
での書き込みの場合は、ドキュメント内に何もデータがない場合は新規作成を行い、すでにデータがある場合はそのデータの中身を更新します。データの追加
データの追加には
addDocument(data: , completion: )
を使用します。var ref: DocumentReference? = nil ref = db.collection("users").addDocument(data:[ "name": name, "age": age, ]) { error in if let error = error { print("ドキュメントの追加に失敗しました:", error) } else { print("ドキュメントの追加に成功しました:", ref?.documentID as Any) } }こちらは
setData
とは違い、指定したcollection
内に問答無用でどんどん新規追加してくれます。
なので、データを更新したりするのには使用出来ません。データを1回取得
データを1回取得するには、
getDocuments()
を使用します。db.collection("users").getDocuments() { (querySnapshot, error) in if let error = error { print("ドキュメントの取得に失敗しました:", error) } else { print("ドキュメントの取得に成功しました") for document in querySnapshot!.documents { let data = document.data() //取得したデータに対しての処理を書く print(data["name"]) } } } }データをリアルタイムで取得する
データが更新される度にリアルタイム取得をするには、
addSnapshotListener(listener:)
を使用します。1.グローバル変数としてリスナー
ListenerRegistration
を定義var listener: ListenerRegistration?2.リスナーをアタッチ
listener = db.collection("users").addSnapshotListener { documentSnapshot, error in if let error = error { print("ドキュメントの取得に失敗しました", error) } else { self.queriedDataArray = [] if let documentSnapshots = documentSnapshot?.documents { for document in documentSnapshots { let data = document.data() //アップデートされた際に行いたい処理を書く print(data["name"]) } } } } } }3.リスナーを切り離し
リスナーは必要がなくなったら切り離し(デタッチ)しましょう。
listener?.remove()公式ドキュメントより
データをリッスンする必要がなくなったら、イベント コールバックが呼び出されないようにリスナーをデタッチしなければなりません。これにより、クライアントは更新を受信するための帯域幅の使用を停止できます。
まとめ
公式ドキュメントの情報は本当に充実しているので、この記事より遥かに充実して間違いのない情報が得られますので、参考にする際は是非とも公式ドキュメントを参考にしていただければと思います。笑
また、セキュリティルールについてはまだまだなので知識を深めていきたい。
何か間違いがありましたら、優しく訂正していただけると幸いです?♂️
参考
Cloud Firestore を使ってみる
Cloud Firestore にデータを追加する
Cloud Firestore でデータを取得する
Cloud Firestore でリアルタイム アップデートを入手するサンプルアプリのコード全体
FirestoreViewController.swiftimport UIKit import Firebase class FirestoreViewController: UIViewController { @IBOutlet weak var queryDataTableView: UITableView! @IBOutlet weak var setTextLabel: UILabel! @IBOutlet weak var setAgeTextField: UITextField! @IBOutlet weak var setButton: UIButton! @IBOutlet weak var addTextField: UITextField! @IBOutlet weak var addAgeTextField: UITextField! @IBOutlet weak var addButton: UIButton! @IBOutlet weak var getButton: UIButton! @IBOutlet weak var realTimeQuerySwitch: UISwitch! let db = Firestore.firestore() var queriedDataArray = [String()] var listener: ListenerRegistration? override func viewDidLoad() { super.viewDidLoad() queryDataTableView.dataSource = self addButton.layer.cornerRadius = 15 setButton.layer.cornerRadius = 15 getButton.layer.cornerRadius = 15 } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) listener?.remove() } @IBAction func didTapButton(_ sender: UIButton) { switch sender.currentTitle { //SetButton Action case "Set": guard let name = setTextLabel.text, let age = setAgeTextField.text else { return } db.collection("users").document("HiroshiTachi").setData([ "name": name, "age": age, ]) { error in if let error = error { print("ドキュメントの書き込みに失敗しました:", error) } else { print("ドキュメントの書き込みに成功しました") } } //AddButton Action case "Add": guard let name = addTextField.text, let age = addAgeTextField.text else { return } var ref: DocumentReference? = nil ref = db.collection("users").addDocument(data:[ "name": name, "age": age, ]) { error in if let error = error { print("ドキュメントの追加に失敗しました:", error) } else { print("ドキュメントの追加に成功しました:", ref?.documentID as Any) } } //GetButton Action default: queriedDataArray = [] db.collection("users").getDocuments() { (querySnapshot, error) in if let error = error { print("ドキュメントの取得に失敗しました:", error) } else { for document in querySnapshot!.documents { let data = document.data() guard let name = data["name"] as? String, let age = data["age"] as? String else { return } let nameAndAge = name + " " + age + "歳" self.queriedDataArray.append(nameAndAge) DispatchQueue.main.async { self.queryDataTableView.reloadData() } } } } } } @IBAction func didChangeRealTimeQueryState(_ sender: UISwitch) { if sender.isOn { print("リアルタイムアップデートOn") listener = db.collection("users").addSnapshotListener { documentSnapshot, error in if let error = error { print("ドキュメントの取得に失敗しました", error) } else { self.queriedDataArray = [] if let documentSnapshots = documentSnapshot?.documents { for document in documentSnapshots { let data = document.data() if let name = data["name"] as? String, let age = data["age"] as? String { let nameAndAge = name + " " + age + "歳" self.queriedDataArray.append(nameAndAge) DispatchQueue.main.async { self.queryDataTableView.reloadData() } } } } } } } else { print("リアルタイムアップデートOff") listener?.remove() } } } extension FirestoreViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return queriedDataArray.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = queryDataTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = queriedDataArray[indexPath.row] return cell } }
- 投稿日:2021-01-24T05:25:06+09:00
Storyboardを使わずにコードベースでプロジェクトを作成する方法
はじめに
iOSアプリを複数人で開発する際、Storyboardを使用すると、ソースコードに意図せぬ差分が出やすく管理がめんどくさい・・・。
だったらUIKitじゃなくてSwiftUI使いましょうということで、Storyboardを使わずにコードベースで開発する方法をまとめます。
なお、iOS13以降とそれ以前では設定の方法が大きく異なります。
今記事では、iOS14での設定方法をまとめます。バージョン情報
Xcode 12.3
Swift 5.3.2プロジェクト作成
1.Xcodeを起動してプロジェクトを作成
通常時と同様の手順でプロジェクトを作成します。
- [Create a new Xcode project]
- [Single View App]を選択して[Next]
- 任意のプロジェクト名を入力
- プロジェクト設定
- Interface --> Storyboard
- Life Cycle --> UIKit
- Language --> Swift
- プロジェクトを保存
2.Storyboardの削除
上記の手順で作成したプロジェクトからStoryboardを消していきます
Main.Storyboardを削除。
[プロジェクト]>TARGETS>ProjectName>General>Development Info>Main Interfaceを空白にする。
SceneDelegate.swiftに初期画面情報を設定
以下のように編集。
SceneDelegate.swiftfunc scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let windowScene = scene as? UIWindowScene{ let window = UIWindow(windowScene: windowScene) window.rootViewController = ViewController.init() self.window = window window.makeKeyAndVisible() } }3.ビルド
- ViewController.swiftを以下のように編集
ViewController.swiftclass ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .red let titleLabel: UILabel = { let view = UILabel.init() view.text = "はろーわーるど★" view.textColor = .yellow view.translatesAutoresizingMaskIntoConstraints = false return view }() view.addSubview(titleLabel) NSLayoutConstraint.activate([ titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0), titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0) ]) } }おわりに
今回は、iOS14での、Storyboardを使用しないプロジェクトの作成〜ビルドまでをまとめました。
コードでAutoLayoutを使用する場合は、以下のコードでAutoresizingを無効にする必要があリますのでお気をつけください。ViewController.swiftview.translatesAutoresizingMaskIntoConstraints = false
- 投稿日:2021-01-24T03:24:46+09:00
【Swift UI】ObservableObjectとObservedObjectを理解するための時計アプリ
この記事は何か
SwiftUIフレームワークによるアプリ開発におけるデータフローを理解するために、シンプルな時計アプリを作成します。
環境
macOS 11.1
Xcode 12.3
Swift 5.3コード
アプリのエントリーポイント@main struct WatchApp: App { let clock = Clock() var body: some Scene { WindowGroup { ContentView(clock: clock) } } }ObservableObjectのクラスclass Clock: ObservableObject { var timer = Timer() @Published var currentTime = "" init() { start() } func start() { let formatter = DateFormatter() formatter.timeStyle = .medium timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { (_) in self.currentTime = formatter.string(from: Date()) }) } func stop() { timer.invalidate() } }ContentViewimport SwiftUI struct ContentView: View { @ObservedObject var clock = Clock() var body: some View { Text("\(clock.currentTime)") .font(.title) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { Group { ContentView(clock: Clock()) } } }
- 投稿日:2021-01-24T03:06:19+09:00
【iOS】個人開発で使って気分がよかったライブラリ5選【個人開発】
Yalantis/ColorMatchTabs: This is a Review posting app that let user find interesting places near them
綺麗な上タブが作れるライブラリ!
janselv/fave-button: FaveButton is an iOS cute animated like button written in Swift.
Ramotion/paper-onboarding: PaperOnboarding is a material design UI slider. Swift UI library by @Ramotion
ウォークスルー(解説画面)が簡単に実装できる!
varabeis/SPPermissions: Ask permissions on Swift. Available List, Dialog & Native interface. Can check state permission.
権限のリクエストがお洒落になるよ!
iOSライセンス一覧を生成するツールLicensePlistが便利 | kazyブログ
ライセンス表示もこれで安心!
まとめ
エンジョイ個人開発!