- 投稿日:2020-10-18T21:58:01+09:00
【Swift】FirebaseのdocumentIDを取得する方法
var documentId : String? self.firestore.collection("users").document("samplesample") .getDocuments() { (querySnapshot, err) in if let err = err { print("Error getting documents: \(err)") } else { for document in querySnapshot!.documents { self.documentId = document.documentID } } }
- 投稿日:2020-10-18T21:50:27+09:00
SwiftでFirebaseのデータを上書きする方法
import Firebase Firestore.firestore() .collection("コレクション名") .document(ドキュメントID) .setData([ "Value": 1, "UserId" : Auth.auth().currentUser!.uid ], merge: true)ポイントはmerge: trueです。
import Firebaseも忘れずに
- 投稿日:2020-10-18T21:44:29+09:00
SwiftでFirebaseで認証したユーザーのIDを取得する方法
import Firebase let userID = Auth.auth().currentUser!.uidimport Firebaseも忘れずに。
- 投稿日:2020-10-18T21:42:18+09:00
完全独学 IOSアプリ開発 日記
本日から完全未経験者が独学(参考書1冊)でIOSアプリ開発までの記事を落として行きたいと思います。
※学習内容の整理、定着を目標にしているため更新は夜型になると思います。
※レベル感的には、Railsでサーバーサイドの実装が可能。なぜ
3つの効果を期待している。
1,アウトプットを意識することで学習効率をあげること。
2,要点だけをまとめて整理すること。
3,宣言することで退路を断つ!それでは、Let's Go!
本日の学習内容。
1,Swiftの特徴
2,重要単語
3,個人的Topic1,Swiftの特徴
・まずSwiftとはどんな言語なの? → Mac,Ios系のアプリケーションを開発できる...もっと詳しく...
静的型付き言語で、コンパイル時の実行段階で変数や定数の型の値の情報を決定してくれる。(間違えた情報をコンパイルエラーとして表示してくれるため安全性が保証された言語である。大規模開発にも適しているそうです。)2,重要単語
※ここからは学習を進めていく上で特に重要だなと感じた概念や言語を選定して行きます!
型推論...文字列には(String)、数値には(Int)など型を推測してくれること。
ジェネリクス...型を明確に宣言してから変数を定義してくこと。(型を具体的に宣言して引数として渡すからなんか記述量が多い。)←このこと。
ライブラリ
標準ライブラリ-Swiftに標準的備わっているライブラリのこと。コアライブラリ-非同期や通信、ファイル操作の際に使用するライブラリ
・Foundation
多くのアプリに必要となる機能を提供してくれるライブラリ・libdispatch
ハードウェアの並列処理を行ってくれるライブラリ開発ツール...LLDB(バグを見つけやすくしてくれるツールのこと/binding.pry的な...)
命名規則
・変数、関数、定数-camelCaseを利用。単語選びのコツ
・あいまい×
・略語×
・一般的○ ← そりゃそう。3,個人Topic
ここまでで個人的に感じたことがあります。(正直...内容うっす。)
これから毎日、更新できるように気ままにやっていきます!
・・・ここに書き落としていく事ってかなり辛いですわ。本当に初心者の初心者の方のお助けになればいいと思っております。
- 投稿日:2020-10-18T21:38:51+09:00
SwiftでコードだけでUICollectionViewを実装する方法
class SampleCollectionView: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { lazy var collectionView : UICollectionView = { let collectionView = UICollectionView( frame: CGRect(x: 0, y: statusBarHeight, width: self.view.frame.width, height: self.view.frame.size.height - statusBarHeight), collectionViewLayout: UICollectionViewFlowLayout()) collectionView.delegate = self collectionView.dataSource = self collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cellId") return collectionView }() func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { //セルのアイテムの数 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { //アイテムの大きさ } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { //アイテム表示領域全体の上下左右の余白 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { //アイテムの上下の余白の最小値 } // アイテムの表示内容(UICollectionViewDataSource が必要) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CollectionViewCell //セルの表示内容 return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { //セルをクリックされた時のアクション } }
- 投稿日:2020-10-18T20:46:20+09:00
ARKit : Reality Composerでオブジェクトに名前をつける、Swiftで名前を得る
Reality Composerで名前をつける
表示するオブジェクトをダブルクリックして設定画面を表示し名前をつける。下図の場合「hako」という名前をつけている。
Swiftで名前を得る
オブジェクトをタップするとデバッグ画面に「hako」と表示される。
import UIKit import RealityKit class ViewController: UIViewController { @IBOutlet var arView: ARView! override func viewDidLoad() { super.viewDidLoad() // Load the "Box" scene from the "Experience" Reality File let boxAnchor = try! Experience.loadBox() boxAnchor.actions.tapped.onAction=clickEvent(_:) // Add the box anchor to the scene arView.scene.anchors.append(boxAnchor) } func clickEvent(_ entity: Entity?) { guard let entity = entity else { return } print(entity.name) } }「tapped」はReality Composerで付けたビヘイビア。
- 投稿日:2020-10-18T20:09:15+09:00
[Tips]初心者向け XCodeやSwiftで困ったときの解消法
環境
macOS:Catalina 10.15.6
Xcode:12.0
Swift:5.3
筆者について
趣味で
swift
を勉強中の只のエンジニアです。仕事では全く使っていません。
swift
は2015年頃に一度勉強していて、最近再燃して勉強し直しています。1.Stack Viewの制約がうまくつけられない
spacing
の値に0以外の値が入っていると制約に影響が出てしまうようなので、
spacing
には0を入力した方が良いです(制約に影響が出ない箇所なら可)。
2.新しいクラスを作成しプロトコルを継承すると、警告が出る。
Cannot declare conformance to 'NSObjectProtocol' in Swift;
と言って怒られます。
この場合、import Foundation
した上で、NSObject
を継承すれば解決です。
私の場合、import Foundation
を削除した上で試行錯誤していたので、大変でした。3.ビルドエラー
Multiple commands produce XXX
が発生。
詳しく見るとTarget [アプリ名] has copy command from XXX
とTarget [アプリ名] has link command with output XXX
が発生。
少し調べてみると何かが衝突しているようだった。.swift
ファイルをXCode
上で手動コピーしたのがまずかったのかと思う。
よく分からなかったので、プロジェクトを作り直すことで解決した。
XXX.swift
とMain.storyboard
をFinder
上で、旧プロジェクトフォルダから新プロジェクトフォルダへコピーすることで解決した。
Main.storyboard
の場所が一瞬分からなかったが、Finder
上では.../[アプリ名]/[アプリ名]/Base.Iproj/
の中にある。4.オブジェクトを比率で上手いこと調整したい(高さ40%のTableViewなど)
こちらのサイトが参考になります。
- 投稿日:2020-10-18T17:44:39+09:00
カスタムUIViewにて"loadNibNamed"のところで"EXC_BAD_ACCESS "
- 投稿日:2020-10-18T17:42:47+09:00
俺的RxSwiftまとめ①
RxSwiftとは
RxSwiftはReactive Extensions Swiftの略のこと。Reactive Extensionsは非同期処理をいい感じに時系列に沿って処理してくれるC#の最強ライブラリ。
つまり、RxSwiftはSwiftに従来備わっている非同期処理の書き方
- デリゲートデザインパターン
- Notificatin Centerを使ったオブザーバーパターン
- GCD(サクッとわかる用)(一番詳しい記事はこっち)
- クロージャー
- KVO
- PromiseKit(Promiseの概念はこちら)
- Combine
の仲間で、非同期処理をいい感じに行うための方法の一種です。
RxSwiftの非同期処理方法
では、RxSwiftはどうやって非同期処理を行っているのか?
RxSwiftでは、様々なイベント/処理を、時間とともに流れてくる一連のデータ(データストリーミング)に対して実行します。(リアクティブプログラミングと呼ばれるもの)上記の利点として、
- プログラム内で発生する全てのイベントを時間軸で捉えることができる
- コールバック地獄に陥らない
- MVVMを導入することで、FatViewControllerからの脱却/テストしやすいコードを書ける(具体例1,具体例2)
- 関数型プログラミングで書ける
というものがあります。
- 投稿日:2020-10-18T10:33:47+09:00
大学の研究室配属選考での自己アピールのために製作物をGitHubで公開してみた話
経緯
大学の情報系学科に通っている大学3年生です!
私の通っている大学・学科では、3年生の後半(10~11月)に研究室配属があります。この研究室配属では、各研究室が成績+面接等によって希望者の中から配属者を選考するのですが、面接では研究への興味・プログラミング能力・継続力・学習意欲などの自己アピールを求められます。そして、ここで重要なのは自己アピールにはGitHub等にあげた製作物も利用することができるという点です。
今回、私も研究室配属選考に備え、自己アピールに使うために過去の製作物をGitHubで公開し、「せっかく公開するんだったら記事でも書いてみようかな」と思って記事を書いて見ました。
製作物
Qiita_for_iOS
iOS開発の学習のために作成した
俺得Qiita閲覧アプリです。
- 記事の閲覧・検索やLGTM・ストック、ストック記事の確認などができます。
- Qiitaの公開APIを使用
- iOS13のキャッチアップを兼ねて作成したので、
UICollectionViewCompositionalLayout
やProperty Wrapper
などを盛り込んでいます。discord_clone_firebase
React開発の学習のために作成したチャットサービスDiscordのクローンアプリです。
デモ: https://discord-clone-36c89.web.app/
※ ユーザー名の入力が求められますが、「test」等を入力していただければ大丈夫です。
- メッセージ送信, 画像・ファイル送信, AddReactionなどができます
- バックエンドにFirebaseを利用
- React + Typescript + React Hooks
大学2年生以下の開発者様へ
製作物があると自己アピールに使える武器が手に入るので、作っておいて損はないと思いますし、研究室配属選考に限らず、その自己アピールが使えるケースも多いのではないでしょうか。
私の場合は製作物がiOS, Reactアプリケーションなので研究内容に直結しにくいですが、それでもプログラミング能力・継続力・学習意欲などのアピールになると思いますし、更に近年は機械学習・画像処理・音声認識などの比較的研究に関連しやすい分野も個人開発で手を出せるので、製作物で研究への興味や経験をアピールすることも可能だと思います。
もし、大学2年生以下で開発をしているのであれば、1つで良いのである程度形になった・公開できる製作物を作っておくと研究室配属で役に立つかもしれません!!
まとめ
(大学の先輩から聞いた話なのですが)
研究室配属選考の面接だと、「〇〇に興味があります。」 「プログラミングは得意です。」等の口頭での自己アピールのみの学生も多いらしく、その中で「〇〇に興味があります。〇〇を作りました。ソースコードはGitHubに上げてあります。」 「プログラミングは得意です。〇〇を作りました。△△にリリースしてあります。」と言った様に 製作物という証拠と一緒にアピールをすると信憑性が高く評価されやすい みたいです。
もちろん今までの成績も評価されますが、成績のみで全てを決める場合はむしろ少ない様です(?)。
やはり、自分がやってきたことのアウトプットは大事だなと思いました。今回、自分の過去のリポジトリで公開できる物を探したところ、とりあえずできそうだったのは2つでしたが、これを機にこれからは製作物をよりPublicに発信して行こうと思います。
私も研究室配属選考で勝ち残れる様に頑張ります!!!客観的評価がついているとより強いと思うので、製作物が良いと思ったらリポジトリにスターください(小声)(願望)(切実)
おまけ
GitHubのプロフィールも作成し、それっぽくしてみました!
- 投稿日:2020-10-18T06:07:59+09:00
Kerasで作った簡単なモデルをCoreMLを使ってiOSで動作させる
CoreML Toolsを理解するために、自分で作ったシンプルなモデルを使えば、自分で好きなようにモデルを変更して試せるので良いのではないかと思い試してみました。その手順を説明します。
作るもの
数字2組を与えたら、それらを足した結果を予測するモデルを作ります。
手順
今回のコードの完全版はこちらに保管してあります。
https://gist.github.com/TokyoYoshida/bab3d0396c05afce445852d2ae224cf41.Google Colabを起動する
Google Colaboratoryのサイトにアクセスします。
Google Colaboratory2. 必要なものをインストール & インポートする
coremltoolsのバージョンにあわせてtensorflow, kerasもインストールしています。
notebook!pip install tensorflow==1.14.0 !pip install -U coremltools !pip install keras==2.2.4そして必要なモジュールをインポートします。
notebookfrom keras.models import Sequential from keras.layers import Dense, Activation import numpy as np import math from keras.utils import np_utils import keras3. Tensorboardの準備をする
CoreML Toolsで変換をするとき、モデルの情報が必要になることがあります。
そのためにはある程度モデルを理解する必要がありますが、Tensorboardで可視化しておくと、モデルの情報を理解する助けになるため、その準備をしておきます。
(今回は自分でモデルを作っているので、モデルの情報は要りませんが)参考:
[TF]KerasからTensorboardを使用する方法notebook!mkdir logs tb_cb = keras.callbacks.TensorBoard(log_dir='./logs', histogram_freq=0, batch_size=32, write_graph=True, write_grads=False, write_images=False, embeddings_freq=0, embeddings_layer_names=None, embeddings_metadata=None) cbks = [tb_cb]4. モデルを作り学習させる
入力データして、2つの整数の組み合わせを1万組作成します。
教師データは今回は分類問題として扱うため、[0,1,2,3,4・・17,18]という18個の配列になります。(答えの組み合わせとして9 + 9 = 18が最大値のため)
この配列には、答えとなる部分だけに1が入り、それ以外は0が入ります。このような表現方法をOne-hot表現と呼びます。モデルは、シンプルな全結合レイヤーを3つ、活性化関数はsoftmaxを使用します。
参考:
Kerasで1桁の足し算notebookx = np.random.randint(0, 10, (10000,2)) y = np_utils.to_categorical(np.sum(x, axis=1)) model = Sequential() model.add(Dense(512, activation='relu', input_dim=2)) model.add(Dense(256, activation='relu')) model.add(Dense(y.shape[1])) model.add(Activation("softmax")) model.compile('rmsprop', 'categorical_crossentropy', metrics=['accuracy']) train_rate = 0.7 train_len = math.floor(len(x) * train_rate) trainx = x[0:train_len] trainy = y[0:train_len] testx = x[train_len:] testy = y[train_len:] history = model.fit(trainx, trainy, batch_size=128, epochs=100, verbose=1, callbacks=cbks, validation_data=(testx, testy))上のコードを実行すると学習が始まります。
notebook7000/7000 [==============================] - 0s 54us/step - loss: 0.1705 - acc: 0.9677 - val_loss: 0.0172 - val_acc: 1.0000 Epoch 99/100 7000/7000 [==============================] - 0s 53us/step - loss: 0.0804 - acc: 0.9804 - val_loss: 0.0069 - val_acc: 1.0000 Epoch 100/100 7000/7000 [==============================] - 0s 57us/step - loss: 0.0745 - acc: 0.9806 - val_loss: 0.0062 - val_acc: 1.0000今回はトレーニングデータとテストデータは分割しているものの、実際にはかぶりがあるので、テストデータによる検証結果(val_acc)はあまり当てにならないです。
5. Tensorboardで可視化してみる
Tensroboardを読み込んで、実行します。
なぜかtensorboard-plugin-wit
をアンインストールしないとエラーがでるので、アンインストールしておきます。notebook%load_ext tensorboard !pip uninstall tensorboard-plugin-wit %tensorboard --logdir ./logs学習の状況
グラフの情報
レイヤーは下に入力(dense_1)、上に出力(activation_1)というようにレイヤーが下から上に向かっています。
入力側(dense_1)を見てみます。
OperationがPlaceholderとなっており、ここにデータが入ることがわかります。
dtypeはDT_FLOATとなっています。今回のデータは整数で作っていますが、小数以下のデータも扱えます。
shapeは、{"dim":{"size":-1},"size":2]}となっています。つまり(-1,2)のshapeということですね。
-1は任意の値という意味になります。2は、2つの文字の組み合わせを入力しているためです。
<図.2>の入力データのところで、2つの数字を組み合わせxテストデータ数(任意)ということと一致します。出力側(activation_1)を見てみます。
activation_1はsoftmax関数なので、1つ前のdense_3が出力した19個の数字に対してsoftmax関数の計算結果を出し、loss、metrics、trainingに出力しています。モデルの情報は、kerasのsummaryメソッドでも出力できます。
Tensorboardではレイヤーが下から上に向かっていましたが、こちらは上から下に向かっていますね。notebookmodel.summary() _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 512) 1536 _________________________________________________________________ dense_2 (Dense) (None, 256) 131328 _________________________________________________________________ dense_3 (Dense) (None, 19) 4883 _________________________________________________________________ activation_1 (Activation) (None, 19) 0 ================================================================= Total params: 137,747 Trainable params: 137,747 Non-trainable params: 06. Notebook上で予想してみる
iOSで動作させる前に、Notebook上でうまく動作しているか確認します。
適当に2つの数字の組み合わせを与えると、正しく計算できていることがわかります。notebooknp.argmax(model.predict(np.array([[7,6]])),axis=1) // array([13]) np.argmax(model.predict(np.array([[1,3]])),axis=1) // array([4])7. Core MLに変換する
上で作ったモデルを保存しておきます。
(これをしなくても既にモデルは得られているのでそのままCore MLに変換することもできます。)notebookmodel.save('my_model.h5')読み込んで変換します。
notebookfrom keras.models import load_model keras_model = load_model('my_model.h5') from coremltools.converters import keras as converter # 予想結果の数字の分類ラベルを作る["0","1","2"..."18"] class_labels = np.arange(0, 19).astype('unicode').tolist() # 変換 mlmodel = converter.convert(keras_model, # 変換対象のモデル output_names=['digitProbabilities'], # 予想の出力に名前をつける。swiftから変数名としてアクセスできるようになる class_labels=class_labels, # 予想結果の分類ラベル predicted_feature_name='digit' # 分類の出力に名前をつける。swiftから変数名としてアクセスできるようになる )保存します。
notebookcoreml_model_path = 'my_model.mlmodel' mlmodel.save(coreml_model_path)このあたりのコードは、こちらの本に書いてあるものをそのまま使っています。
Core ML Tools実践入門 - iOS × DEEP LEARNING
8. Core MLモデル(.mlmodelファイル)をダウンロードする
Notebookから下のように選択して「ダウンロード」を選択してダウンロードします。
9. XcodeのプロジェクトにDrag & Dropする
Xcodeを起動して、プロジェクトの作成から「Single View App」を作成します。
今回作ったプロジェクトはgithubにあるので、こちらを使っても良いです。
TokyoYoshida/CoreMLSimpleTestプロジェクトの、任意の位置に.mlmodelファイルをDrag & Dropします。
Xcode内でモデルを選択すると、プレビューを見ることができます。
入力がMultiArray型で2つのDoubeを取ります。これは例えば、2+3を予想させたいなら、[2,3]を与えます。
出力は、digitProbabilitiesは、Dictionaryで文字列がKey、DoubleがValueになっています。この項目は予想結果で、数字のラベルごとの確率が出力されます。
digitは、予想結果を数字のラベルに当てはめた結果です。なお、MultiArray型は、Core ML内に定義されているモデルの入力や出力として使用する多次元配列です。
10. Core MLを使った推論のコードを書く
今回のモデルは、画像認識ではないのでVisionFrameworkなどは用いず、Core MLを直接操作します。
ViewControllerのviewDidLoadにコードを書いていきます。ViewController.swiftclass ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let model = my_model() // 予想したい数字のペアをつくる let inputArray = try! MLMultiArray([2,3]) let inputToModel: my_modelInput = my_modelInput(input1: inputArray) // 推論する if let prediction = try? model.prediction(input: inputToModel) { // 結果の出力 print(prediction.digit) print(prediction.digitProbabilities) } } }11. 実行する
アプリを実行すると無駄にアプリの画面が出ますが、気にせずXcodeのOutput欄を見ます。
結果出力5 ["13": 1.401298464324817e-45, "7": 4.403268860642129e-08, "16": 0.0, "12": 1.401298464324817e-45, "10": 1.401298464324817e-45, "4": 2.876720373024e-06, "11": 1.401298464324817e-45, "1": 1.2956196287086532e-23, "6": 6.624156412726734e-06, "8": 6.452452973902557e-18, "15": 1.401298464324817e-45, "2": 7.265933324842114e-14, "0": 1.0373160919090815e-33, "18": 0.0, "9": 1.7125880512063084e-34, "17": 0.0, "3": 1.129986526746086e-15, "14": 1.401298464324817e-45, "5": 0.9999904632568359]2+3を与えているので、5が推論されていますね。
digitProbabilitiesの出力では、各ラベルをキーとして確率が出力されています。
5である確率は0.9999904632568359なので、ほぼ1になっています。
それ以外の数字、例えば13である確率は、1.401298464324817e-45となっていますがこれは浮動小数の表現で1.401298464324817✕10の-45乗なのでほぼゼロという結果になっています。最後に
NoteではiOS開発について定期的に発信していますので、フォローしていただけますと幸いです。
https://note.com/tokyoyoshidaTwitterでは簡単なtipsを発信しています。
https://twitter.com/jugemjugemjugem
- 投稿日:2020-10-18T03:53:23+09:00
iOS14 Widget機能を実装してみる
今回の記事では、iOS14のWidget機能について、紹介、実装していこうと思います。
環境
OS:Catlina 10.15.4
Xcode:12.2 beta21、Widgetとは
Appleは
iOS8
からホーム画面から左にスワイプすると出てくる通知センターにカレンダーやタイマーといったようなシンプルな機能を持ったアプリをアクセス出来るようなAPIを提供してきたが、iOS14
ではホーム画面にもその表示ができるようになりました。機能がさらに増加され、表示内容もタイムラインに沿って更新できます。!重要:
Widget
機能はSwiftUI
でしか実装できないので、Widget
の本質はタイムラインによって変わるSwiftUI
ビューと言っても良い。2、実装順番
Widget Extension追加
SwiftUI
でマルチプラットホームのプロジェクトを新たに作成したら、Widget Extension
を追加します。
ちなみに、1つのアプリで複数のWidget Extension
を追加することができます。Xcode →File →New →Target →Widget Extension
widgetDemoW target
の追加に成功したら、タイムviewがデフォルトで表示されます。Widgetの入り口
widgetDemoW.swiftクラスの中、
@main
マークはWidget
の入り口です。struct widgetDemoWEntryView : View { var entry: Provider.Entry var body: some View { Text(entry.date, style: .time) } } @main struct widgetDemoW: Widget { let kind: String = "widgetDemoW" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in widgetDemoWEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") .supportedFamilies([.systemMedium]) } }
configurationDisplayName
はユーザがアプリのWidget
を追加、編集する時のWidget
の表示名(1つのアプリに複数のWidget
の存在もあり得るため)description
はWidget
の説明supportedFamilies
はサーポットサイズでデフォルトでは大、中、小を全てサーポット。providerは、
Widget
を更新するためのタイムラインを決定するオブジェクトです。Widget
を更新するための将来の時間を提供することで、システムは更新プロセスを最適化できます。
widgetDemoWEntryViewのEntryViewは実際に表示するViewです。
Widget
をクリックしたら本アプリを起動できます。Widget
を長押すと編集に入ります。3、Widgetの表示内容をカスタマイズする
Widget
の更新はWidgetCenter
によって完全に制御されます。 開発者は、APIを介してWidget
ビューをアクティブに更新することはできません。タイムラインを更新する必要があることをWidgetCenterに通知することしかできません。
システム上は、タイムラインのリロードを駆動する2つの方法を提供します。System Reloads
とApp-Driven Reloads
。注意:タイムラインのリロードは
Widget
を直接更新しませんが、WidgetCenter
はデータの次のステージのためにWidget
を再要求します。 タイムラインのProvider
は、このデータを提供するオブジェクトです。タイムラインの
Provider
によって提供されるデータには2つの部分があります。1つはTimelineEntry
で、もう1つはReloadPolicy
です。
TimelineEntry
は、Widget
が特定のタイムノードの下に表示する必要があるビュー情報と時点です。
ReloadPolicy
は、次の期間中のタイムラインの更新ポリシーです。3つのタイプがあります。atEnd:最後のタイムスライスに達したときにタイムラインが更新されることを示します。
atAfter:一定時間後の定期的な更新を指します。
never:それは将来更新する必要がないことを意味します。 最後のEntry
が表示されたら、更新せず最後のEntry
に止まります。アプリの起動だけでなく、
Widget
には異なるページへ遷移する複数のボタンが含まれることも可能です(中/大サイズのWidget
のみ)。実装例1
静的表示
struct widgetDemoWEntryView : View { var entry: Provider.Entry var body: some View { HStack { Text("今回の記事では、iOS14のWidget機能について、紹介、実装していこうと思います。 -20201017") .foregroundColor(.black) VStack { Image("widgetPic") .overlay(RoundedRectangle(cornerRadius: 10) .stroke(Color.purple, lineWidth: 3)) .shadow(radius: 10) .cornerRadius(10.0) Text(entry.date, style: .time) } .padding() } .padding() } }実際の表示
実装例2
タイムラインに沿って表示する
import WidgetKit import SwiftUI import Intents struct Provider: IntentTimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), text: "朋遠方より来たるあり。また楽しからずや。", configuration: ConfigurationIntent()) } func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), text: "朋遠方より来たるあり。また楽しからずや。", configuration: configuration) completion(entry) } func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] // 現在の日付から開始して、1時間間隔で60エントリで構成されるタイムラインを生成する let currentDate = Date() var textArray = [String]() for i in 0..<60 { textArray.append("朋遠方より来たるあり。また楽しからずや。 timeline: \(i)") } for minOffset in 0 ..< 60 { let entryDate = Calendar.current.date(byAdding: .minute, value: minOffset, to: currentDate)! let entryText = textArray[minOffset] let entry = SimpleEntry(date: entryDate, text: entryText, configuration: configuration) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { let date: Date let text: String let configuration: ConfigurationIntent } struct widgetDemoWEntryView : View { var entry: Provider.Entry var body: some View { HStack { Text(entry.text) .foregroundColor(.black) VStack { Image("widgetPic") .overlay(RoundedRectangle(cornerRadius: 10) .stroke(Color.purple, lineWidth: 3)) .shadow(radius: 10) .cornerRadius(10.0) Text(entry.date, style: .time) } .padding() } //アプリ側ではonOpenURLを通じてURLをハンドリングし、Widgetをタップすれば特定の画面へ遷移するアクションを実現 .widgetURL(URL(string: "widgetdemo://go2secondview")) .padding() } } @main struct widgetDemoW: Widget { let kind: String = "widgetDemoW" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in widgetDemoWEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") .supportedFamilies([.systemMedium]) } } struct widgetDemoW_Previews: PreviewProvider { static var previews: some View { widgetDemoWEntryView(entry: SimpleEntry(date: Date(), text: "朋遠方より来たるあり。また楽しからずや。", configuration: ConfigurationIntent())) .previewContext(WidgetPreviewContext(family: .systemMedium)) } }悟空アイコンが静的に表示し、論語のコンテンツとタイムを1分単位で更新します。
実際の表示
4、終わり
WidgetKit
の発想はすごく良いですが、OSのパフォーマンスと電力消費の考慮で、Widget
はビデオと動的画像の表示ができません。但し、うまく利用すればアプリの品質向上に繋がるには間違いないと思いので、もっと優秀なWidget
が出るのを期待したいです!
- 投稿日:2020-10-18T01:12:35+09:00
AVCaptureVideoPreviewLayerのプレビューサイズについて
AVFoundationのカメラ映像とプレビューのサイズが思ったように合わない時ってありますよね。
そんな時は、videoGravityで調整します。【準備】AVCaptureVideoPreviewLayerの設定
let previewLayer = AVCaptureVideoPreviewLayer(session: avCaptureSession) // avCaptureSessionは任意のAVCaptureSessionとします previewLayer.frame = previewView.bounds // previewViewは任意のUIViewとします previewLayer.connection.videoOrientation = .portrait // 向きの設定 previewView.addSubLayer(previewLayer)【本題】プレビューサイズの設定
カメラ映像全体が以下とします。
わかりやすいようにプレビューレイヤーは横長にします。1、縦横比を保ったまま、レイヤーサイズ内に収める
previewLayer.videoGravity = .resizeAspect2、縦横比を変えて、レイヤーサイズを満たす
previewLayer.videoGravity = .resize3、縦横比を保ったまま、レイヤーサイズを満たす
previewLayer.videoGravity = .resizeAspectFill4、おまけ:映像の真ん中だけプレビューする
カメラ映像のサイズが 1920 / 1080 で 上記画像のように真ん中の 810 / 1080 をプレビューしたいとします。let previewViewSize = previewView.bounds.size // previewViewは任意のUIViewとします let widthAspect = previewViewSize.width / 1080 let heightAspect = previewViewSize.height / 810 print(widthAspect,heightAspect) previewLayer?.frame = CGRect(x: -(1080 / 2) * widthAspect, y: -(810/ 2) * heightAspect, width: 1080 * widthAspect, height: 1920 * heightAspect) previewView.layer.addSublayer(previewLayer!) previewLayer.videoGravity = .resizeAspectプレビューレイヤーをビューより大きくして、はみ出させて真ん中だけビューから見えています。
?
お仕事のご相談こちらまで
rockyshikoku@gmail.comCore MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。