- 投稿日:2019-05-22T23:51:13+09:00
処理中にNavigationBarのボタン操作を無効にする。
まくら
Navigation Controllerを使ったアプリを作っている時に、稀によくある問題に対するアプローチです。例えばこんな場合
- とある画面で時間のかかる処理中に読み込み中のインジケータを出したい
- インジケータが出ている間はナビゲーションバーの操作(戻るボタンとか)を無効にしたい
インジケータを出すのはいいとして、操作を無効にする方法はいくつか思いつきますが、今回は前面に目隠しを出して操作できないようにしてみましょう。
ほんだい
まずはソースをどうぞ。
インジケータを表示する部分です。// インジケータを表示する private func showIndicator() { // 背景になるView let backView = UIView() backView.backgroundColor = UIColor.init(white: 0.0, alpha: 0.5) backView.tag = 12345 // 消す時用にタグを付けておく // インジケータ let indicator = UIActivityIndicatorView() indicator.activityIndicatorViewStyle = .whiteLarge indicator.frame = CGRect(x: 0, y: 0, width: 100, height: 100) // NavigationControllerのViewを使う guard let naviView = self.navigationController?.view else { return } // 背景にインジケータを貼り付け backView.addSubview(indicator) // 背景をNavigationControllerのViewに貼り付け naviView.addSubview(backView) // サイズ合わせはAutoLayoutで backView.translatesAutoresizingMaskIntoConstraints = false backView.topAnchor.constraint(equalTo: naviView.topAnchor).isActive = true backView.bottomAnchor.constraint(equalTo: naviView.bottomAnchor).isActive = true backView.leftAnchor.constraint(equalTo: naviView.leftAnchor).isActive = true backView.rightAnchor.constraint(equalTo: naviView.rightAnchor).isActive = true // インジケータもAutoLayout indicator.translatesAutoresizingMaskIntoConstraints = false indicator.centerXAnchor.constraint(equalTo: backView.centerXAnchor).isActive = true indicator.centerYAnchor.constraint(equalTo: backView.centerYAnchor).isActive = true indicator.widthAnchor.constraint(equalToConstant: 100).isActive = true indicator.heightAnchor.constraint(equalToConstant: 100).isActive = true // インジケータ起動 indicator.startAnimating() }これを適当に実装するとこんな感じになります。
ポイントは
NavigationBarがグレーのViewに覆われていてBarButtonItemが押せない状態になっているところです。ここですね。
// NavigationControllerのViewを使う guard let naviView = self.navigationController?.view else { return }// 背景をNavigationControllerのViewに貼り付け naviView.addSubview(backView)要は、
NavigationController.ViewにもaddSubViewできますよって事ですね。
NavigationControllerの上にViewを貼り付けるので、当然その下のボタン等は押せなくなるわけです。
ちなみに注意点としては、当然ですが、
NavigationController.Viewが存在しないと使えません。
prepare for segueの遷移時などNavigationControllerが追いづらい時には正直使いづらいです。
現場からは以上です。
- 投稿日:2019-05-22T23:48:14+09:00
AnyとAnyObject
- 投稿日:2019-05-22T22:38:40+09:00
Ionic+Cordovaでuniversal linkの実装(ios版)
はじめに
Ionic + Cordovaでiosアプリのuniversal linkが実装できるか試してみました。universal linkは、URLを使ってアプリを起動するものです。
Ionic + Cordova環境でuniversal linkのプラグインは、検索するといくつか出てきて、どれを使うか迷いました。cordova-universal-links-pluginやcordova-universal-links-plugin-deluxeが検索されたのですが、廃止予定(DEPRECATED)のようです。
今回は、Ionicから出されているDeeplinksを使ってみました。Deeplinksの導入手順を実行しましたが、引数等のパラメータがわからないところがあり、試行錯誤しました。以下では、うまくいった方法を書いていきます。
環境
- Ionic 4.12.0
- Cordova 9.0.0
- Xcode 10.2.1
- Ionic Deeplink Plugin 1.0.19
手順
- universal linkを実装するアプリフォルダに移動します。アプリを新規に作成するときは
ionic startで作成します。$ cd myApp
- Deeplinkプラグインをインストールします。オプション引数の順番が違うとエラーが起きたので、順番もドキュメントに書かれている通りに入れる必要があります。
- URL_SCHEME : カスタムURLスキーム。今回は使いませんが、これを指定しないとインストールできませんでした。
- DEEPLINK_SCHEME : universal linkのスキーム。通常は
https。- DEEPLINK_HOST : universal linkのホスト名。ここでは、
example.comを指定$ ionic cordova plugin add ionic-plugin-deeplinks --variable URL_SCHEME='example' --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOST='example.com'
package.jsonに以下の設定が入るようです。"plugins": { "ionic-plugin-deeplinks": { "URL_SCHEME": "example", "DEEPLINK_SCHEME": "https", "DEEPLINK_HOST": "example.com" } }
- ionic nativeをインストールします。
$ npm install @ionic-native/deeplinks
- アプリのパッケージ名を指定します。iosの場合は、Bundle Identifierにこの値が入ります。
config.xmlのidフィールドを書き換えます。最初は、io.ionic.starterとなっているので、 例えば、com.exampleに書き換えます。<widget id="io.ionic.starter" version="0.0.1"><widget id="com.example" version="0.0.1">
- iosプラットフォームを追加します。
$ ionic cordova platform add ios
src/app/app.module.tsに以下の2つを追加します。import及びprovidersにDeeplinksを追加します。
import { Deeplinks } from '@ionic-native/deeplinks/ngx'; providers: [ StatusBar, Deeplinks, ]
src/app/app.component.tsに以下を追加します。
- import文
- constructorにDeeplinks
- initializeAppにDeeplinksの処理を追加します。この例は、https://example.com/home で起動された場合に、 HomePageを表示します。HomePageの部分は、
import { Deeplinks } from '@ionic-native/deeplinks/ngx'; import { HomePage } from './home/home.page'; constructor( private statusBar: StatusBar, private deeplinks: Deeplinks ) initializeApp() { this.platform.ready().then(() => { // 他のコード this.deeplinks.route({ '/home': HomePage }).subscribe(match => { console.log('Success', match); }, nomatch => { console.error('Fail', nomatch); }); }); }
- Xcodeを立ち上げのプロジェクト設定を変更します。 プロジェクトファイルは、
platform/ios/MyApp.xcodeprojです。TARGETのCapabilitiesを選び、Associated DomainsをONにします。このときに署名を選択する画面が出たときには、署名を選びます。+ボタンを押してapplinks:example.comを追加します。
- アプリを端末/シミュレータに配置します。 これでuniversal linkによっって起動されるアプリができました。別のアプリからuniversal linkを使ってこのアプリを起動することができます。
$ ionic cordova run ios参考文献
- 投稿日:2019-05-22T21:05:47+09:00
Swiftの型キャストPART1
型をダウンキャストする方法
Swift における型変換について調べました。
as?演算子の基本です。こんな継承したAnimal型があって
Animal型とその継承クラスclass Animal { // 体重とか } class Dog: Animal { var name: String } class Cat: Animal { var boxSize: Int } class Bird: Animal { let featherColor: Color }こんな関数があって
Animal型を返す関数func getPet() -> Animal { // return the pet } let pet = getPet() //petはAnimal型この時点で、
petの具体的な型は不明。
Dogかも知れないし、Catかも知れないし、Birdかも知れない。ペットの種類に合わせたタスクfunc walk(dog: Dog) { // お散歩させる print("Walking with \(dog.name)") } func cleanLitterBox(cat: Cat) { // ネコ用トイレを掃除する print("Cleaning the \(cat.boxSize) litter box") } func cleanCage(bird: Bird) { // 鳥かごをキレイにする print("Removing the \(bird.featherColor) feathers") }ペットに合わせたタスクを行う関数があるけど、定数
petを引数にするとエラーになる可能性がある。ダウンキャストして、適切にタスクを実行する
as?とif-letステートメントを組み合わせて、型キャストしつつオプショナルをアンラップする。ダウンキャストの例let pets = allPetAnimals() //あらゆる種のペットを含んだ配列 for pet in pets { if let dog = pet as? Dog { walk(dog: Dog) } else if let cat = pet as? Cat { cleanLitterBox(cat: Cat) } else if let bird = pet as? Bird { cleanCage(bird: Bird) } else { print("The type of this pet is unknown.") } }
- 投稿日:2019-05-22T16:41:17+09:00
iOSの自動更新購読 Auto-Renewing subscriptionでハマるたった1つの罠
出落ちです
共有シークレットを開発用だけ作成して本番用を忘れがち
開発環境では上手く動くが、本番環境で急に動かなくなったら共有シークレットを疑いましょう。
あと、TestFlightビルド時のレシートはSandBox環境に送りつけないとエラーになります。
- 投稿日:2019-05-22T16:40:06+09:00
SwiftのProtocol ExtensionはExistentialをopenする
この記事ではSwiftのProtocol ExtensionがExistentialをopenする事を説明します。
Existentialと型変数の違い
SwiftのExistentialは、プロトコルに対して自動定義される型で、そのプロトコルを満たす型を代入できます。
protocol P {} extension Int : P {} var p: P = 3ここで
pの型はPのexistentialです。Pのexistentialに代入できるのは、Pを満たす型なので、つまり、ジェネリクスの<T: P> Tで表される型変数です。Intは<T: P> Tにマッチしますね。ここで、
Pのexistentialと<T: P> Tの違いを説明します。
Pのexistentialは、条件を満たす型がなんでも代入できる型です。この型になった時点で、元の値の型の情報は消去されています。そのため、例えば下記のように異なる型を再代入する事ができます。protocol P {} extension Int : P {} extension String : P {} var p: P = 3 p = "str"一方、
<T: P> Tは、条件を満たす特定のある型を表しています。実行時の実際の値の型が
何になっているかはわかりませんが、コンパイル時の型検査ではTという表現で特定の型を表しています。そのため、下記のように特定の型を代入する事はできません。func g<T: P>(_ x: T) { var x = x x = "" // Cannot assign value of type 'String' to type 'T' }このように、existentialと型変数は、実行時に代入できる型の条件は同じでも、コンパイル時の型検査においては、型の同一性が失われている、維持しているのかという違いがあります。
SwiftのCore TeamメンバーのJoe Groffは、この違いを
value-level abstraction(Existential)とtype-level abstraction(型変数)と説明しています。Existentialのopen
ここで、Existentialから型変数を取り出す事を考えることができます。つまり、コードにおいて、Existentialに入っている型の同一性を復旧させます。
例1
この概念は、Generics Manifestoで紹介されています。
let e1: Equatable = ... let e2: Equatable = ... if let storedInE1 = e1 openas T { // T is the type of storedInE1, a copy of the value stored in e1 if let storedInE2 = e2 as? T { // Does e2 have type T? If so, copy its value to storedInE2 if storedInE1 == storedInE2 { ... } // Okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable } }この例は、
Equatableのexistentialであるe1とe2を==で比較する架空のコードです。実際の現在のSwiftにおいては、Equatableのexistentialはありませんが、それが仮にあった場合の話です。このコードでは、
openasという架空の言語機能を使って、e1から真の型Tを取り出し、e2を、そのTにキャストした後で、e1とe2の==比較をしています。この、真の型を取り出す操作をExistentialのopenと言います。なぜこのような手間を踏む必要があるかというと、
Equatableプロトコルは自身と同じ型を比較するためのプロトコルだからです。その機能を提供する==メソッドの型はfunc ==(lhs: Self, rhs: Self) -> Boolであり、両辺が同じ型でなければ呼び出せません。Equatableのexistentialであるe1とe2にはそれぞれ異なる型が入っているかもしれないので、そのままでは比較できないのです。ちなみに、本題とは関係ありませんが、標準ライブラリには
AnyHashableというtype erasureが提供されていて、これを使うと異なる型の等値比較を行うことができます。var e1 = AnyHashable(Int(3)) var e2 = AnyHashable(Double(3.0)) print(e1 == e2) // true例2
Joe Groffがフォーラムに投稿した書き込みにもう一つの例があります。
func bar(x: Collection) { let <X: Collection> openedX = x // X is now bound to the dynamic type of x let start = openedX.startIndex let first = openedX[start] // OK, indexing X with a value of type X.Index, to get a result of type X.Element }この例では、また別の架空の構文を使って、
Collectionのexistentialであるxから、真の型をXとして取り出しています。これにより、openedXのsubscriptにopenedXのstartIndexを渡しています。なぜこのような処理が必要かというと、
Collectionは自身のIndex型を持っているが、existentialのままではその型情報が消えてしまうために、型安全なコードが書けないからです。例えば標準ライブラリには
AnyCollectionというtype erasureがありますが、これを使うと下記のようなコードを書くことができます。このコードはコンパイルすることはできますが、実行時にクラッシュしてしまいます。それは、真の型Array<Int>のsubscriptに対して、String.Indexを渡しているからです。var c1 = AnyCollection([3]) var c2 = AnyCollection("str") print(c1[c2.startIndex])仮に
Collectionのexistentialがあったとしても、これと同様に、真の型がわからないので、真の型に対応したIndex型もわからず、型安全にできないのです。Protocol Extensionによるopen
ここまで見たように、Existentialのopenという概念は公式に説明されていますが、実際の言語仕様としては導入されていません。しかし実は、Protocol Extensionにおいては、すでにExistentialのopenが実現されているのです。
protocol P { func f() } extension Int : P {} extension P { func f() { } }このように、プロトコル
Pのメソッドfが、Protocol Extensionによって実装を与えられている状況を考えます。このとき、
Pのexistential型に対しても、fを呼び出すことができます。var x: P = 3 x.f()実はここでExistentialがopenされています。これは、
P.fにおけるselfの型を考える事でわかります。
P.fの型は何になっているのでしょうか。もし、Pのexistentialであったとしたら、前述の例と同様にして、selfの型の変数に対して、何かPを満たす型を代入できるはずです。extension P { func f() { var x = self x = Int(3) // Cannot assign value of type 'Int' to type 'Self' } }しかし、代入することはできません。一方で、
selfはPを満たしているとして扱えます。下記のように他にもメソッドgがあったとき、それを呼び出すことができます。protocol P { func f() func g() } extension P { func f() { g() } }
selfはこのような特性をもっているので、<T: P> Tという型になっていると考えられます。もう一つの根拠として、このコードのSILコードがあります。
// P.f() sil hidden @$s1a1PPAAE1fyyF : $@convention(method) <Self where Self : P> (@in_guaranteed Self) -> ()
P.fはこのように、<Self where Self : P>というジェネリックシグネチャを持っていて、引数の型がSelfになっています。そして、このextensionによって自動実装された
Int.fのSILは下記のようになります。// protocol witness for P.f() in conformance Int sil private [transparent] [thunk] @$sSi1a1PA2aBP1fyyFTW : $@convention(witness_method: P) (@in_guaranteed Int) -> () { // %0 // user: %2 bb0(%0 : $*Int): // function_ref P.f() %1 = function_ref @$s1a1PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %2 %2 = apply %1<Int>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () %3 = tuple () // user: %4 return %3 : $() // id: %4 } // end sil function '$sSi1a1PA2aBP1fyyFTW'これを見ると、
P.fである$s1a1PPAAE1fyyFに対して、型変数<Self>にIntを与えて呼び出している事が確認できます。これは通常のジェネリックメソッドと同じ機構になっています。このように、
selfの型が<Self : P> Selfという型変数になっているのは、実はextensionに限らずプロトコルのメソッドの共通の特徴です。Self conformanceとopen
protocolのexistentialは自分自身のprotocolに準拠しません。詳細は以前書いた記事を参照してください。
これは記述でいうと、
Pは<T: P> Tにマッチしないということです。一方で、ここまでで見たように、protocol extensionのメソッドが呼び出される時、P型であるselfが<Self: P> Self型としてopenされています。この違いを説明します。まず、なぜexistentialをopenできるのかというと、
P型の変数には、実際にPを満たす真の型を持った値が入っているからです。<Self: P> Selfとしてopenされているのは、この真の値が対象なのです。この値の型がPを満たしているという事は、Pの存在型に値を代入する際の条件でした。よって、それがopenできるのは当然です。一方、protocolのself conformanceとして議論しているのは、
Pのexistential型が、プロトコルPに準拠できるか、ということです。これは特定の値とは関係の無い、型の話です。ここで逆に、
P型がself-conformanceを満たす条件を考えてみます。それは@objcがついていて、スタティックメンバを持たないことです。スタティックメンバを持たないということは、そのP型に対して何か関わるときは、かならずインスタンスが絡んでいるということです。そして、インスタンスが絡んでいるということは、内部にselfを保持したexistential型の値が対象になっているということです。これはexistentialがopenできる理由と同じ事を考えているとわかります。protocol extensionによるself conformanceの制約の回避
これまでの話で、protocol extensionを使うとexistentialがopenしていることがわかりました。これを利用すると、self-conformanceがなくて困った場合に、問題を回避できる場合があります。
例えば、下記のように、
P型がEncodableであるときに、Pのexistentialを持つ型Sがあったとします。protocol P : Encodable {} extension Int : P {} struct S { var p: P init() { p = 3 } }
Sの値があるとき、pをencodeしようと思っても、JSONEncoderは<X: Encodable> Xは受け取れますが、Encodableのexistentialはサポートしていないため、できません。func main(_ s: S) throws { let encoder = JSONEncoder() try encoder.encode(s.p) // Protocol type 'P' cannot conform to 'Encodable' because only concrete types can conform to protocols }そこで、
Pに下記のようなextensionを与えて、それ経由の呼び出しに書き換えると、encodeができます。extension P { func encode(jsonEncoder: JSONEncoder) throws -> Data { return try jsonEncoder.encode(self) } } func main(_ s: S) throws { let encoder = JSONEncoder() try s.p.encode(jsonEncoder: encoder) }合わせて読みたい
型システムの理論からみるSwiftの存在型(Existential Type)
Swiftでprotocol型の値がそのprotocol自身にconformしていない理由
- 投稿日:2019-05-22T16:00:11+09:00
【Server-side-Swift】【Vapor】iOSアプリエンジニアが挑戦する初めてのサーバーサイド【勉強会レポ】
はじめにの前に(概要/対象読者)
概要
- SwiftでサーバーサイドAPIの開発ができる
-> サーバーサイドの知識があまりなくても開発できちゃう- Swiftすごい、Vaoprすごい
対象読者
- Swiftの基本的なコーディングを経験した人
- サーバーサイド知識はあまりないが(Swiftを用いた)サーバーサイドの構築に興味がある人
※今回の勉強会で使用したコードは以下に置いています。Vaporで生成されたコードをちょっといじっただけですが。
https://github.com/s-kamada/Swift-serverあくまでレポなので勉強会の内容に合わせて記述します。
はじめに
今年の3月に大学を卒業し、4月からアプリを作る会社に入社した者です。
まだ研修期間中なのですが、研修では所定の要件に沿ったiOSアプリとAndroidアプリを製作しています。先日研修のiOSアプリの製作が終了し、「Swiftの知見をさらに深めたい!」ということでこちらの勉強会に参加してきました。
iOSアプリエンジニアが挑戦する初めてのサーバーサイド(connpassリンク)
スライド(SlideShare)
大学時代にエンジニア志望の仲間として緒に勉強していた友人が講師役の勉強会です。
生粋のiOS/Swiftエンジニアですが普段は睡眠とAIを掛け合わせた研究をしているんだとか。Swiftでサーバーサイドを作れるらしい
Swiftで書くと何がいいの?
- 早い
Swiftは4秒で終了しました。 Javaは4.3秒と、ほぼ同じぐらいです。このテストにおけるSwiftとJavaの性能はほぼ同じですね。Node.jsを見てみましょう。15.8秒かかっています。SwiftやJavaの約4倍ですね。Rubyはどうでしょう。とても遅いです。1
- 省メモリ
RSS(ベンチマークを実行するのに必要な物理メモリ領域)のベンチマークを見てみると、Swiftでは15MBで、Javaでは32MBです。必要なメモリはJavaの半分なのに、同じ性能です。Node.jsはJavaよりは若干少ないですが、Swiftよりずっと多いです。Rubyはたくさん必要ですね。1
なんでVaporなの?
Server-side-swift のフレームワークは数多あるけれど
- perfect
- Swifton
- Slimane
- Express
- HTTPSwiftServor
- Kitura
- Vapor etc......
主流はKitura、Vapor。
海外ではVaporの方がファンが多く、サーバーサイドの知識があまりない人はVaporの方がいい、ただしVaporの方がSwiftのアップデートへの対応が早いが、開発者もそれについていくためのコストがかかる、など長短あるようです。2Vaporを使う
※ Homebrew、Swift/Xcode(4.1.0以上)がインストールされている前提です
構築
インストール
$ brew install vapor/tap/vaporプロジェクト作成
$ cd [プロジェクトを置きたいディレクトリ] $ vapor new [プロジェクト名]プロジェクトを開く(Xcode)
すこし時間がかかります
$ vapor xcode Generating Xcode Project... Open Xcode project? y/n> y Opening Xcode project...プロジェクト構成
基本的に編集するのはAppフォルダ内のファイルです
Controllersはコントローラ、Modelsはモデルを入れるフォルダ、など見た通りの命名がされています。コーディング(抜粋)
自動生成されたものだけも十分動きますが、ちょっといじります。
今回は「Todo管理アプリ」のサーバーサイドAPIを作成する前提で編集します。モデル
TodoModel.swiftfinal class TodoModel: SQLiteModel { /// The unique identifier for this `Todo`. var id: Int? /// A title describing what this `Todo` entails. var title: String var isFinished: Bool /// Creates a new `Todo`. init(id: Int? = nil, title: String, isFinished: Bool) { self.id = id self.title = title self.isFinished = isFinished } }データモデルです。マイグレーション(自動的にデータベースの更新・管理を行う)などの
extensionも実装できます。コントローラ
TodoController.swiftfinal class TodoController{ /*中略*/ //新しいTodoを生成する func create(_ req: Request) throws -> Future<Todo> { return try req.content.decode(Todo.self).flatMap { todo in return todo.save(on: req) } } //すでに登録されているデータを更新する func update(_ req: Request) throws -> Future<Todo> { return try flatMap(to: Todo.self, req.parameters.next(Todo.self), req.content.decode(Todo.self)) { todo, updateTodo in todo.title = updateTodo.title todo.isFinished = updateTodo.isFinished return todo.save(on: req) } } }データの読み込み、生成、削除の操作は自動で生成されますが、不足しているので更新処理を追記しました。
ルーティング
routes.swiftpublic func routes(_ router: Router) throws { // そのエンドポイントのルートにアクセスした場合 // Basic "It works" example router.get { req in return "It works!" } // http://(エンドポイント)/hello/world にアクセスした場合 // Basic "Hello, world!" example router.get("hello","world") { req in return "Hello, world!" } // http://(エンドポイント)/todos に対してAPIを叩く場合 // Example of configuring a controller let todoController = TodoController() router.get("todos", use: todoController.index) }ルーティングを定義するファイルです。
ビルドしてサーバーを立てる
左上のバーを
Run > My Macと設定してビルドします。この場合はlocalhost:8080にサーバーが立ちます。
ターミナル上でもビルドが可能ですがXcode上でビルドするよりも時間がかかるようです。$ vapor build $ vapor run動いたか確認してみる
ブラウザで
localhost:8080に移動してみると...
動いてるみたいですね![]()
他のAPIも叩いてみます。ここからはAPIツールのPostmanを使用しますまとめ
- サーバーサイドAPIをSwiftだけで簡単に作れる。やったね!
![]()
- 課題(今回の勉強会では触れなかったこと)
- DBとの連携が必要(今回はしていません。連携の設定をしないとXcodeを落とした際にデータがリセットされます)
- マイグレーションの方法
- デプロイ方法
Try! Swift NYCの講演より。現在は少し記録が更新されているようですが、やはりSwiftは早いかつ省メモリなようです(The Benchmarks Game) ↩
ここやHacking with Swiftなど、KituraとVaporを比較考察している記事が多いです。 ↩
- 投稿日:2019-05-22T12:05:57+09:00
【学習記録31】2019/5/22(水)
学習時間
1.0H
使用教材
・本気ではじめるiPhoneアプリ作り Xcode 10.x対応 (Informatics&IDEA)
学習分野
chapter4
コメント
学習開始からの期間:32日目
今日までの合計時間:90.0H
- 投稿日:2019-05-22T11:32:53+09:00
swift バーション確認について
バージョン調査(xcode)
xcode場合は、xcodeを開くと書いてあります
バージョン調査
ターミナルにswift -vと打ち込みます。
$ swift -vたくさん出るが最後の方にswiftのバージョンが出ます
参考サイト
バージョン確認https://pippi-pro.com/xcode-version-confirmation#Xcode-2
https://qiita.com/t_punch/items/21b745054356e7828c35
- 投稿日:2019-05-22T11:15:47+09:00
円弧でパーセントグラフを作る
描く順序としては
➀円の半径とパーセントを引数にする円弧のパスを作る関数を定義し、それを指定の座標に描く。
➁ここでパスを平行移動する
➂さらに62.3%のようにテキストをコンテキストに描画して円弧のグラフを合成。
です。パーセント円弧を作る関数
func arcPercent(_ radius:CGFloat, _ percent:Double) -> UIBezierPath { //終わりの角度を計算します let endAngle = 2*Double.pi*percent/100-Double.pi/2 //StartAngleで初めの角度指定します。 let path = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: radius, startAngle: CGFloat(-Double.pi/2), endAngle: CGFloat(endAngle), clockwise: percent > 0 ) return path }パスを平行移動する
let tf = CGAffineTransform(translationX: view.center.x, y: view.center.y) //ビューの中心に平行移動します arcpath.apply(tf)文字を合成する
let font = UIFont.boldSystemFont(ofSize: 28) //フォント属性を指定します let textFontAttributes = [NSAttributedString.Key.font:font,NSAttributedString.Key.foregroundColor:UIColor.gray] //表示する数値 let drawString = String(percent) + "%" let posX = view.center.x-45 let posY = view.center.y-15 let rect = CGRect(x: posX, y: posY, width: 90, height: 30) //テキストを描く drawString.draw(in:rect,withAttributes:textFontAttributes)以上をまとめたクラス
import UIKit class ViewController: UIViewController { //パーセントの円弧のパスを作る func arcPercent(_ radius:CGFloat, _ percent:Double) -> UIBezierPath { //パーセントの最終角度に換算します let endAngle = 2*Double.pi*percent/100-Double.pi/2 let path = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: radius, startAngle: CGFloat(-Double.pi/2), endAngle: CGFloat(endAngle), clockwise: percent > 0 ) return path } func drawLine() -> UIImage { //イメージ処理の開始 let size = view.bounds.size UIGraphicsBeginImageContextWithOptions(size, false, 1.0) let percent = 62.3 //円弧のパスを作る UIColor.red.setStroke() let arcpath = arcPercent(80, Double(percent)) arcpath.lineWidth = 60 arcpath.lineCapStyle = .butt //パスを平行移動する let tf = CGAffineTransform(translationX: view.center.x, y: view.center.y) arcpath.apply(tf) //円弧を描画 arcpath.stroke() //『何パーセント』の数字を書く let font = UIFont.boldSystemFont(ofSize: 28) let textFontAttributes = [NSAttributedString.Key.font:font,NSAttributedString.Key.foregroundColor:UIColor.gray] let drawString = String(percent) + "%" let posX = view.center.x-45 let posY = view.center.y-15 let rect = CGRect(x: posX, y: posY, width: 90, height: 30) //テキストを描く drawString.draw(in:rect,withAttributes:textFontAttributes) //イメージコンテキストからUIImageを作る let image = UIGraphicsGetImageFromCurrentImageContext() //イメージ処理の終了 UIGraphicsEndImageContext() return image! } override func viewDidLoad() { super.viewDidLoad() //図形のイメージを作る let drawImage = drawLine() //イメージビューに設定する let drawView = UIImageView(image: drawImage) //画面に表示する view.addSubview(drawView) } }
- 投稿日:2019-05-22T10:10:14+09:00
[Swift]ポインタ使用パターンをテストする
はじめに
UnsafeMutablePointer の allocate/deallocate/initialize/move などのメソッドを一通り使用する。
また、ポインタの扱いでメモリリークが発生しないようにする。テストで共通使用する関数・クラス
import Foundation import CoreAudio // オブジェクトのアドレスを保存するマップ var addressMap = [String: Bool]() // オブジェクトのアドレス(文字列)を得る func address(_ object: AnyObject) -> String { return "\(Unmanaged.passUnretained(object).toOpaque())" } // テストの終了処理 func testFinally() { assert(addressMap.count == 0) // アドレスマップの要素数を確認する(0でなければメモリリークをしている print("- OK\n") } // モッククラス class Mock { // インスタンス生成時にアドレス表示&アドレスをマップに保存する init () { print("init : \(address(self))") addressMap[address(self)] = true // Store address. } init(_ value: Int) { print("init : \(address(self))") addressMap[address(self)] = true // Store address. v1 = value } // deinit() でアドレスマップからアドレスを削除する deinit { print("deinit : \(address(self))") addressMap.removeValue(forKey: address(self)) // Remove address. } // 自由に設定できる変数 var v1 = Int(0) }テストケース
allocate / deallocate パターン
func testAllocateAndDeallocate() { print("# \(#function)") do { let p = UnsafeMutablePointer<Mock>.allocate(capacity: 1) // 未初期化のメモリはアクセスする前に、初期化する必要がある // initialize せずに以下のコードを実行するとエラーになる // // p.pointee.v1 = ... p.deallocate() // deallocate をコールしないとメモリリークが発生する } testFinally() // メモリリークが発生していないことを確認する }initialize / deinitialize パターン
func testInitializeAndDeinitialize() { print("# \(#function)") do { // モックインスタンスを作成する var mock = Mock() mock.v1 = 10 let p = UnsafeMutablePointer<Mock>.allocate(capacity: 1) defer { p.deallocate() } // インスタンスから UnsafeMutablePointer を initialize する p.initialize(from: &mock, count: 1) p.pointee.v1 = 20 // initialize した UnsafeMutablePoitner はアクセス可能になる assert(address(mock) == address(p.pointee)) // インスタンスと UnsafeMutablePointer は同一アドレスである assert(mock.v1 == 20) p.deinitialize(count: 1) // initialize した分、 deinitialize をコールしないとメモリリークが発生する } testFinally() }initialize / move パターン
func testInitializeAndMove() { print("# \(#function)") do { // UnsafeMutablePointer を allocate する let p = UnsafeMutablePointer<Mock>.allocate(capacity: 1) defer { p.deallocate() } // インスタンス値から UnsafeMutablePointer を初期化する p.initialize(to: Mock()) p.pointee.v1 = 10 // p を move してインスタンスを取得する // move したことにより、 deinitialize しなくても p は deinit される let mock = p.move() // p.deinitialize() は不要 assert(address(mock) == address(p.pointee)) assert(mock.v1 == 10) } testFinally() }moveInitialize パターン
func testMoveInitialize() { print("# \(#function)") do { var mock = Mock() mock.v1 = 10 let p = UnsafeMutablePointer<Mock>.allocate(capacity: 1) defer { p.deallocate() } p.moveInitialize(from: &mock, count: 1) p.pointee.v1 = 20 // moveInitialize() したので p.deinitialize() は不要 assert(address(mock) == address(p.pointee)) // Check same address. assert(p.pointee.v1 == 20) } testFinally() }initialize/ assign パターン
func testInitializeAndAssign() { print("# \(#function)") do { var mock = Mock() mock.v1 = 10 var mock2 = Mock() mock2.v1 = 20 let p = UnsafeMutablePointer<Mock>.allocate(capacity: 1) defer { p.deallocate() } p.initialize(from: &mock, count: 1) // mock1 で初期化する p.assign(from: &mock2, count: 1) // mock2 をアサインする p.pointee.v1 = 30 // UnsafeMutablePointer は mock2 を指している assert(address(mock2) == address(p.pointee)) // Check same address. assert(mock2.v1 == 30) p.deinitialize(count: 1) } testFinally() }moveAssign パターン
func testMoveAssign() { print("# \(#function)") do { var mock = Mock() mock.v1 = 10 var mock2 = Mock() mock2.v1 = 20 let p = UnsafeMutablePointer<Mock>.allocate(capacity: 1) defer { p.deallocate() } p.initialize(from: &mock, count: 1) // mock1 で初期化する p.moveAssign(from: &mock2, count: 1) // mock2 を move する p.pointee.v1 = 30 // UnsafeMutablePointer は mock2 を指している assert(address(mock2) == address(p.pointee)) // Check same address. assert(mock2.v1 == 30) // move したので p.deinitialize() は不要 } testFinally() }Array パターン
func testArray() { print("# \(#function)") do { var array: [Mock] = [Mock(10), Mock(20), Mock(30)] let p = UnsafeMutablePointer<[Mock]>.allocate(capacity: 1) defer { p.deallocate() } // Array から UnsafeMutablePointer を初期化する p.moveInitialize(from: &array, count: 1) p.pointee[0] = Mock(40) p.pointee[1] = Mock(50) p.pointee[2] = Mock(60) assert(array.count == p.pointee.count) assert(address(array as AnyObject) == address(p.pointee as AnyObject)) assert(array[0].v1 == 40) assert(array[1].v1 == 50) assert(array[2].v1 == 60) } testFinally() }AudioBufferList パターン
func testAudioBufferList() { print("# \(#function)") do { let abl = AudioBufferList.allocate(maximumBuffers: 1) free(abl.unsafeMutablePointer) // AudioBufferList を allocate() したあとは free() が必要 } }各テストケースを実行する
上記のテストをまとめて繰り返し実行し、エラーとメモリリークがないことを確認する
while true { autoreleasepool { testAllocateAndDeallocate() testInitializeAndDeinitialize() testInitializeAndMove() testMoveInitialize() testInitializeAndAssign() testMoveAssign() testArray() testAudioBufferList() Thread.sleep(forTimeInterval: 1) } }











