- 投稿日:2020-06-23T22:52:10+09:00
cocoapodsでsdwebimageをinstallしたのにNo such moduleって出るときの対処法
症状
cocoapodsでライブラリ(sdwebimage)をインストールしたのに,importしようとするとNo such moduleとエラーが表示される
対処法
よくあるエラーらしい.いくつか対処法があるらしいが,
私は以下で解決したステップ1
ワークスペース(拡張子が.xcworkspaceのやつ)を消す
ステップ2
ターミナルでワークスペースがあった階層まで行き
pod install
を実行する
これで治るはず
参考
https://stackoverflow.com/questions/54683959/no-such-module-sdwebimage-using-pod私は治らなかった対処法
- 投稿日:2020-06-23T22:50:03+09:00
NSPasteboardで画像を扱う
NSPasteboardの基本
- 【Apple】URL Reading Options
NSPasteboard
に関する全体の話。ベースとしてここを参照しています。- ※ただしサンプルコードはObjective-Cです。
クリップボードからファイルのURLを取得
- 画像のタイプ(png/jpeg/tiff/gif/...)の判別には、UTIを使うと便利そうです。
URL
からUTI
を取得するメソッドは以下を参考にしました。// ファイルパスがコピーされている場合を考える let pasteboard = NSPasteboard.general // NSImageの扱うUTIを指すfileURLのみ抽出する設定 // 具体的にはprint(NSImage.imageTypes)で確認できる let classes = [NSURL.self] let options: [NSPasteboard.ReadingOptionKey : Any] = [.urlReadingFileURLsOnly : true, .urlReadingContentsConformToTypes : NSImage.imageTypes] let canRead = pasteboard.canReadObject(forClasses: classes, options: options) if canRead { let objectsToPaste = pasteboard.readObjects(forClasses: classes, options: options) ?? [] if objectsToPaste.count > 0 { let fileURL = objectsToPaste[0] as! URL if let uti = uti(url: fileURL) { // 画像フォーマットを取得できる print(uti) // e.g. UTI: Optional("public.png") } imageView.image = NSImage(contentsOf: fileURL) // 出力して確認 } } else { print("クリップボードにファイルパスはありませんでした") } func uti(url: URL) -> String? { guard let r = try? url.resourceValues(forKeys: [.typeIdentifierKey]) else { return nil } return r.typeIdentifier }クリップボードから画像を取得
NSImageとして取得
readObjects
のforClasses
をNSImage
と変えるだけでOKです。// 画像がクリップボードにコピーされている場合 let pasteboard = NSPasteboard.general let classes = [NSImage.self] let options = [NSPasteboard.ReadingOptionKey : Any]() let canRead = pasteboard.canReadObject(forClasses: classes, options: options) if canRead { let objectsToPaste = pasteboard.readObjects(forClasses: classes, options: options) ?? [] if objectsToPaste.count > 0 { let image = objectsToPaste[0] as! NSImage imageView.image = image // 出力して確認 } } else { print("クリップボードに画像はありませんでした") }
- ただ欠点として、
NSImage
としてデータを保持してしまうと、元の画像のフォーマットを取得できません。- そこで一旦
Data
として読み込み、その段階で画像のフォーマット情報を得ておく必要があります。NSDataとして取得(画像のフォーマット情報も取得できる)
NSPasteboard
の機能をうまく利用すると、画像のフォーマット情報を取得することができます。- toolinbox/iPicUploader
- ここのコードを参考にしました。サンプルプロジェクトから
Jump to Definition
を繰り返せば色々見れるはずです。- 今回私のコードはなるべく短くしています。きちんと書く場合は、クラスや関数の分け方を上記ソースから参考にすると良いと思います。
// クリップボードの内容を一旦全て取得する guard let pasteboardItems = NSPasteboard.general.pasteboardItems else { return } var myImages = [MyImageInfo]() for pasteboardItem in pasteboardItems { for type in pasteboardItem.types { if let data = pasteboardItem.data(forType: type) { if let image = NSImage(data: data) { myImages.append(MyImageInfo(image: image, uti: type)) } } } } struct MyImageInfo { let image: NSImage let uti: NSPasteboard.PasteboardType }
- NSPasteboard.PasteboardType
- しかし足りないものもあって、例えばjpg(
public.jpeg
)は定数で用意されていないから自分で用意しないといけないかもです。print(NSPasteboard.PasteboardType.png.rawValue) // public.png
- StackOverflow等を検索すると、画像のバイナリの先頭数バイトを見て判断、というアイディアしか見つかりませんでした。
- (ゴリゴリ系はあまり実装したくないですね)
- How to identify a NSData's image format?
- PNGとJPGを最初の4byteで見分ける。
- Finding image type from NSData or UIImage
- 実際にHex Fiendでバイナリを確認してみると以下の通り。
小ネタ 現在クリップボードを確認する
- Finderのメニューから
クリップボードを表示
から確認できます。- 拡張子も確認できるので、デバッグ時のお供に。
- 投稿日:2020-06-23T22:50:03+09:00
クリップボードから画像を取得する
NSPasteboardの基本
- 【Apple】URL Reading Options
NSPasteboard
に関する全体の話。ベースとしてここを参照しています。- ※ただしサンプルコードはObjective-Cです。
クリップボードからファイルのURLを取得
- 画像のタイプ(png/jpeg/tiff/gif/...)の判別には、UTIを使うと便利そうです。
URL
からUTI
を取得する関数は以下を参考にしました。// ファイルパスがコピーされている場合を考える let pasteboard = NSPasteboard.general // NSImageの扱うUTIを指すfileURLのみ抽出する設定 // 具体的にはprint(NSImage.imageTypes)で確認できる let classes = [NSURL.self] let options: [NSPasteboard.ReadingOptionKey : Any] = [.urlReadingFileURLsOnly : true, .urlReadingContentsConformToTypes : NSImage.imageTypes] let canRead = pasteboard.canReadObject(forClasses: classes, options: options) if canRead { let objectsToPaste = pasteboard.readObjects(forClasses: classes, options: options) ?? [] if objectsToPaste.count > 0 { let fileURL = objectsToPaste[0] as! URL if let uti = uti(url: fileURL) { // 画像フォーマットを取得できる print(uti) // e.g. UTI: Optional("public.png") } imageView.image = NSImage(contentsOf: fileURL) // 出力して確認 } } else { print("クリップボードにファイルパスはありませんでした") } func uti(url: URL) -> String? { guard let r = try? url.resourceValues(forKeys: [.typeIdentifierKey]) else { return nil } return r.typeIdentifier }クリップボードから画像を取得
NSImageとして取得
readObjects
のforClasses
をNSImage
と変えるだけでOKです。// 画像がクリップボードにコピーされている場合 let pasteboard = NSPasteboard.general let classes = [NSImage.self] let options = [NSPasteboard.ReadingOptionKey : Any]() let canRead = pasteboard.canReadObject(forClasses: classes, options: options) if canRead { let objectsToPaste = pasteboard.readObjects(forClasses: classes, options: options) ?? [] if objectsToPaste.count > 0 { let image = objectsToPaste[0] as! NSImage imageView.image = image // 出力して確認 } } else { print("クリップボードに画像はありませんでした") }
- ただ欠点として、
NSImage
としてデータを保持してしまうと、元の画像のフォーマットを取得できません。- つまり
NSImage
として読み込む前の段階で、画像のフォーマット情報を得ておく必要があります。Dataを経由して取得(画像のフォーマット情報も取得できる)
NSPasteboard
の機能をうまく利用すると、画像のフォーマット情報を取得することができます。- toolinbox/iPicUploader
- ここのコードを参考にしました。サンプルプロジェクトから
Jump to Definition
を繰り返せば色々見れるはずです。- 今回私のコードはなるべく短くしています。きちんと書く場合は、クラスや関数の分け方を上記ソースから参考にすると良いと思います。
// クリップボードの内容を一旦全て取得する guard let pasteboardItems = NSPasteboard.general.pasteboardItems else { return } var myImages = [MyImageInfo]() for pasteboardItem in pasteboardItems { for type in pasteboardItem.types { if let data = pasteboardItem.data(forType: type) { if let image = NSImage(data: data) { myImages.append(MyImageInfo(image: image, uti: type)) } } } } struct MyImageInfo { let image: NSImage let uti: NSPasteboard.PasteboardType }
- NSPasteboard.PasteboardType
- しかし足りないものもあって、例えばjpg(
public.jpeg
)は定数で用意されていないから自分で用意しないといけないかもです。print(NSPasteboard.PasteboardType.png.rawValue) // public.png
- StackOverflow等を検索すると、画像のバイナリの先頭数バイトを見て判断、というアイディアしか見つかりませんでした。
- (ゴリゴリ系はあまり実装したくないですね)
- How to identify a NSData's image format?
- PNGとJPGを最初の4byteで見分ける。
- Finding image type from NSData or UIImage
- 実際にHex Fiendでバイナリを確認してみると以下の通り。
デバッグ時の小ネタ
スクリーンショットの拡張子を変更する
- デフォルトは
png
ですが下記の通り変更可能です。defaults write com.apple.screencapture type jpeg
現在のクリップボードを確認する
- Finderのメニューから
クリップボードを表示
から確認できます。- 拡張子も確認できるので、デバッグ時のお供に。
- 投稿日:2020-06-23T21:33:05+09:00
Cycle GANをCoreMLモデルに変換
*論文:サイクルでつながった敵対的ネットワークを使用するペアになっていない画像から画像への変換
CoreMLに変換することでCycleGAN画像変換をiPhoneアプリで使用できます。
このストーリーでは、TensorFlow CoreのCycleGANチュートリアルモデルを使用します。
最初に、Colaboratoryでチュートリアルモデルをトレーニングします。
colabの以下のセルまでの全てのセルを実行します。
colabfor epoch in range(EPOCHS): start = time.time() n = 0 for image_x, image_y in tf.data.Dataset.zip((train_horses, train_zebras)): train_step(image_x, image_y) if n % 10 == 0: print ('.', end='') n+=1 clear_output(wait=True) # Using a consistent image (sample_horse) so that the progress of the model # is clearly visible. generate_images(generator_g, sample_horse) if (epoch + 1) % 5 == 0: ckpt_save_path = ckpt_manager.save() print ('Saving checkpoint for epoch {} at {}'.format(epoch+1, ckpt_save_path)) print ('Time taken for epoch {} is {} sec\n'.format(epoch + 1, time.time()-start))モデルのトレーニングが完了したら、新しいセルを挿入して以下の手順で変換を実行します。
1、TFCoreMLをインストールします。
colab!pip install --upgrade tfcoreml2、チェックポイントを復元します。
colabcheckpoint_path = "./checkpoints/train" ckpt = tf.train.Checkpoint(generator_g=generator_g, generator_f=generator_f, discriminator_x=discriminator_x, discriminator_y=discriminator_y, generator_g_optimizer=generator_g_optimizer, generator_f_optimizer=generator_f_optimizer, discriminator_x_optimizer=discriminator_x_optimizer, discriminator_y_optimizer=discriminator_y_optimizer) ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=5) # if a checkpoint exists, restore the latest checkpoint. if ckpt_manager.latest_checkpoint: ckpt.restore(ckpt_manager.latest_checkpoint) print ('Latest checkpoint restored!!')3、「saved_model」フォーマットでジェネレータg(これがhorse2zebraジェネレーターです。ジェネレーターfはzebra2horse)を保存します。
colabgenerator_g.save( './ savedmodel')4、変換を実行します。
import tfcoreml input_name = generator.inputs[0].name.split(':')[0] print(input_name) #Check input_name. keras_output_node_name = generator_g.outputs[0].name.split(':')[0] graph_output_node_name = keras_output_node_name.split('/')[-1] mlmodel = tfcoreml.convert('./savedmodel', input_name_shape_dict={input_name: (1, 256, 256, 3)}, output_feature_names=[graph_output_node_name], minimum_ios_deployment_target='13', image_input_names=input_name, image_scale=2/ 255.0, red_bias=-1, green_bias=-1, blue_bias=-1, ) mlmodel.save('./cyclegan.mlmodel')これで、iOSプロジェクトでCycleGANを使用できます。
import Vision lazy var coreMLRequest:VNCoreMLRequest = { let model = try! VNCoreMLModel(for: cyclegan().model) let request = VNCoreMLRequest(model: mode, completionHandler: self.coreMLCompletionHandler0) return request }() let handler = VNImageRequestHandler(ciImage: ciimage,options: [:]) DispatchQueue.global(qos: .userInitiated).async { try? handler.perform([coreMLRequest]) }multiArrayを画像として視覚化するには、Hollance氏のCoreML Helpersが非常に便利です。
MultiArrayからImageへの変換 CoreMLHelper
自分でデータセットを用意してトレーニングすれば何2何でも作れます!
- 投稿日:2020-06-23T21:30:45+09:00
SwiftのKeyPathは結構遅いから気を付けろ
実験
SwiftのKeyPathは普通のプロパティアクセスの100倍遅い。
両方を100万回実行して確認。
var a: Int = 0 class Person { var name: String { didSet { a += 1 } } init(name: String) { self.name = name } } // ドットアクセス let bobName = "Bob" let bob = Person(name: bobName) let start1 = Date() for _ in 0..<1000000 { bob.name = bobName } print(Date().timeIntervalSince(start1), "s") // KeyPathアクセス let start2 = Date() let nameKeyPath = \Person.name for _ in 0..<1000000 { bob[keyPath: nameKeyPath] = bobName } print(Date().timeIntervalSince(start2), "s") print(a) // 20000結果
普通のアクセス KeyPath 0.0066s 0.6208s KeyPathあまり使わないようにしよう...
PlayGroundでは何故か逆になります。
多分Debug表示の関係でプロパティアクセスに特殊な処理がされてる。
- 投稿日:2020-06-23T20:48:24+09:00
iPhoneアプリ学習:診断画面
はじめに
某プログラミング学習サイトでの学習記録を記します。
共通操作
・範囲選択:Shiftを押しながらクリック
・複数行コメントアウト:Command /No1:プロジェクト作成
・Single View
No2:画面
・Main.storyboardを選択
・画面上の三つのアイコンの左端を選ぶ(ViewController)
・Editor > Embed In > Navigation Controllerを選択
(Navigation Controllerは画面遷移に必要)
・View ControllerのNavigation itemのTitleに題名を入力する
・名前を入力するためのText Fieldとそれを送信するためのButtonを配置する
- 投稿日:2020-06-23T19:04:12+09:00
何故かUIScrollViewの正しいサイズが取れない
問題
何故か、UIScrollViewのサイズ(
scrollView.bounds.size
)が正しい値が取れないことがあった。値を見てみると、AutoLayoutが作用する前の値が返ってきているようだった。
対処
- viewDidLayoutSubviewsメソッドを使う
- 意図したサイズ(AutoLayoutが作用した後のサイズ)を取ることができた。
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() //UIScrollViewのサイズ取得 }ただ、viewDidLayoutSubviewsはViewControllerのライフサイクルの一員ではないので、何度でも呼ばれる可能性がある。普通に書いただけで二度呼ばれていたし、画面を回転させた時も呼ばれるのでは?
- lauoutIfNeededメソッドで強制的にレイアウトを行う
- 意図したサイズ(AutoLayoutが作用した後のサイズ)を取ることができた。
//scrollviewの正しいサイズを取得するため、レイアウトを行う。 self.view.setNeedsLayout() self.view.layoutIfNeeded() //UIScrollViewのサイズ取得viewDidLayoutSubviewsは何度も呼ばれる点が不便だったため、lauoutIfNeededを採用した。
更なる課題
UITableView、UICollectionViewなどUIScrollViewのサブクラスではこのような問題は経験上起きないと思うが、何故UIScrollViewでのみこういった問題(オートレイアウトがされていない)が起きるのかよく分からなかった。
参考
https://stackoverflow.com/questions/12527191/ios-autolayout-get-frame-size-width/14147303#14147303
- 投稿日:2020-06-23T18:51:26+09:00
[Swift] Storyboard プロトタイプセルの使い方
プロトタイプセル
Storyboard上でUITableViewやUICollectionViewを設置した際、さらにプロトタイプセルも設置できる。(オブジェクトの追加ボタン(+ボタン)から、table view cellや collection view cellを選ぶ)
セルそのものは、reuseIdentifierを使うことにより、コード上で識別できる。
プロトタイプセルの中身
プロトタイプセル内に自分で設置した部品(例えばUIImageViewやUITextFieldなどなど)は、Story Board上でタグの数値を指定することで、コード上で識別できるようになる。ViewControllerの
viewWithTag()
メソッドを使う。参考
- 投稿日:2020-06-23T18:47:54+09:00
技術的負債を解消するには(iOS開発)
技術的負債がたまるとはどういう状態か
読みにくいコード、複雑なコード(Cruft)は、何も考えなくてもかける為、一応実装のスピードは出る。しかしそれも短期的なもので、時間が経つに連れて負債がたまる。具体的には、ある部分に修正の必要があったり、追加機能が必要な際に、既存のコードを読み解いたりするのに余計な時間がかかってしまう。
どのような方針で対応すべきか
すでにコーディングした部分に修正をくわえる場合
技術的負債を解消するにもコストはかかる。その為、コストとリターンを秤にかけて決定する。
例えば、将来あまり使わなそうなプロジェクトであれば修正をする意味はあまりない。
またプロジェクト内部において、将来あまりいじらない部分のモジュールであれば同じく修正をする意味はあまりなくなる。具体的には、独立性の高い部分のコードなど。
またやるにしてもどの程度やるかの問題がある。全体に対して完璧になるまで修正をすると何ヶ月もかかりかねず、過剰な措置ともなりかねない。他方、全部後回しにしては、前述のようにどんどん負債が大きくなっていってしまう。その為、コーディングする都度、関係する部分を少しずつ直し、自分が見る前よりも少し綺麗なコードにする(ボーイスカウトルール)など、日々少しずつ解消していくのが有効な場合が多いと言える。
これからコーディングをする場合
そもそも技術的負債が発生しないように最初にコーディングするのが一番効率が良い。また知見と技術力さえあれば、そのようなコーディングをしつつ、かかる時間もさほど増やさないようにするのは不可能ではない。
その為、そのような知見を積極的に吸収し、コーディングをするときから積極的に実行していくのが良い。
参考文献
https://note.com/timakin/n/nf7e2a70905d4
https://qiita.com/erukiti/items/9cc7850250268582dde7
http://tlync.hateblo.jp/entry/2014/09/28/025652
https://martinfowler.com/bliki/TechnicalDebt.html
https://medium.com/mop-developers/how-to-manage-technical-debt-961f8f94a623
- 投稿日:2020-06-23T18:16:31+09:00
Swift: Memory Safety
はじめに
この記事は、下記の記事を見ていきながら Memory Safety について理解していく記事です。
この記事で分かること
inout
を使う際に気をつけることMemory Safety
Swiftはデフォルトで安全でない動作が発生するのを防ぎます。
- 変数は使用される前に初期化される
- メモリが開放された後はアクセスされない
- 配列の添字が範囲外でないか確認する
Swiftはメモリを自動で管理しているので、ほとんどの場合は開発者がメモリアクセスについて考える必要はありません。
メモリアクセス
変数への格納や関数に引数として値を渡す際に、コード内でメモリにアクセスします。
// 変数oneが格納されているメモリへの書き込みアクセス var one = 1 // 変数oneが格納されているメモリからの読み取りアクセス print("We're number \(one)!")コード内の異なる領域から同時にメモリ内の同じ場所にアクセスを試みる場合に、競合する可能性があります。
また、メモリ内の同じ場所への複数同時アクセスは、予期せぬ動作や一貫性のない動作が発生する可能性があります。
inout
へのアクセスの競合
input
とは
input
は関数の引数に参照渡しとして値を渡す際に使用する修飾子です。例として、引数をインクリメントする関数を考えます。
inout
の知識ゼロで書くと以下になりますが、コンパイルエラーとなります。// コンパイルエラー var num = 1 func increment(number: Int) { number += 1 // Left side of mutating operator isn't mutable: 'number' is a 'let' constant }エラー文によると、引数は
let
で定義されていることが分かりました。
つまり、変数num
は参照渡しではなく、値渡しになっていることが分かります。ここで
inout
を付与します。// コンパイルエラー var num = 1 func increment(number: inout Int) { number += 1 } increment(number: num) // Passing value of type 'Int' to an inout parameter requires explicit '&'再度コンパイルエラー。
inout
を付与する場合は、実引数の前に&
が必要です。var num = 1 func increment(number: inout Int) { number += 1 } print(num) // 1 increment(number: &num) print(num) // 2
inout
として渡された変数にアクセスできない時式内で書き込みと読み込みが混在するケース
// コンパイルエラー var stepSize = 1 func increment(_ number: inout Int) { number += stepSize // number: write, stepSize: read } increment(&stepSize) // error: Execution was interrupted, reason: signal SIGABRT. // The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
stepSize
はグローバル変数- 通常なら
increment(_:)
内からアクセス可能stepSize
の読み込みと書き込みが同一の式内で混在しており、競合が発生上記の競合は、一時的な変数を用意して解決することができます。
var stepSize = 1 var copyOfStepSize = stepSize func increment(_ number: inout Int) { number += stepSize } increment(©OfStepSize) // 別の変数へ値をコピー (値渡し) stepSize = copyOfStepSize print(stepSize) // 2関数の複数の
input
引数として、同じ変数を渡した時// コンパイルエラー func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } var playerOneScore = 42 var playerTwoScore = 30 balance(&playerOneScore, &playerTwoScore) // OK balance(&playerOneScore, &playerOneScore) // コンパイルエラー // Inout arguments are not allowed to alias each other // Overlapping accesses to 'playerOneScore', but modification requires exclusive access; consider copying to a local variable変数
playerOneScore
に対して、複数の書き込みアクセス権が発生し競合してしまうため、コンパイルエラーとなります。メソッドにおける自己自身へのアクセスの競合
補足: 構造体の
mutating
修飾子構造体で自身のプロパティを変更するメソッドには、
mutating
修飾子が必要です。struct Bird { var name: String var age: Int mutating func updateName(name: String) { self.name = name } } var bird = Bird(name: "hoge", age: 11) print(bird.name) // hoge bird.updateName(name: "fuga") print(bird.name) // fuga(上の補足にもある通り、)
mutating
メソッドは、メソッド呼び出しの間は自己への書き込みアクセスを持っています。例として、下記のようなゲームを考えます。
- プレイヤーがいる
- プレイヤーはダメージを受けると減少する
health
を持つ- プレイヤーは特殊能力を使うと現象する
energy
を持つstruct Player { var name: String var health: Int var energy: Int static let maxHealth = 10 mutating func restoreHealth() { health = Player.maxHealth } }
restoreHealth()
メソッドにおいて、self
の書き込みアクセスはメソッドの先頭から始まり、メソッドがreturnされるまで続きます。下記の
shareHealth(with:)
メソッドにて、競合が起こる可能性があります。func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } struct Player { var name: String var health: Int var energy: Int static let maxHealth = 10 mutating func restoreHealth() { health = Player.maxHealth } } extension Player { mutating func shareHealth(with teammate: inout Player) { balance(&teammate.health, &health) // ① 引数のインスタンスとメソッド自身を呼ぶインスタンスが同じとなり、書き込みアクセスが競合する } } var oscar = Player(name: "Oscar", health: 10, energy: 10) var maria = Player(name: "Maria", health: 5, energy: 10) oscar.shareHealth(with: &maria) // OK maria.shareHealth(with: &maria) // コンパイルエラー①プロパティへのアクセスの競合
構造体、タプル型、列挙型などの型は、構造体のプロパティやタプルの要素などの複数の要素から構成されています。
これらは値型であるため、値の一部を変更することは値全体が変更されることと同義です。
つまり、プロパティ1つにアクセスするだけでも、値全体へ読み取りアクセスや書き込みアクセスが必要になります。// コンパイルエラー func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } var playerInformation = (health: 10, energy: 20) var num = 1 // balance(&playerInformation.health, &num) // OK balance(&playerInformation.health, &playerInformation.energy) // 要素の親が同じタプルなので2つの書き込みアクセスが競合関数内などのローカルで定義した場合はコンパイルエラーは起こりません。
func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } struct Player { var name: String var health: Int var energy: Int static let maxHealth = 10 mutating func restoreHealth() { health = Player.maxHealth } } // コンパイルエラー // var oscar = Player(name: "Oscar", health: 10, energy: 10) // balance(&oscar.health, &oscar.energy) func someFunction() { var oscar = Player(name: "Oscar", health: 10, energy: 10) balance(&oscar.health, &oscar.energy) // OK }Swiftは、コンパイラがメモリへの非排他的アクセスが安全であることを証明できれば、上記のようなメモリ安全なコードを許可します。(ローカルだとコンパイラで面倒を見れるが、グローバルだと見きれない。)
以下の条件が適用される場合、構造体のプロパティへの重複アクセスが安全であることを証明されます。
- インスタンスのstoredプロパティのみにアクセスしていて、computedプロパティやクラスのプロパティにアクセスしていない
- 構造体はグローバル変数ではなく、ローカル変数の値
- 構造体がどのクロージャにもキャプチャされていないか、ノンエスケープクロージャのみにキャプチャされているか
コンパイラがアクセスが安全であることを証明できない場合は、アクセスを許可しません。
まとめ
inout
を使う時は読み込み・書き込みのアクセス元に気をつけよう。inout
が指定された複数の引数をもつ関数に、構造体・タプル型・列挙型などの複数の要素から成る型の要素を同一の親から複数渡した場合、グローバルではコンパイルエラーになり、ローカルだとコンパイラが競合の発生が無いと判断した場合に実行できる。
- 投稿日:2020-06-23T16:10:43+09:00
初めてのアプリ Swift UIでrealmからデータ変更があった時のUI更新
realmに保存したデータを削除した時にUIへうまく反映されない件(削除されたのに消えないDoneをクリックするとクラッシュ)、今日はコードを微調整したら新しい不具合がありました。Deleteをクリックすると、クラッシュします。
調べると、この記事を参照:https://qiita.com/chocoyama/items/af172b32f492b706c96d
なるほど。realmデータベースの中に保存したデータに変更があってもSwiftが検知できず、知らせて処理するよう書く必要がるなんだとわかりました。
Observeメソッドを使って実現できるらしいですが、自分の場合、まだうまくできませんでした。
またまた同じエラーでクラッシュ。
- 投稿日:2020-06-23T15:59:01+09:00
Present Modallyをdismissした時に遷移前の画面を更新する
備忘録
ios13以降で画面遷移をPresent Modallyにした場合、dismissした時に遷移前の画面が更新されないので、対処法を残しておきます。
対処法1
PresentationをfullScreenにする
対処法2
extension 遷移前ViewControler: UIAdaptivePresentationControllerDelegate { func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { tableView.reloadData() } }extension 遷移後ViewController { override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { super.dismiss(animated: flag, completion: completion) guard let presentationController = presentationController else { return } presentationController.delegate?.presentationControllerDidDismiss?(presentationController) } }最後に
初心者のため、間違っている可能性があります。
参考までによろしくお願いします。
- 投稿日:2020-06-23T15:50:03+09:00
iOSでステータスバーの背景色を変える拡張関数(Swift)
はじめに
iOSでは、ステータスバーの背景色を単体でかんたんに変えることができません。
UIViewController#view.backgroundColor
を指定するとステータスバーの背景色が変わりますが、同時にビュー全体の背景色も変わってしまいます。いくつか方法はあるのですが、今回は「上左右をSuperviewに合わせ、下をSafe Areaの上に付けたビュー」を追加することで実現します。
Storyboardを作成するたびに手動でビューを追加するのは手間なので、呼び出すだけでステータスバーの背景色を変えられる拡張関数を実装しました。環境
- OS:macOS Mojave 10.14.6
- Xcode:11.3.1 (11C504)
- Swift:5.1.3
- iOS:13.3
実装
UIViewController
に拡張関数を追加します。UIViewController+StatusBar.swiftimport UIKit extension UIViewController { private final class StatusBarView: UIView { } func setStatusBarBackgroundColor(_ color: UIColor?) { for subView in self.view.subviews where subView is StatusBarView { subView.removeFromSuperview() } guard let color = color else { return } let statusBarView = StatusBarView() statusBarView.backgroundColor = color self.view.addSubview(statusBarView) statusBarView.translatesAutoresizingMaskIntoConstraints = false statusBarView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true statusBarView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true statusBarView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true statusBarView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true } }複数回呼ばれてもビューが重ならないよう、処理の最初で
StatusBarView
があったら剥がすようにしています。使い方
UIViewController
のviewDidLoad()
で色を指定して呼び出すのみです。FooViewController.swiftfinal class FooViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() configureView() } private func configureView() { setStatusBarBackgroundColor(.blue) } }ノッチの有無にかかわらず、ステータスバーの背景色が変わります。
iPhone 8 iPhone 11 Pro Max 参考リンク
- 投稿日:2020-06-23T15:02:56+09:00
Xcode 12アップデートまとめ!
WWDC2020
今年中もiOS、iPadOS、MacOSなどの新しいOSバージョンと共にXCodeも12が発表されました。
早速、Xcode12のアップデート内容をまとめてみました!!XCode12
WWDCでApple siliconの発表があったため、 Xcode 12 for macOS Universal Appsと言ったアプリケーションもBETA版として登場しています。
新機能
タブ表示が追加され、素早いファイルの移動が可能になった
以前にもタブ表示の機能はありましたが、新たに細かい階層でもタブ表示できるようになりました。
Optionキーを押すことで新しいタブが追加されます。
以前のタブよりもだいぶ切り替え表示されるスピードが早いので、かなり便利です。WidgetやAppClipなどの新しく登場したパッケージをサポート
iOS14やiPadOS14からのサポートになるので、早めにいじりたい方はBeta版をインストールしましょう。
新規プロジェクトでLife Cycleという項目が追加
InterfaceでSwiftUIを選択するとLife Cycleで
SwiftUI App
が選択可能になります。
また、SwiftUIでの開発の仕方ががっつり変わりそうですね。コード補完が高速に
毎回アップデートされていますね。
Xcode11ではSwiftUIの補完が微妙だったのでそこは期待。SVGがAssetのサポートに追加
やっと、正式にSVGがXcodeでサポートされましたね...
これでアイコンを追加するたびに3枚の画像を追加しなくて良くなります。サポートOSはiOS13、iPadOS13以降です。
ブレークポイントがドラッグ&ドロップで移動できるようになった
小さなアップデートですが、ありがたい...
Xcodeとシミュレーターを並べて表示できるようになった
Swift
guard letのインデントが揃うようになった
guard let x = someOptional, let y = anotherOptional else { // ... }Swift Packages
リソースファイルをパッケージに含められるようになった
バイナリファイルをパッケージに含められるようになった
ついにFirebaseなどのビルド済みフレームワークもSPM対応できそうですね。
SwiftUI
関数ビルダーないで
if let
やswitch
が利用可能になったこの機能はXcode11のときから付けて欲しかった...
struct ContentView: View { @State var username: String? var body: some View { Group { if let username = username { Text("Welcome, \(username)") } else { Image(systemName: "circle") } } } }まとめ
まだ、登場したばかりのXcode12。
今回は開発者目線でバージョンアップしたとわかる箇所ピックアップしてまとめました。
MacOSのデザイン変更によりXcode全体やアイコンも変更されています。まだまだ、正式リリースには時間があるのでBeta版がアップデートするたびにこの記事もアップデートしていこうと思っています。
読んでいただきありがとうございました。
- 投稿日:2020-06-23T15:02:56+09:00
Xcode12アップデートまとめ!
WWDC2020
今年中もiOS、iPadOS、MacOSなどの新しいOSバージョンと共にXCodeも12が発表されました。
早速、Xcode12のアップデート内容をまとめてみました!!XCode12
WWDCでApple siliconの発表があったため、 Xcode 12 for macOS Universal Appsと言ったアプリケーションもBETA版として登場しています。
新機能
タブ表示が追加され、素早いファイルの移動が可能になった
以前にもタブ表示の機能はありましたが、新たに細かい階層でもタブ表示できるようになりました。
Optionキーを押すことで新しいタブが追加されます。
以前のタブよりもだいぶ切り替え表示されるスピードが早いので、かなり便利です。WidgetやAppClipなどの新しく登場したパッケージをサポート
iOS14やiPadOS14からのサポートになるので、早めにいじりたい方はBeta版をインストールしましょう。
新規プロジェクトでLife Cycleという項目が追加
InterfaceでSwiftUIを選択するとLife Cycleで
SwiftUI App
が選択可能になります。
また、SwiftUIでの開発の仕方ががっつり変わりそうですね。コード補完が高速に
毎回アップデートされていますね。
Xcode11ではSwiftUIの補完が微妙だったのでそこは期待。SVGがAssetのサポートに追加
やっと、正式にSVGがXcodeでサポートされましたね...
これでアイコンを追加するたびに3枚の画像を追加しなくて良くなります。サポートOSはiOS13、iPadOS13以降です。
ブレークポイントがドラッグ&ドロップで移動できるようになった
小さなアップデートですが、ありがたい...
Xcodeとシミュレーターを並べて表示できるようになった
Swift
guard letのインデントが揃うようになった
guard let x = someOptional, let y = anotherOptional else { // ... }Swift Packages
リソースファイルをパッケージに含められるようになった
バイナリファイルをパッケージに含められるようになった
ついにFirebaseなどのビルド済みフレームワークもSPM対応できそうですね。
SwiftUI
関数ビルダーないで
if let
やswitch
が利用可能になったこの機能はXcode11のときから付けて欲しかった...
struct ContentView: View { @State var username: String? var body: some View { Group { if let username = username { Text("Welcome, \(username)") } else { Image(systemName: "circle") } } } }まとめ
まだ、登場したばかりのXcode12。
今回は開発者目線でバージョンアップしたとわかる箇所ピックアップしてまとめました。
MacOSのデザイン変更によりXcode全体やアイコンも変更されています。まだまだ、正式リリースには時間があるのでBeta版がアップデートするたびにこの記事もアップデートしていこうと思っています。
読んでいただきありがとうございました。
- 投稿日:2020-06-23T15:02:56+09:00
Xcode12のアップデート内容をまとめてみた。
WWDC2020
今年中もiOS、iPadOS、MacOSなどの新しいOSバージョンと共にXCodeも12が発表されました。
早速、Xcode12のアップデート内容をまとめてみました!!XCode12
WWDCでApple siliconの発表があったため、 Xcode 12 for macOS Universal Appsと言ったアプリケーションもBETA版として登場しています。
新機能
タブ表示が追加され、素早いファイルの移動が可能になった
以前にもタブ表示の機能はありましたが、新たに細かい階層でもタブ表示できるようになりました。
Optionキーを押すことで新しいタブが追加されます。
以前のタブよりもだいぶ切り替え表示されるスピードが早いので、かなり便利です。WidgetやAppClipなどの新しく登場したパッケージをサポート
iOS14やiPadOS14からのサポートになるので、早めにいじりたい方はBeta版をインストールしましょう。
新規プロジェクトでLife Cycleという項目が追加
InterfaceでSwiftUIを選択するとLife Cycleで
SwiftUI App
が選択可能になります。
また、SwiftUIでの開発の仕方ががっつり変わりそうですね。コード補完が高速に
毎回アップデートされていますね。
Xcode11ではSwiftUIの補完が微妙だったのでそこは期待。SVGがAssetのサポートに追加
やっと、正式にSVGがXcodeでサポートされましたね...
これでアイコンを追加するたびに3枚の画像を追加しなくて良くなります。サポートOSはiOS13、iPadOS13以降です。
ブレークポイントがドラッグ&ドロップで移動できるようになった
小さなアップデートですが、ありがたい...
Xcodeとシミュレーターを並べて表示できるようになった
Swift
guard letのインデントが揃うようになった
guard let x = someOptional, let y = anotherOptional else { // ... }Swift Packages
リソースファイルをパッケージに含められるようになった
バイナリファイルをパッケージに含められるようになった
ついにFirebaseなどのビルド済みフレームワークもSPM対応できそうですね。
SwiftUI
関数ビルダー内で
if let
やswitch
が利用可能になったこの機能はXcode11のときから付けて欲しかった...
struct ContentView: View { @State var username: String? var body: some View { Group { if let username = username { Text("Welcome, \(username)") } else { Image(systemName: "circle") } } } }まとめ
まだ、登場したばかりのXcode12。
今回は開発者目線でバージョンアップしたとわかる箇所ピックアップしてまとめました。
MacOSのデザイン変更によりXcode全体やアイコンも変更されています。まだまだ、正式リリースには時間があるのでBeta版がアップデートするたびにこの記事もアップデートしていこうと思っています。
読んでいただきありがとうございました。
- 投稿日:2020-06-23T12:16:01+09:00
Swift文字列←→CGFloatやり方によっては誤差が含まれたまま文字列に変換されてしまう
Swift文字列←→CGFloatやり方によっては誤差が含まれたまま文字列に変換されてしまう
目的:"1.1"をCGFloatに変換して、変換したCGFloatの値をもとの文字列"1.1"に戻したい
- 文字列からCGFloatに変換
- CGFloatから文字列に変換
誤差が含まれて文字列になってしまうやり方があることがわかった
"1.1"をfloatへ(1.100000023841858)これをCGFloatに変換してから文字列へ"1.100000023841858"
32bitアプリか64bitアプリかで違うということらしいので、64bitのCGFloatはdoubleのtypedef// CGFloatより /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures.ここで注意して欲しいのは、CGFloat(Float("1.1"))としないこと、これは32bitアプリの場合
現在は64bitアプリなのでCGFloat(Double("1.1"))が正解CGFloatへの変換はNumberFormatterクラスを使う
- NumberFormatterクラスを使う
これを使うといい
let strNum = "1.1" let numberFormatter = NumberFormatter().number(from: strNum) ?? 0.0 let cgFloat = CGFloat(truncating: numberFormatter) let str = "\(cgFloat)"
- 投稿日:2020-06-23T11:49:07+09:00
ダークモード対応時にObjective-Cのコードがあったとき
はじめに
歴史のあるiOSアプリのプロジェクトになると、Swift移行が完全でなくObjective-C(以降、objc)のコードが残存していたりするかと思います。
自分の関わるプロジェクトでは、ダークモード対応するぞ!ってなったときに、objcのコードが残っていたので一部objcで対応しました。
コードから直接カラーセットを利用する
Storyboard上のパーツにカラー指定すれば基本的には対応できますが、中にはコード上でカスタムカラーを指定している場合もあります。
Swiftで記述する場合は
enum
でカラーセットを定義してそれを利用するとか、UIColor
のextensionでカスタムカラーを定義する方法があるかと思います。自分の関わったプロジェクトでは前者の方法で対応していたため、objcでenumを使えませんでした。
objc用で列挙型を定義するのもありかもしれませんが、いずれobjcを撲滅することを考えるとわざわざ定義するのに工数もかけたくありません。
直接呼び出せればいいやって感じです。その時は以下のようにします。
// ラベルの文字色を決めるとき textLabel.textColor = [UIColor colorNamed: @"hogehoge"]; // 画像の枠の色を決めるとき imageView.layer.borderColor = [[UIColor colorNamed: @"hugahuga"] CGColor];一応、Swiftだとこんな感じになります。
// ラベルの文字色を決めるとき textLabel.textColor = UIColor(named: "hogehoge") ?? UIColor.black // 画像の枠の色を決めるとき imageView.layer.borderColor = UIColor(named: "hugahuga").cgColor ?? UIColor.black.cgColor意外と簡単にカラーセット呼び出せるんですね。
早くobjc撲滅してSwiftで綺麗にしたい。ライトモードとダークモードの切り替え時の対応
traitCollectionDidChange
を利用して、モードが切り替わったことを検知して、そのときにカスタムカラーの更新をかけます。- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; if (@available(iOS 12.0, *)) { UIUserInterfaceStyle currentStyle = self.traitCollection.userInterfaceStyle; UIUserInterfaceStyle previousStyle = previousTraitCollection.userInterfaceStyle; if (currentStyle != previousStyle) { //カスタムカラーの更新処理を記述する } } }Swiftでも基本的に同様の書き方です。
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) if #available(iOS 12.0, *) { let previousStyle = previousTraitCollection?.userInterfaceStyle let currentStyle = traitCollection.userInterfaceStyle if previousStyle != currentStyle { // カスタムカラーの更新処理を記述する } } }参照
- 投稿日:2020-06-23T11:19:16+09:00
Swift5.3の変更点
末尾クロージャ(追加)
複数のトレーリング・クロージャに関する情報をトレーリング・クロージャのセクションに追加しました。
https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID102プロトコル(追加)
合成された実装を使用したプロトコルの採用のセクションに、列挙のための Comparable の合成された実装に関する情報が追加されました。
https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID627状況に応じたWhere句(追加)
より多くの場所で一般的な where 節を書くことができるようになり、コンテキストに応じた where 節のセクションが追加されました。
https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID628所有されないオプショナル参照(追加)
オプション値での未所有の参照の使用についての情報を持つ、未所有のオプション参照セクションを追加しました。
https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html#ID625@main属性(追加)
main セクションに @main 属性に関する情報を追加しました。
https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID626リテラル式(追加)
リテラル式のセクションに #filePath を追加し、#file の説明を更新しました。
https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID390クロージャのエスケープ(更新)
Escaping Closuresセクションを更新し、より多くのシナリオでクロージャが暗黙的に自己を参照できるようになりました。
https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID546エラー対処(更新)
Do-CatchとDoステートメントを使用したエラーの処理セクションを更新し、キャッチ句が複数のエラーにマッチするようになりました。
https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html#ID541プロパティ・オブザーバ(更新)
遅延プロパティがオブザーバを持つことができるようになりました。
https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID262プロトコル宣言(更新)
プロトコル宣言セクションを更新し、列挙のメンバがプロトコルの要件を満たすことができるようになりました。
https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID369プロパティ・オブザーバ(更新)
ストアド変数オブザーバとプロパティオブザーバのセクションを更新し、ゲッターがオブザーバの前に呼び出される場合について説明しました。
https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID359
- 投稿日:2020-06-23T10:25:37+09:00
初めてのアプリ トラブル日記 swift--NavigationLinkとForEach/Listの相性悪っ
ユーザが書いたブログのデータをrealmから取得して、タイトルだけを一覧にして、タイトルをNavigationLinkにしてクリックできるようにしたいと思っていました。
一覧はForEachかListループして、クロージャの中でNavigationLinkを書く。下記のようなイメージ:
ForEach(diaries!, id: .self){ diary in
NavigationLink(destination:DiarycontentView(diaries: diary),label:{
Text(diary.diaryTitle)
})
}シミュレータで確認すると、タイトルをクリックし、DiarycontentViewへ遷移した途端、一覧ページへ戻っちゃう奇妙現象発生でした。(Qiita初心者で画像/動画添付できず許してください)
調べてみてStackoverFlowも色々見ました。先輩にも見てもらって、解決案が二つでした。
①NavigationLinkをクロジャーの中ではなく、ForEachループの外へ移動。
②ForEachの外でさらにScrollViewを使う。
↑こちらの記事を参照:https://masamichi.me/development/2019/10/21/swiftui-list-multiple-navigationlink.html②の方法が完璧に解決できました!
けど、こういう記事一覧みたいなのは、NavigationLinkじゃなくて他にもっといいやつがありますよねきっと。みなさんもしご存知でしたら、ぜひお教えいただけますと幸いです!知りたいです!
- 投稿日:2020-06-23T02:24:27+09:00
[Swift] iOS13以降で使用可能なAPIまとめ
iOS APIとは?
Appleにより提供されている標準APIです。
意外と知られていない面白い機能などがたくさんあるので、今日はいくつかピックアップして紹介します。環境
- Xcode11.1
- Swift5
Vision(Text Recognition)
概要:
・文字の領域を識別
・文字認識※英数字の識別しか現状できない
技術資料:
How to use VNRecognizeTextRequest’s optical character recognition to detect text in an image
Text recognition on iOS 13 with Vision, SwiftUI and Combineサンプルコード:
ScanningDocumentsApple Document:
https://developer.apple.com/documentation/visionVisionKit
概要:
・iOS13からNotesアプリに搭載されるOCRの機能を提供(文字認識)
・ドキュメントスキャン
・ドキュメント編集技術資料:
VisionKitBasics
Scanning documents with Vision and VisionKit on iOS 13サンプルコード:
VisionKit-ExampleApple Document:
https://developer.apple.com/documentation/visionkit
https://developer.apple.com/documentation/visionkit/vndocumentcameraviewcontrollerdelegateCore NFC
概要:
・NFCタグ直接読み取り/書き込み(システムコード指定、暗号化通信可)
・カード固有IDの読み出し
・高機能/複雑な構造を持ったカード・タグの取り扱い
・交通系電子マネーの履歴読み取り
・会員カードの読み取り
・NDEFタグの作成・書き換え
・フィットネス機器などとの双方向の通信
・各種機器の設定
・各種機器のそれなりの容量のデータの読み取り※ FeliCa Plug, FeliCa Linkその他など
技術資料:
iPhoneでFeliCaを読み取ってみた
iOS13 CoreNFCの使いみちとQRコード、BLEとの比較サンプルコード:
vCardCoreNFCWriterライブラリ:
TRETJapanNFCReaderApple Document:
https://developer.apple.com/documentation/corenfcPortrait Effects Matte
概要:
・人物の矩形を背景から切り抜き
・人物の矩形と背景の深度測定
・AR表現における回り込み※現状静止画のみ
※人物のみ技術資料:
iOS 12のPortrait Matteがすごい/ #iOSDC 2018で登壇しますサンプルコード:
PortraitEffectsMatteSampleApple Document:
https://developer.apple.com/documentation/avfoundation/avportraiteffectsmatteSemantic Segmentation Matte
概要:
概要:
・人物の"髪", "肌", "歯"をセグメンテーションする※現状静止画のみ
※人物のみ技術資料:
Multi-cam support in iOS 13 allows simultaneous video, photo, and audio captureApple Document:
https://developer.apple.com/documentation/avfoundation/avsemanticsegmentationmatte?language=objc補足:
Portrait Effects Matteと同様の方法でImageDataを操作するが、用意されているメソッドでは今の所実装出来なかった(動かなかった)。 Semantic Segmentation Matteの実装資料も見当たらない。 またApple公式でもSemantic Segmentation Matteの実装について言及している資料が見当たらない。Core Haptic
概要:
・デバイスの振動制御が可能
・細かな振動制御まで可能技術資料:
iOS13で公開予定の「Core Haptics」を使って、Haptic Feedback (触覚フィードバック) するコードを書いてみたApple Document:
https://developer.apple.com/documentation/corehapticsデプス推定(Apple提供CoreML)
概要:
・iOS11以上であればどのデバイスでも測定可能
・静止画でも動画でも測定可能
・人間以外のオブジェクトも識別可能 ・Depth測定以外(セグメンテーション)なども可能
・Portrait Effects MatteやSemantic Segmentation Matteなど、iOS13以降の標準APIよりかは精度は荒い印象(学習モデルが強化されたら今後の期待大)技術資料:
DepthPrediction-CoreMLApple Document:
https://developer.apple.com/jp/machine-learning/models/