20210504のiOSに関する記事は5件です。

iOS概論

目次 1. View 2. Inteface ※準備中 3. Framwork ※準備中 モチベーション まず、きっかけから話します。 わからない時にその都度調べるというのが、大部分のプログラマーの知識の獲得方法かと思います。 しかし、それでは体系的な知識が身に付いていかない体感があったので、ios業務のために一冊オライリーの参考書を読むことにしました。 結果、結構良かったです。体系的な知識が身に付きました。 また、本という枠に入った知識を知ることで必要な知識の輪郭が明確になった感じがしました。 これは個人的にはとても良くて、”技術者としての最低限知識はこの範囲。これ以上は都度調べればOK”と安心して割り切れるようになりました。 ということで本記事のモチベーションですが、 iosの仕組みを概要的に説明することです。 細かい実装テクニックなどは他の記事に任せて、仕組みをだーっと書いていきます。 また、ほぼ参考書からの知識になりますが、大部分を省略しています。 1200ページくらいあって全部有用な内容ではあるのですが、説明が長いと理解の妨げになるので、仕組みのエッセンスだけを採用しました。 皆様が開発に臨む上での最低限の知識の輪郭ができれば嬉しいなと思います。 参考文献 読んだ参考書です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TabBarControllerにViewを追加する

はじめに ツールバーでViewが追加できる項目があるかなと思ったら見当たず、調べた結果GUI操作で設定できました。 UITabBarControllerの仕様が面白いなと思ったので記事を書きました。 自分の学習メモ替わりに書いているので内容は薄いです。ですが初心者向けに具体的な手順を解説していきたいと思います。 作ったサンプル こんな感じです 実践 新しくstoryboardファイルを作ったら、そこに画面右上のプラスボタンをクリックして、そこからTabBar Controllerをドラッグしてきましょう。 生まれたばかりのTabBarControllerが表示されるはずです。 先ほどのような3画面にするため、もう一つViewを追加します。 TabBarControllerを追加したのと同様の手順でViewControllerを持ってきます。 したの画像のように画面が合計4つ表示されていれば大丈夫です。 そしてTabBarControllerのTabBarに3つ目のItemが表示されるように追加するやり方は、したのようにやります。 まずTabBarControllerを選択し、上部左の黄色い丸いマークを、controlキーを押しながら追加したいViewControllerへとドラッグします。 指を離すとメニューが表示されるので、Relationship Segueの下、ViewControllersを書いてあるところをクリックします。 これでTabBarControllerに画面が追加されます。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift言語 一定の範囲内でループする数値

コピペ用 例えば 99 の次が 0 で, 逆に 0 の前が 99 のようにしたい時に使う。 増加: ... 97 < 98 < 99 < 0 < 1 < 2 ... 減少: ... 3 > 2 > 1 > 0 > 99 > 98 ... extension_Int.swift extension Int { /// looped value in range func looped(in range: ClosedRange<Self>) -> Self { let min = range.lowerBound let max = range.upperBound if self < min { return max - ((min - self - 1) % (max - min)) } else if max < self { return min + ((self - max - 1) % (max - min)) } else { return self } } } 使い方 一度ループを無視した計算をした後に、 .looped(in:) を使う 99 + 1 = 100 0 - 1 = -1 let a = 100.looped(in: 0...99) // a: 0 let b = (-1).looped(in: 0...99) // b: 99 さいごに 99 + 300 のように、ループが何周も回っている場合にも対応しています。 自動でループする型を作りたい人は @propertyWrapper と組み合わせれば良さそうですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter 端末サイズによってWidgetの大きさを比率で指定する方法

はじめに 上記を参考にしました。間違ってたらごめんなさい。。 Widgetの大きさを固定すると FlutterでUIを作る際に、Widgetの大きさを固定値で指定するのはできるだけ避けたいところです。。。 Container( height: 40, width: 60, ), もし上記のように指定した場合、iPhone 5sとiPhone Xs Maxでは見た目のサイズ感が違っています。。 なぜなら、Flutterはどの端末でアプリが実行されているかわからないため、Containerは常に40 x 60となります。 さまざまな画面サイズに応じてWidgetの大きさを比率で指定する さてここから本題です。 どの画面サイズでも同じような大きさの比率でWidgetを表示させるために、画面をグリッドのように区切って「ブロック」ができるようにイメージします。 どの画面サイズでも「ブロック3つ分の横幅」みたいなサイズ指定をするイメージかと。 size_config.dartを作る lib/config/size_config.dart import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; class SizeConfig { static MediaQueryData _mediaQueryData; static double screenWidth; static double screenHeight; static double blockSizeHorizontal; static double blockSizeVertical; static double _safeAreaHorizontal; static double _safeAreaVertical; static double safeBlockHorizontal; static double safeBlockVertical; void init(BuildContext context) { _mediaQueryData = MediaQuery.of(context); screenWidth = _mediaQueryData.size.width; screenHeight = _mediaQueryData.size.height; blockSizeHorizontal = screenWidth / 100; blockSizeVertical = screenHeight / 100; } } SizeConfigを初期化して使う lib/main.dart import './config/size_config.dart'; class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { SizeConfig().init(context); return Center( child: Container( height: SizeConfig.blockSizeVertical * 20, width: SizeConfig.blockSizeHorizontal * 50, color: Colors.orange, ), ); } これで長方形のサイズは、 width : 画面の幅50% height : 画面の高さ20% となります!!! このSizeConfigはあらゆる場面で汎用的に使えると思うので、Flutter開発者は知っておいて損はないでしょう 説明が間違ってりしたら教えてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vision+CIFilter+Metal でオプティカルフローを使って呼吸を見る①

 iOS14で Vision Framework に追加されたオプティカルフローを使って 「呼吸(の様子)」 をすこし解析しやすいように可視化してみました。 <完成イメージ> お腹の膨らみ・引っ込みの向き・量を把握することで呼吸の数をカウントすることがゴールです(今回は呼吸の可視化まで。呼吸数のカウントは次の記事に記載予定)。 この記事で役に立つかもしれない情報 Vision Frameworkのオプティカルフローでできることは何? → 時系列の前後の画像からピクセル毎 にピクセルの移動量x, yを取得でできます。 → 移動量 x, y はCVPixelBuffer内にFloat型で交互に格納されてます。 CIFilterで独自のフィルタをMSL(Metal Shader Language)で作る方法は? → 後述 ※作成したサンプルはGithubに置いています。 1. オプティカルフロー 1−1. オプティカルフローとは 「視覚表現(通常、時間的に連続するデジタル画像)の中で物体の動きをベクトルで表したもの」(Wikipediaより) この記事で紹介する Vision Framework のオプティカルフローは、ピクセル毎に移動量x, yを導出するというもの。 下の動画は、左側が「移動したピクセル(移動量が大きいピクセル)」が確認できる例で、右側が「ピクセルが移動した方向」が確認できる例。 移動したピクセルを表示 ピクセルがどちらに移動したのか表示 移動前後の画像を与えることでピクセル毎に移動量を取得することができ、上の動画の例では撮影フレーム毎に前後の移動量を可視化してます。 画像解析で用いられるOpenCV のオプティカルフロー(Optical Flow)のドキュメント を見るとオプティカルフローのアルゴリズムにはいくつかあるようですが、Vision FrameworkのアルゴリズムがなんなのかはAppleのドキュメントから見つけられませんでした。 1−2. VNGenerateOpticalFlowRequest そではどうやってオプティカルフローを作るかというと、とても簡単で次のコードだけです。 var requestHandler = VNSequenceRequestHandler() let request = VNGenerateOpticalFlowRequest(targetedCGImage: newImage, options: [:]) try self.requestHandler.perform([request], on: baseImage) if let pixelBufferObservation = request.results?.first as? VNPixelBufferObservation { result = CIImage(cvImageBuffer: pixelBufferObservation.pixelBuffer) }  VNGenerateOpticalFlowRequest がオプティカルフローを取得するためのリクエストで、このサンプルでは時系列的に後になる画像を targetedCGImage に指定してます。  次に VNSequenceRequestHandler の perform() に上記のリクエストと 時系列で前側になる画像を on に指定してます(ちなみに、VNSequenceRequestHandler の代わりに VNImageRequestHandler を使っても動作します)。 あとはオプティカルフローを受け取るだけ。 request.results?.first(CVPixelBuffer)の中に 連続する x, y の移動量がFloat型で格納されます。 注意点としては、与える2枚の画像のサイズが一致していないと例外が発生します。 2. オプティカルフローの可視化 2−1. CIImageの挙動について  先のコードではピクセル毎のベクトル情報が入ったCVPixelBuffer を与えてCIImageをインスタンス化してます。  この記事のサンプルコードは WWDC2020の『Explore Computer Vision APIs』を参考にしており、その中でこうやってCIImageをインスタンス化しているのですが「画像情報」ではなく「移動量」という情報をCIImageに与えていいの?移動量は1を大きく超えるしマイナスになることもあるので、色としてそのまま使えないからダメでは?思いました。  で、インスタンス化したCIImageがどうなっているのか中を見ると次のようになってます。 (lldb) po result ▿ Optional<CIImage> - some : <CIImage: 0x283a801a0 extent [0 0 480 640]> affine [1 0 0 -1 0 640] extent=[0 0 480 640] opaque colormatch sRGB_to_workingspace extent=[0 0 480 640] opaque IOSurface 0x283a9bae0(1011) seed:1 RGf alpha_one extent=[0 0 480 640] opaque  ポイントとなるのはどうやら RGf の部分で、これはピクセルフォーマットのことでCIFormat .RGf で定義されており「1ピクセルあたり64ビットであり、RとGの値をFloatで持つ」とあります。つまりFloatのxがR、yがGに格納されている、と解釈でき、型がなにか別ものに変わっておかしなデータとして扱われる、、、という心配は減りました。また、CIImageはAppleのドキュメントに次のように記載されており、投入したデータが何もせず加工されてしまうということはなさそうです。 Although a CIImage object has image data associated with it, it is not an image. You can think of a CIImage object as an image “recipe.” A CIImage object has all the information necessary to produce an image, but Core Image doesn’t actually render an image until it is told to do so. (Google翻訳)CIImageオブジェクトには画像データが関連付けられていますが、画像ではありません。 CIImageオブジェクトは、画像の「レシピ」と考えることができます。 CIImageオブジェクトには、画像を生成するために必要なすべての情報が含まれていますが、Core Imageは、指示されるまで実際には画像をレンダリングしません。  ちなみに、移動量が入った CVPixelBuffer のデータの単位が「Float32が2つ」という情報はどこから来たのかというと、CVPixelBufferGetPixelFormatType を使ってCVPixelBufferを調べてみるとわかります。 let ostype = CVPixelBufferGetPixelFormatType(pixelBufferObservation.pixelBuffer) CVPixelBufferGetPixelFormatTypeを使うことで CVPixelBuffer のフォーマットを取得することができ kCVPixelFormatType_TwoComponent32Float となっていたのでFloat32が2つ分を扱う定義となっていました(OSTypeの見方はややこしく、取得したUInt32を8bit毎にASCIIにする。今回確認した値は0x32433066 -> "2C0F")。  CIImageの中で値は期待通りFloat32が2つを1単位として管理できているようなので、次に、このCIImageをそのままUIImageViewで表示するどうなるのか確認してみました。 ペンを左から右に移動 ペンを右から左に移動 すこしわかりづらいですが、ペンを左から右に移動したときはx方向(Red)の移動がプラスなのでペンは赤くなり、右から左に移動したときはマイナスなので黒くなっており、つまり、CIImage(なのかUIImageViewなのか)は表示できないような大きな値は無視するだけ、という動作です(そもそも、RGfのようにFloat32を扱えるフォーマットを用意しているので想定の動作、ということなのですね。そういえばMetalのフラグメントシェーダーの戻り値もfloat4やhalf4なので、GPUレベルで取り扱えるということ)。 2−2. CIKernelによる独自フィルタの作成  この記事ではオプティカルフローの可視化のため、CIKernelで三角形でフローの向きを表示する独自フィルタを実装しています。  CIKernelによる独自フィルタの作り方についてswift部分は参考にさせていただいたこちらの記事『Core ImageのカスタムフィルタをMetalで書く』のまんまです。  以下、本サンプルでの設定です。 Xcodeの設定  Xcodeの設定はAppleのドキュメントが更新されていないらしく、次のように設定する必要がありました(Xcode12.5で確認)。 MTLLINKER_FLAGSはドキュメントだと-cikernel ですがワーニングがでるので指示通り修正します。 MSL  この記事で使っている三角形でフローの向きを表示する独自フィルタはWWDC2020の『Explore Computer Vision APIs』で紹介されているコードをベースにしています。  注) WWDCのサンプルコードはブラウザからは参照できません。Apple Developerアプリからビデオをみると「コード」タブから参照できます。  ちょっと残念なことにWWDCのサンプルコードはMSLではなくCore Image Kernel Languageで書かれています(なんで?)。MSLへの移植は容易なのですが、1点躓いたところがあります。 Core Image Kernel Languageだと描画先の座標は destCoord() で取得できますが、MSLではこの方法では取得できませんでした。色を決める関数の引数にdestination型の引数を指定することで、その型のcoord() から取得することができます。ちなみに、swift側からは引数を与える必要はありません。 extern "C" { namespace coreimage { half4 flowView(sampler_h image, destination dest) { // 描画先の座標 float2 destCoord = dest.coord(); // 略 この辺りの仕様は『Metal Shading Language for Core Image Kernels』に記載されています。 その他MSLで三角形作る方法について興味がある方はGithubにソースをあげているので参照ください。 2−3. 比較する画像の決定  呼吸の振幅を確認するため、基点となる画像を決めて、基点の画像と各時点の画像を比較することで移動量の変化を確認します。  今回作成したサンプルは「観察開始」ボタンタップ時の画像を基点としています。 最後に iOS+オプティカルフローで何ができそうなのか? ググったり、精一杯思いついたものとして。。。 膨らんだり萎んだり、振動したりするものの観察 例)生体のイメージング。 本記事はもともとはこちらの論文『 オプティカルフローによる脈波検出』のように脈をとって心電図っぽくしたかったが、自分の手首をiPhoneで撮影しても全く変化がなかったので「呼吸」を採用。 何か動いたことの検出 例)だるまさんが転んだ、G検出機、防犯 どのくらいの移動があるのか、量の把握 例)交通量(?)、人混み(?) 次の記事で呼吸の数を数えてみます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む