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

【Swift】処理フローが「一発」でわかる非同期の順次処理の書き方

はじめに 今回は、スパゲッティコードにならないような順次処理を考えてみたので記事にしてみました。 特定の順番で毎回実行される処理って、条件分岐や繰り返しが無いので、シンプルに書くことができます。 しかし、非同期で実行される処理がある場合などは、可読性を意識して書かないと、案外メチャクチャになりがちです。(←過去の経験より) この記事で学べること 順次処理とは? 非同期で実行される順次処理を実装する上での課題はなにか? 処理フローを追いやすい順次処理の書き方 結論だけ確認したい方は、「処理フローを追いやすい順次処理の書き方」から読んでいただければと思います。 順次処理ってなに? 指定した順番に沿って順番に実行されていくプログラムのこと 日常生活にたとえてみると、 朝起きる → 朝食を食べる → 歯を磨く → 着替える → 家を出る というような、一連の流れがこれに当てはまります。 とてもとてもシンプルな構造ですので、一見プログラムもシンプルになると思われがちです。 非同期処理を含む順次処理の課題とは? さっきの日常生活のたとえを、実際の要件にありそうな仕様に変えてみます。 今回は、起動時の処理を例にして説明しようと思います。 アプリ起動 → バージョンチェックAPI呼出 → ユーザー情報取得API呼出 → 商品情報取得API呼出 → トップ画面へ遷移 この一連の流れをSwiftでコードに落とし込むとこんな感じになります。 今回の場合、アプリを起動すると、SplashViewControllerが一番最初に呼ばれるとします。 ※サンプルコードなので中身ちゃんと見なくて大丈夫です。 ※非同期処理の[weak self]など割愛してます。 SplashViewController.swift import UIKit class SplashViewController: UIViewController { let api: APIManeger = APIManeger() override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 1. 起動したのでバージョンチェック fetchVersionInfo() } func fetchVersionInfo() { print("!!!! start version check") api.requestVersionCheck() { result in if result.isSuccess { // バージョンアップが必要ならAppStoreに飛ばす etc... // バージョンチェック正常終了 // 2. ユーザー情報取得API呼出 self.fetchUserInfo() } else { // エラーだったらアプリ終了 } } } func fetchUserInfo() { print("!!!! start userInfo request") api.requestUserInfo { result in if result.isSuccess { // ユーザー情報から必要な情報を取得したり、データを加工したりする処理 etc... // 正常終了 // 3. 商品情報取得API呼出 self.fetchProductInfo() } else { // エラーだったらアプリ終了 } } } func fetchProductInfo() { print("!!!! start productInfo request") api.requestProductInfo { result in if result.isSuccess { // 商品情報情報から必要な情報を取得したり、データを加工したりする処理 etc... // 正常終了 // 全部終わったのでトップ画面へ遷移する } else { // エラーだったらアプリ終了 } } } } いかがでしょうか。 自分はこの実装を見たときに 「順番で実行されるメソッドがどこで呼ばれているのかぱっと見てわからない」 という課題を感じます。 上記のような実装の場合、 「AがBを呼び出す」「BがCを呼び出す」 というような構造になっているため、上からちゃんと全部読まないと、どこで「C」が呼び出されているのかがわかりません。 今回は、サンプルコードでコードが短いため、比較的すぐに流れを追うことができると思いますが、 実際のプロジェクトのコードになると、 requestメソッドのコールバックでエラーハンドリングや色々な処理が行われる 今回は3つの処理しかしてないが、実際はもっと多くの処理を行う可能性がある 仕様変更でB処理とC処理の間に「B'処理」を入れたいなどが起こる可能性がある といったコードが複雑化する要因が増えるため、サンプルコードよりも処理を追うことが困難になると予測されます。 処理フローを追いやすい順次処理の書き方 とりあえずコードはこんな感じ! SplashViewController.swift import UIKit enum SplashTask { case fetchVersionInfo case fetchUserInfo case fetchProductInfo static func getTask() -> [Self] { [.fetchVersionInfo, .fetchUserInfo, .fetchProductInfo] } } class SplashViewController: UIViewController { let api: APIManeger = APIManeger() var splashTasks: [SplashTask] = [] override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // getTask()によって、起動時の処理と順番を確定させる splashTasks = SplashTask.getTask() // 初回のみここで実行処理を呼び出す executeSplashTask() } private func executeSplashTask() { if splashTasks.count == 0 { // タスクがなくなったので起動処理終了 -> トップ画面へ遷移 print("!!!! finish all") return } let task = splashTasks.removeFirst() switch task { case .fetchVersionInfo: fetchVersionInfo { self.executeSplashTask() } case .fetchUserInfo: fetchUserInfo { self.executeSplashTask() } case .fetchProductInfo: fetchProductInfo { // この処理が最後なので念の為処理(整合性が取れてれば実際は不要) splashTasks.removeAll() self.executeSplashTask() } } } func fetchVersionInfo(completion: () -> Void) { print("!!!! start version check") api.requestVersionCheck() { result in if result.isSuccess { // バージョンアップが必要ならAppStoreに飛ばす etc... // バージョンチェック正常終了 completion() } else { // エラーだったらアプリ終了 } } } func fetchUserInfo(completion: () -> Void) { print("!!!! start userInfo request") api.requestUserInfo { result in if result.isSuccess { // ユーザー情報から必要な情報を取得したり、データを加工したりする処理 etc... // 正常終了 completion() } else { // エラーだったらアプリ終了 } } } func fetchProductInfo(completion: () -> Void) { print("!!!! start productInfo request") api.requestProductInfo { result in if result.isSuccess { // 商品情報情報から必要な情報を取得したり、データを加工したりする処理 etc... // 正常終了 completion() } else { // エラーだったらアプリ終了 } } } } この順次処理の特徴は、 各リクエストメソッドが次にどのメソッドを呼び出すかを決定する責務を持っていないという点 です。 それにより、 リクエストメソッドの中身を覗かなくても、プログラマは順次処理の流れを把握することができるようになりました。 最初のコードでは 「AがBを呼び出す」「BがCを呼び出す」 という処理を各メソッドで行っていましたが、 今回は、リクエストが正常に完了したら、completion()を呼び出して終わりです。 起動処理のコントロールは、executeSplashTask()が行い、順序をコントロールしているのはSplashTaskという列挙型です。 executeSplashTask()は、 Taskリストの先頭から切り出したTaskを実行し、Taskリストが空になったら、起動処理がすべて完了したとみなす仕組みを採用しています。 そして、自分自身を再帰呼び出しすることで処理を次に進めます。 まとめ 非同期処理を含む順次処理では、順序を決定するメソッドと実際の処理を行うメソッドに責務を分散させることで、プログラマは各処理の中身を知らなくても、順次処理の流れを理解することができる。 順所の変更や処理の追加といった仕様変更が発生しても、既存のロジックにほぼ影響を与えることなく修正が可能になる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS シミュレーターに写真をドラッグ&ドロップすると PHPhotosErrorDomain エラーが発生する

概要 macOS に保存されている写真を iOS のシミュレーターで使う際に、mac から シミュレーターにドラッグ&ドロップすればコピーできるとあったため、実施したところエラーが発生した。 エラー内容 One or more media items failed to import: ファイルパス: The operation couldn’t be completed. (PHPhotosErrorDomain error -1.) 解決方法 コピー元のディレクトリー(xxx)がダウンロードディレクトリー(~/Downloads/xxx)だったため、ホームディレクトリー(~/xxx)へ移動したところ解決した。 参照 URL
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactエンジニアがReact Nativeを使ってみた

はじめに もともとReactを使用してweb開発をしていましたが、React Nativeを使用したモバイル開発に関わることになりました。Reactを使っていた人間がReact Nativeを勉強してみた所感を書きたいと思います。本記事はReact Native公式ドキュメント[1]を参考に書いていきます。 前提 Reactが使える TypeScriptが使える モバイルは初めて 環境構築と簡単な動作確認が知りたい人は前回の記事[1]で行っているのでそちらを参照してください。 React Nativeの必要性 現在モバイル端末での使用OSはAndroidOSとiOSで二極化しており、なおかつそれぞれでの使用プログラミング言語も異なります。昔はiOSではObjective-C、AndroidではJavaが使用されていました。しかし、最近ではiOSではSwift、AndroidではKotlinという言語が使われており、2つのOS合わせると最大4つのコードが存在する可能性があるわけです。もちろんこれは開発効率の観点から見て良くなく、これを解決するためにReact Nativeなどのクロスプラットフォームで開発出来るフレームワークが開発されてきたわけです。 React Nativeでないとだめなのか 近年よく比べられるフレームワークとしてFlutterがあります。よくある議論として「Flutter vs React Native」のようなものがありますが、開発コストという点で見ればReact Nativeの方がFlutterよりも明らかに効率的です。FlutterではDartという言語を新しく覚える必要があり、学習コストは高いです。それに対してReact NativeはReactの知識を利用して開発出来るので、学習コストは比較的低いです。パフォーマンス的にはFlutterの方が良いのかもしれませんが、そこまでパフォーマンスを求めないのであれば手軽に作成できるReact Nativeの方が良いでしょう。ただそのまま使えるわけではなく、多少は覚えることがあるので注意です。 ここからはReact Nativeの概念について話していきたいと思います。 React Nativeのコンポーネント React Native公式ではコンポーネントを何種類かに分けて呼んでいます。 Native Components それぞれのOSでのViewを担当する部分がバックアップしているコンポーネント。 Core Components React Nativeですぐに使えるNative Componentsのセットコンポーネント。 community-contributed components React Nativeコミュニティが開発する各OS独自のNative Component。 各OSの特定の部分に対応するものがReact Nativeコンポーネントであり、それをまとめたものがCore Componentsであると。恐らく僕たちが使うのは基本的にCore Componentsになるということでしょう。それで足りない場合はcommunity-contributed componentsなどにないか探してみるというような感じなのでしょうか。 公式のベン図的な画像を載せておきます。 [1]より引用 あくまでReact Native ComponentsはReact Componentsの部分集合であるということなので、この図からもReact Componentsを理解していれば問題なさそうということが分かりますね。 React Nativeの文法 さてここからが本題です。ReactにはないReact Native特有の部分を見ていきたいと思います。 App.tsxを理解する まずはexpo initコマンドで作成されたテンプレートのApp.tsxの解読をしていきましょう。文章はHello,Worldに変更してあります。 App.tsx import { StatusBar } from "expo-status-bar"; import React from "react"; import { StyleSheet, Text, View } from "react-native"; export default function App() { return ( <View style={styles.container}> <Text>Hello,World</Text> <StatusBar style="auto" /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff", alignItems: "center", justifyContent: "center", }, }); まず、ReactをimportしているようですがこれはReactと違って必要でした。内部的に使っているんでしょうか。StatusBarというやつはなんだろうと調べると、モバイル上部に表示される電源残量や電波状況を表すアイコンが並んでいる部分をステータスバーというようです。ViewとTextはReact Nativeのコアコンポーネントで、詳しくは後ほど説明します。Viewに対してstyleを適用しているようですが、stylesというオブジェクトのプロパティとしてcontainerを設定し、スタイルを記述しています。スタイルについても後ほど詳しく記述しますが、ここではキャメルケースでの書き方でないといけないようで、Reactでのインラインスタイルによるスタイリングと同じですね。 ざっと見ていきましたが、大枠はReactと変わらないようなので、あとは各コンポーネントを覚えていくだけかなと思います。 コンポーネントのprops React Nativeのリファレンスを見ると、propsがiOSとAndroidOSで異なる物が多いようです。また、propsの数自体相当多く、これを全て覚えるのはかなり大変だと思います。この記事では各OS限定のpropsは基本的に扱わず、使いそうなpropsがあれば紹介するという感じで紹介していきますので、詳しく知りたい方は公式のリファレンス[3]を参考にしてください。 View React Nativeにおける一番基本的なコンポーネントです。divタグのようなものだと思って貰えばいいと思います(厳密には少し違う)。 SafeAreaView(iOS) iOSデバイスではノッチや角丸などの物理な違いによって安全にレンダリング出来る範囲が異なります。このコンポーネントでは安全な範囲内でコンテンツをレンダリングすることが出来ます。 Text 文字を書く場合は基本的にこれを使います。Viewに直接書いても反映されません。 ネスト出来る AndroidとiOSでは文字列の範囲に特定の書式をつけることが出来、この実現のためにTextはネストすることが出来ます。ネストしたテキストだけをスタイリングすることで、任意の範囲に書式をつけることが出来ます。 ・レイアウトが特殊 また、Textはレイアウトに関して特殊です。Test内の要素は長方形ではなくなる場合があり、行の終わりを見て折り返します。 ・スタイルの継承が制限されている テキストのスタイルはTextによってしか継承されません。つまりViewでテキストに関するスタイルをしても繁栄されません。特定のスタイルのTextを使いまわしたい場合は既存のTextを拡張したコンポーネントを定義することが公式では進められています。Text内では継承されるため、ネストしたTextでは親のスタイルが適用されます。 Image 画像を利用する際に使います。propsのsourceに参照を渡すことで画像が表示できます。下記に公式の例を載せます。 import React from 'react'; import { View, Image, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ container: { paddingTop: 50, }, stretch: { width: 50, height: 200, resizeMode: 'stretch', }, }); const DisplayAnImageWithStyle = () => { return ( <View style={styles.container}> <Image style={styles.stretch} source={require('@expo/snack-static/react-native-logo.png')} /> </View> ); } export default DisplayAnImageWithStyle; [3]より引用。 TextInput 入力で使用します。全部説明すると長くなるので例を下記に示します。 import React from "react"; import { SafeAreaView, StyleSheet, TextInput } from "react-native"; const UselessTextInput = () => { const [text, onChangeText] = React.useState("Useless Text"); const [number, onChangeNumber] = React.useState(null); return ( <SafeAreaView> <TextInput style={styles.input} onChangeText={onChangeText} value={text} /> <TextInput style={styles.input} onChangeText={onChangeNumber} value={number} placeholder="useless placeholder" keyboardType="numeric" /> </SafeAreaView> ); }; const styles = StyleSheet.create({ input: { height: 40, margin: 12, borderWidth: 1, padding: 10, }, }); export default UselessTextInput; [3]より引用 一番ある使い方はonChangeTextにstateのセッターを入れておいて、入力を反映しつつstateで保持みたいな感じだと思います。気になるのはTypeScriptで使う際にnumberとstringどうやって分けているんだろうというとこですね。時間のある時に検証したら更新します。 ・下にボーダーがある TextInputはデフォルトでビューの下部にボーダーを持ちます。これはシステムが提供する背景画像によってパディングが設定されており、変更することは出来ません。この問題を回避するには、高さを明示的に設定しないようにして、システムが適切な位置にボーダーを表示するようにするか、underlineColorAndroidをtransparentに設定してボーダーを表示しないようにすることが必要なようです。詳しくは公式リファレンスを見てください。 ScrollView リファレンスを直訳したんですが、少し意味がわからないところがあったので分かりにくいかもしれません。 スクロールしたい場合に使います。React NativeではViewを使って表示する場合、表示画面領域より大きいものをレンダリングしようとしても自動的にスクロールするようにはならないようです。スクロールしないことは少ないと思うので基本的にはこれを使うことになるんでしょうか。 ・全ての親Viewが高さ制限されている必要がある リファレンスではboundedと書かれていたのですが、この訳で合っているのかあやしいです。高さを制限する方法は2つありViewの高さを直接設定すること(非推奨)と全ての親Viewに高さを設定することです。{flex:1}を忘れるとエラーが出るとリファレンスには書いてあるのですが、よく分かりません。下記に例を載せておきます。 import React from 'react'; import { StyleSheet, Text, SafeAreaView, ScrollView, StatusBar } from 'react-native'; const App = () => { return ( <SafeAreaView style={styles.container}> <ScrollView style={styles.scrollView}> <Text style={styles.text}> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </Text> </ScrollView> </SafeAreaView> ); } const styles = StyleSheet.create({ container: { flex: 1, paddingTop: StatusBar.currentHeight, }, scrollView: { backgroundColor: 'pink', marginHorizontal: 20, }, text: { fontSize: 42, }, }); export default App [3]より引用。 showsVerticalScrollIndicator このpropsをfalseにすることでスクロールバーを表示しないように出来ます。 horizontal このpropsをtrueにすることで横スクロールにも対応できます。 FlatlListとの違い ScrollViewでは全ての子コンポーネントを一度にレンダリングするため、パフォーマンス上の問題があります。FlatListではアイテムが表示されようとしているときに遅延的にレンダリングし、画面外に大きくスクロールするアイテムを削除することでメモリや処理時間の節約が出来ます。FlatListはほかにもアイテム間の距離、複数列、無限スクロールローディングなどの機能をサポートしているので便利です。 ここまで書いていて思いましたが、もしかして割とScrollViewって要らない子?状況に応じて使い分けるんですかね。 StyleSheet ここまででも使用してきていますが、cssのスタイルを使用するために利用しています。特に言うことはないのですが、React NativeはTailwind CSSとは相性が良くなさそうだなーと思います。調べた感じライブラリなどを使えばいけるようですが、vscodeのインテリセンスなどの関係上どうなのかなーという感じです。 逆にCSS in JSは特に変化なく使えそうだなと思います。webとほとんど変わらず記述出来るので、開発コストを削減するという観点から見たら、React NativeではCSS in JSを用いるのがいいんじゃないかなと思います。僕はstyled-componentsを用いて書いているので苦労することはなさそうです。 Button ボタンに相当するコンポーネントです。見たほうが早いと思うので下記に例を載せます。 <Button title="Press me" disabled onPress={() => Alert.alert('Cannot press this one')} /> [3]より引用。 propsとしてonPressとtitleは必須のようです。titleでボタンの表示を決めるようですが、childrenでは無理ということでしょうか。Alert.alertでwindow.alertのようなことが出来るようです。またこの例ではdisabledにしているので利用できないような見た目になります。動的に決定したい場合はdisabledのところにboolean型の変数を入れれば可能でしょう。 Switch このSwitchはswitch文のことではなく、切り替えるためのボタンのことです(名前あるのかな)。リファレンスのダークモード切り替えの部分によくあるやつです。下記に例を示します。 <Switch trackColor={{ false: "#767577", true: "#81b0ff" }} thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"} ios_backgroundColor="#3e3e3e" onValueChange={toggleSwitch} value={isEnabled} /> [3]より引用 タップすることでboolean型変数がトグルし、valueの値によって色々変わるという感じです。 TouchableOpacity 押されるとラップされたViewの不透明度が減少し、薄暗くなります。 <TouchableOpacity style={styles.button} onPress={onPress} > <Text>Press Here</Text> </TouchableOpacity> [3]より引用 FlatList 先程も説明しましたが、リスト表示をする際に利用します。 data 表示したい配列データ。 renderItem dataを使ってどのようなものを表示するかを決める関数 extraData 追加データ。下の例では再レンダリングするためにstateを渡しています。 keyExtractor デフォルトのkeyプロパティの代わりに使用するidの指定。 ・コンテンツがレンダリング領域の外にいったら内部状態は保持されない FlatListの仕様上レンダリングの外のコンテンツの内部状態は保存されません。そのためstateの更新時とレンダリングについて気をつける必要があります ・コンテンツは画面外で非同期にレンダリングされる この特徴によりコンテンツ外のレンダリングよりも早くスクロールすると、一時的に空白のコンテンツが表示される可能性があります。 import React, { useState } from "react"; import { FlatList, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity } from "react-native"; const DATA = [ { id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba", title: "First Item", }, { id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63", title: "Second Item", }, { id: "58694a0f-3da1-471f-bd96-145571e29d72", title: "Third Item", }, ]; const Item = ({ item, onPress, backgroundColor, textColor }) => ( <TouchableOpacity onPress={onPress} style={[styles.item, backgroundColor]}> <Text style={[styles.title, textColor]}>{item.title}</Text> </TouchableOpacity> ); const App = () => { const [selectedId, setSelectedId] = useState(null); const renderItem = ({ item }) => { const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff"; const color = item.id === selectedId ? 'white' : 'black'; return ( <Item item={item} onPress={() => setSelectedId(item.id)} backgroundColor={{ backgroundColor }} textColor={{ color }} /> ); }; return ( <SafeAreaView style={styles.container}> <FlatList data={DATA} renderItem={renderItem} keyExtractor={(item) => item.id} extraData={selectedId} /> </SafeAreaView> ); }; ・・・ [3]より引用 map関数によるレンダリングの代わりなのかなーとおもっています。 Section List FLatListに見出しがついたような感じのものです。ほとんど同じなので使い方は下のサンプルプログラムを見てください。 import React from "react"; import { StyleSheet, Text, View, SafeAreaView, SectionList, StatusBar } from "react-native"; const DATA = [ { title: "Main dishes", data: ["Pizza", "Burger", "Risotto"] }, { title: "Sides", data: ["French Fries", "Onion Rings", "Fried Shrimps"] }, { title: "Drinks", data: ["Water", "Coke", "Beer"] }, { title: "Desserts", data: ["Cheese Cake", "Ice Cream"] } ]; const Item = ({ title }) => ( <View style={styles.item}> <Text style={styles.title}>{title}</Text> </View> ); const App = () => ( <SafeAreaView style={styles.container}> <SectionList sections={DATA} keyExtractor={(item, index) => item + index} renderItem={({ item }) => <Item title={item} />} renderSectionHeader={({ section: { title } }) => ( <Text style={styles.header}>{title}</Text> )} /> </SafeAreaView> ); ・・・ [3]より引用。 おわりに ざっと概念と文法を見てきましたが、理解すれば簡単に使えそうです。ただ、想像していたよりは覚えることがあったのであとは実際に開発してから使用感を確かめていきたいと思います。 参考文献 [1]:Introduction [2]:Ubuntu20.04LTSでReact NativeとExpoを試す [3]:Core Components and APIs
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】iOSでウィンドウからSafeAreaを取得するプラグインを作る

iOS 15でデバイスを回転させるとScreen.safeAreaが正しい値を返さないことがフォーラムで話題になっています。 Unity SafeArea is inconsistent between different starting rotations Screen.safeAreaが正しい値を返さないのは何年も前からある現象で、UnityとしてはもうFixしたと言ってみたり、また再発したりを繰り返しています。 原因はUIViewのsafeAreaInsetsが正しい値を返さないことにあり、UIWindowのsafeAreaInsetsだと正しかったりします。 どうもウィンドウからビューにイベントの伝達がうまくいってないっぽく、これはUnityの責任というより、Appleの責任ではないかとも思われます。 UnityでiOSビルドしたプロジェクトを探すと、UnityView.mmというファイルのComputeSafeArea(UIView* view)という関数でsafeAreaを取得しているようですが、毎回ここを書き換えるのも面倒です。 なのでフォーラムの投稿にもありますが、UIWindowからsafeAreaを取得するネイティブプラグインを作って対処することを考えます。 iOSプラグイン extern "C" { char* convertNSStringToCString(const NSString* nsString); char* GetWindowSafeArea(); } char* convertNSStringToCString(const NSString* nsString) { if (nsString == NULL) return NULL; const char* nsStringUtf8 = [nsString UTF8String]; char* cString = (char*)malloc(strlen(nsStringUtf8) + 1); strcpy(cString, nsStringUtf8); return cString; } char* GetWindowSafeArea() { UIEdgeInsets insets = UIEdgeInsetsZero; UIWindow *window = UnityGetMainWindow(); CGRect bounds = window.bounds; CGFloat scale = window.screen.scale; if (@available(iOS 11.0, *)) { insets = window.safeAreaInsets; } // 上下逆の座標系 CGFloat x = insets.left * scale; CGFloat y = insets.bottom * scale; CGFloat w = (bounds.size.width - insets.left - insets.right) * scale; CGFloat h = (bounds.size.height - insets.top - insets.bottom) * scale; CGRect safeArea = CGRectMake(x, y, w, h); NSString *stringData = [NSString stringWithFormat:@"%f/%f/%f/%f", safeArea.origin.x, safeArea.origin.y, safeArea.size.width, safeArea.size.height]; char* data = convertNSStringToCString(stringData); return data; } Unity側の読み込み using UnityEngine; using System.Runtime.InteropServices; public class UtilityPlugin : MonoBehaviour { #if UNITY_EDITOR #elif UNITY_IPHONE [DllImport("__Internal")] private static extern string GetWindowSafeArea(); #elif UNITY_ANDROID #else #endif public static Rect GetSafeArea() { Rect rect = Screen.safeArea; #if UNITY_EDITOR #elif UNITY_IPHONE string data = GetWindowSafeArea(); if (data != null) { string[] rectArray = data.Split('/'); if (rectArray.Length >= 4) { float x = float.Parse(rectArray[0]); float y = float.Parse(rectArray[1]); float w = float.Parse(rectArray[2]); float h = float.Parse(rectArray[3]); rect = new Rect(x, y, w, h); } } #elif UNITY_ANDROID #endif return rect; } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RealityKitで顔と重なったオブジェクトを表示する(FaceMeshを非表示にする)

RealityKitでFaceAnchorを使うと、顔より後ろのARオブジェクトが透過されます。 この深度オクルージョンをオフにするには、 arView.renderOptions.insert(.disableFaceOcclusions) // iOS15からは.disableFaceMesh ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLやARKitを使ったアプリを作っています。 機械学習/AR関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む