20220119のiOSに関する記事は6件です。

【Swift】CoreBluetoothの使い方 その2:セントラルの実装例

はじめに Corebluetoothのセントラル側の実装例について記載していきます。 ①その1:BLEにおける役割 ②その2:セントラルの実装 ← 今回はここ 処理の順番 ・必要な機能の初期化 ・スキャン処理 ・接続処理 ・ペリフェラルの情報を取得して通信を行う。 ・切断 上記順番に処理を行いますので、順番に記載していきます。 必要な機能の初期化 ・BLE関連の設定の追記 info.plistに「Privacy - Bluetooth Always Usage Description」を追加 試すだけであれば上記だけで問題ありませんが、実際にアプリに組み込み、Appstoreconnectに上げる場合はペリフェラル側の「Privacy - Bluetooth Peripheral Usage Description」も追記が必要です。一見アップロードがうまく言ったように振る舞いますが、メールで怒られます。 ・フレームワークのimport import CoreBluetooth ・必要な変数の宣言 //セントラルの振る舞いは全てcentralManagerで操作します private var centralManager:CBCentralManager! //接続したペリフェラルを保持するために使います private var cbPeripheral:CBPeripheral? = nil //以下は今回のサンプルで便宜上使うものです。信号の送信・読み出しに使います。 //必要に応じて追加・変更が必要です。 private var writeCharacteristic: CBCharacteristic? = nil private var readCharacteristic: CBCharacteristic? = nil ・centralManagerの初期化 //①セントラルマネージャーの初期化 private func bleInit() { //セントラルマネージャーを初期化:初期化した時点でPermissionの許諾のpopupが出て、Bluetoothの電源がONになる。 centralManager = CBCentralManager(delegate: self, queue: nil) } 初期化を実施するとBluetooth機能がONになり、 CBCentralManagerDelegateの下記Delegateメソッドが呼ばれます。 ・centralManagerDidUpdateState //BLEの状態が変化する毎に呼ばれるメソッド func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { //②:セントラル側BLEの電源ONを待つ //BLEが使用可能な状態:電源がONになっている case CBManagerState.poweredOn: print("Bluetooth PowerON") break //BLEが使用出来ない状態:電源がONになっていない case CBManagerState.poweredOff: print("Bluetooth PoweredOff") break //BLEが使用出来ない状態:リセット中 case CBManagerState.resetting: print("Bluetooth resetting") break //BLEが使用出来ない状態:Permissionの許諾が得られていない case CBManagerState.unauthorized: print("Bluetooth unauthorized") break //BLEが使用出来ない状態:不明な場外 case CBManagerState.unknown: print("Bluetooth unknown") break //BLEが使用出来ない状態:BLEをサポートしていない case CBManagerState.unsupported: print("Bluetooth unsupported") break } } 「CBManagerState.poweredOn」を待ってください。 これで、BLEの機能が使用可能になります。 スキャン処理 ・スキャンの実行 //③Peripheralのスキャン private func scan(){ //Bluetoothの状態がONになっていることを確認 if centralManager.state == .poweredOn{ //Service指定せず周囲の全てのPeripheralをスキャンする centralManager.scanForPeripherals(withServices: nil, options: nil) //Serviceを指定してスキャンをする //デリゲートメソッドが指定のサービス以外で呼ばれなく、効率的であるため、こちらがベストプラクティス //let services: [CBUUID] = [CBUUID(string: "サービスのUUID")] //centralManager.scanForPeripherals(withServices: services, options: nil) //タイマーなど設けずスキャンをしているが、この場合は所望のペリフェラルが見つかるまでスキャンし続けてしまうので、 //実際に使う場合はタイマーでスキャンを停止する、スキャン停止ボタンを設けるなどの配慮が必要 // scanTimer = Timer.scheduledTimer(timeInterval: TimeInterval(10), // target: self, // selector: #selector(self.timeOutScanning), // userInfo: nil, // repeats: false) // ///スキャンタイムアウト // @objc func timeOutScanning() { // centralManager.stopScan() // } } } ●注意点 ・コード内にも記載していますが、serviceを指定せずにスキャンを行うと周囲の全てのBLE機器をスキャンしてしまい、そのたびに以降記載するDelegateメソッド(didDiscover peripheral)が呼ばれてしまいます。 なので、serviceを指定してスキャンを実施するのがベストプラクティスです。 ・スキャン処理はcentralManagerのstopScan()を実行するまでずっと継続されます。そのため、タイマーなどで止められる手段を設けておく方がよいです。 接続処理 ・アドバタイズ信号の受信 ※実際はアドバタイズを拾ってスキャンリクエストを実施してスキャンレスポンスが返ってきたら呼ばれます。ここはOSが勝手に行います スキャン処理を行うと、BLE機器が見つかる毎にCBCentralManagerDelegateの下記Delegateメソッドが呼ばれます。 ※service指定した場合は指定したserviceをアドバタイズしている機器が見つかった時だけ呼ばれます。 //④スキャンでPeripheralが見つかる毎に呼ばれるメソッド func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { //peripheralのローカルネーム print("name:\(peripheral.name)") //advertiseの中身 print("advertisementData:\(advertisementData)") //advertiseに入っているServiceUUID print("advertisementServiceUUID:\(advertisementData["kCBAdvDataServiceUUIDs"])") //advertiseの電波強度(RSSI) print("rssi:\(RSSI.stringValue)") //名称フィルターして接続する場合 if peripheral.name == "接続したいperipheralの名称"{ //見つけたペリフェラルを保持 self.cbPeripheral = peripheral central.connect(peripheral, options: nil) //スキャン停止 centralManager.stopScan() } //アドバタイズに入っているService UUIDでフィルターして接続する場合 //※実シーンではscanの時点でadvertisementDataを指定することでフィルターをかけるので使用シーンはほとんど無いと思われる // let SERVICE_UUID:CBUUID = CBUUID(string: "接続したい機器がアドバタイズに乗っけているServiceUUID") // if advertisementData["kCBAdvDataServiceUUIDs"] != nil { // let UUID:[CBUUID] = advertisementData["kCBAdvDataServiceUUIDs"] as! [CBUUID] // //アドバタイズに入っているUUIDは一つだけのため // if UUID.first == SERVICE_UUID{ // central.connect(peripheral, options: nil) // } // //スキャン停止 // centralManager.stopScan() // } } 上記Delegateメソッドの中で接続したい機器を見つけて接続を行います。 ※機器一覧を取得して画面に表示させ、後でユーザーの操作で接続を行いたければ、上記メソッドの中で取得した「peripheral」を配列に保存してtableViewなどで表示すればよいです。 BLE的にはこの時点で接続されています。 ・接続完了後以下Delegateメソッドが呼ばれます。 //⑤接続が成功したときに呼ばれるデリゲートメソッド func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { //ペリフェラルのデリゲートをセット peripheral.delegate = self // 指定のサービスを探す:機器は複数のサービスを持っているので使いたいサービスのみ探すべきである // let services: [CBUUID] = [CBUUID(string: "サービスのUUID")] // peripheral.discoverServices(services) //サービスを指定せずにペリフェラルの全てのサービスを探す peripheral.discoverServices(nil) //サービスが見つかるとCBPeripheralのデリゲートメソッドが呼ばれる。 } //⑤'接続が失敗したときに呼ばれるデリゲートメソッド func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { print("接続失敗") } ペリフェラルの情報を取得して通信を行う。 接続完了時のコードに記載している以下のコードでまずServiceを探します。 //サービスを指定せずにペリフェラルの全てのサービスを探す peripheral.discoverServices(nil) また、接続が完了した時点でperipheralのdelegateをセットしています。 peripheral.delegate = self 以降はCBPeripheralDelegateのDelegateメソッドが呼ばれます。 ・サービスが見つかった時に呼ばれdelegateメソッド //⑥ ⑤で探したサービスが見つかった時に呼ばれるデリゲートメソッド func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { //キャラクタリスティック検索開始 指定して検索。以下は一つしか指定していないが配列なので複数指定も可能 //機器は複数の機能を持っていることがほとんどなので欲しいキャラクタリスティックのみを検索するのが効率的 //let charcteristicUUID:CBUUID = CBUUID(string: "検索したいUUID") //peripheral.discoverCharacteristics([charcteristicUUID], // for: (peripheral.services?.first)!) //全てのサービスのキャラクタリスティックの検索 for service in peripheral.services! { peripheral.discoverCharacteristics(nil, for: service) } } この中でCharacteristicの検索を行います。 peripheral.discoverCharacteristics(nil, for: service) ・キャラクタリスティックが見つかった時に呼ばれるdelegateメソッド //⑦ ⑥で探したキャラクタリスティックが見つかった時に呼ばれるデリゲートメソッド func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { for characteristic in service.characteristics!{ if characteristic.uuid.uuidString == "属性がNotify or indicateのキャラクタリスティックのUUID" { //Notificationを受け取るハンドラ peripheral.setNotifyValue(true, for: characteristic) } if characteristic.uuid.uuidString == "属性がWriteのキャラクタリスティックのUUID" { writeCharacteristic = characteristic } if characteristic.uuid.uuidString == "属性がreadのキャラクタリスティックのUUID"{ readCharacteristic = characteristic } //なおcharacteristicの属性は以下で取得可能 //characteristic.propertie //「.indicate .notify .read .write .writeWithoutResponse」で属性の判別が可能 print("発見したキャラクタリスティック",characteristic.uuid.uuidString) } } 本メソッド内で見つけたキャラクタリスティックは信号の送信・読み出しで使うので保持しておきます。 Notifyとindicateのキャラクタリスティックに関してはsetNotifyValueを行うことで、ペリフェラルのディスクリプタに書き込みを行い、以降、ペリフェラルからの信号送信を受けられるように設定しておきます。 ここまでで接続後の準備は完了です。 以降は信号の送受信になります。 ・信号の送信 信号を送信したければ、保持したキャラクタリスティックに対して、centralManagerを通して信号の送信を行います。 //データの送信 private func sendData(_ data:Data){ //データの書き込み:属性がwrite with responseの場合 if let peripheral = self.cbPeripheral,let writeCharacteristic = self.writeCharacteristic{ peripheral.writeValue(data, for: writeCharacteristic, type: CBCharacteristicWriteType.withResponse) } //データの書き込み:属性がwrite without responseの場合 // if let peripheral = self.cbPeripheral,let writeCharacteristic = self.writeCharacteristic{ // peripheral.writeValue(data, for: writeCharacteristic, type: CBCharacteristicWriteType.withoutResponse) // } } 送信が完了すると以下のCBPeripheralDelegateのDelegateメソッドが呼ばれます。 //write実行時に呼ばれる func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("書き込みエラー:",error.localizedDescription) return }else{ print("書き込み成功:",characteristic.uuid) } } ・信号の受信 以下のように読み出しを行う //データの読み出し private func readData(){ if let peripheral = self.cbPeripheral,let readCharacteristic = self.readCharacteristic{ peripheral.readValue(for: readCharacteristic) } } 上記で読み出しを行う、もしくはペリフェラルからnotify or indicateで信号が送信された場合、以下のメソッドが呼ばれます。 //Notify or indicate or Read時に呼ばれる func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { print("送信元のCharacteristic:",characteristic.uuid.uuidString) if let error = error { print("情報受信失敗...error:",error.localizedDescription) } else { print("受信成功") let receivedData = String(bytes: characteristic.value!, encoding: String.Encoding.ascii) print("受信データ",receivedData) } } 切断を行う cancelPeripheralConnectionで切断を行う。 //切断処理 private func disconnectPeripheral(){ if let peripheral = cbPeripheral { centralManager.cancelPeripheralConnection(peripheral) } } さいごに 以上で一連のBLEのセントラル側の実装は終了です。 対抗機が無いとなかなか実感しにくいので、次はペリフェラル側の実装を行い、実際に通信させます。 qiita初心者ですがqiitaの投稿って結構大変ですね。。。 至らない点、間違っている点などあれば、是非ご指摘頂ければ幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】CoreBluetoothの使い方 その1:BLEにおける役割

はじめに 2022年はOutPutしていくぞ!!ということで、 初めてQiitaに投稿しますが、iOSでのBLEの使い方、CoreBluetooth周りの投稿をしていきます。 ①その1:BLEにおける役割 ← 今回はここ ②その2:セントラルの実装例 CoreBluetoothとは? Core Bluetoothフレームワークは、アプリがBluetooth搭載の低エネルギー(LE)および基本レート/拡張データレート(BR/EDR)ワイヤレステクノロジーと通信するために必要なクラスを提供します。 Apple Developerより https://developer.apple.com/documentation/corebluetooth iOSでBLE(Bluetooth Low Energy)を使う時に使用するフレームワークです。 iOSでBLEを使う時は基本的にCoreBluetoothを使うことになります。 BLEにおける役割とは? CoreBluetoothの前に、 BLEを使う上で知っておくべき前提条件として、2つの役割があります。 Central(セントラル) マスターとも呼ばれ、機能を使う側に当たります。クライアントサイドとなります。スマホアプリなどではスマホがセントラルになることが多いです。機器の機能を使う側ですので。 Peripheral(ペリフェラル) スレーブとも呼ばれ、機能を提供する側に当たります。サーバーサイドとなります。 BLE機器は一般的にはこちらになります。イヤホンや温湿度などの環境センサーなど、機能を提供する機器側ですね。スマホをペリフェラルにすることも当然可能です。 BLE通信の一連の流れ ペリフェラルが電波を発信する(アドバタイズ)。 セントラルは電波をスキャンしアドバタイズ信号を受信する。 アドバタイズ信号を受け取ったセントラルはペリフェラルにどのような機能があるのか更に知りたいのでスキャンリクエストを投げる。※アドバタイズに乗せられる情報は限られているので。 ペリフェラルはスキャンリクエストに応じてスキャンレスポンスを投げる セントラルはスキャンレスポンスの中身を確認し、所望の機器であるならば、セントラルはペリフェラルに接続要求を投げ、接続を行う。 接続後、セントラルはペリフェラルの機能の走査を行い機能の全てを把握し、ペリフェラルを使用する。 使用が終わったら切断を行う。 ペアリングなどのセキュリティ関連は一旦置いておき、一連の流れは上記となります。 次は? 次は上記一連の流れにおけるセントラル側の実装に関して記述していきます。 ②その2:セントラルの実装例
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS & iPadOS 15 リリースノート日本語訳

iOS & iPadOS 15 リリースノート 新機能を利用し、APIの変更をテストするためにアプリをアップデートしてください。 概要 iOS & iPadOS 15 SDKは、iOS & iPadOS 15を実行するiPhone、iPad、及びiPod touchデバイス用のアプリを開発するためのサポートを提供します。 SDKはMac AppStoreから入手できるXcode 13にバンドルされています。 Xcode 13の互換性要件についてはXcode 13リリースノートを参照して下さい。 App Store 新機能 StoreKit 2はSwiftの同時実行性などの新しい言語機能を利用する最新のSwiftベースのAPIを導入しています。このAPIを使用して、製品情報の読み込み、ストアでのアプリ内購入の表示、顧客による購入の許可、コンテンツとサブスクリプションへのアクセスの管理、およびAppStoreによって署名されたトランザクション情報をJSONWeb署名(JWS)形式で受信します。(66587964) Productのrequest(with:)メソッドはproducts(for:)メソッドへと名称変更されました。 Product.SubscriptionInfo.StatusとStorefrontのTransactionリスナータイプのプロパティが、それぞれupdatesとupdatesになりました。 ネストされたAsyncSequence準拠構造は、Transaction.Transactions、Product.SubscriptionInfo.Status.Statuses、およびStorefront.Storefrontsになりました。 TransactionSequenceとTransactionListenerの両方がTransaction.Transactionsになりました。 (79034347) StoreKitError.userDidNotAuthenticateは使用できなくなりました; 代わりにStoreKitError.userCancelledを使用してください。(78270199) カスタムデコード用の生のJSONデータにアクセスできるようになりました。 複数のProduct.PurchaseOptionメソッドが許可されるようになりました。.custom(_ :)は、custom(key:stringValue:)、custom(key:numberValue:)、custom(key:boolValue:)、及びcustom(key:dataValue:)といういくつかの新しいタイプのメソッドに置き換えられました。 Product、Transaction、及びrenewalInfoの添字演算子は、ネストされたキー列挙とともに削除されました。 BackingValue及びBackingValueから初期化子を追加する拡張機能は削除されました。(79101606) 新しいタイプのプロパティunfinishedがTransactionで利用できるようになりました。これは、アプリが引き続きコンテンツをユーザーに配信する必要があるトランザクションの署名付き情報を返します。 (79620896) 新しいonStorefrontChange(shouldContinuePurchase:)がStoreKit 2で利用可能になりました。この購入オプションを使用して、トランザクション中にAppStoreストアフロントが変更された場合にトランザクションを続行するかどうかを決定できます。 このオプションが追加されていない場合、デフォルトはtrueです。 (70757789) VerificationResult.unverified(SignedType)はVerificationResult.unverified(SignedType、VerificationError)になり、未検証の符号付き値の理由を提供します。 jsonRepresentationはTransactionで利用でき、jsonRepresentationはrenewalInfoで利用できます。 どちらのプロパティも、ペイロードJSONをデータとして提供します。 符号付きの値にアクセスするのに便利なように、payloadValueプロパティとunsafePayloadValueプロパティをVerificationResultで使用できます。 (80701792) 解決された問題 サンドボックス環境で実行された購入がVerificationResult.unverified(_ :_ :)を返す問題を修正しました。 (71949674) 既知の問題 unfinishedプロパティが、すでに終了したトランザクションに対してVerificationResult<Transaction>を返す場合があります。 (81346114) Audio Units 新機能 Audio Unitsは、Audio UnitホストがiOSで表示できるカスタムビューを提供するようになりました。 AUAudioUnitにユーザーインターフェイスがあるかどうかを判断するには、providesUserInterfaceプロパティを使用します。 requestViewController(completionHandler:)メソッドを使用して、viewのAUViewControllerを取得してください。 カスタムビューは、tintColorプロパティを介したビューの色合いの設定をサポートしています。 これを使用して、ビューの色をトラックごとに異なる色に設定したり、アプリの外観に一致させたりすることができます。 (74183251) AVFoundation 新機能 iPadOSアプリは、複数のウィンドウを表示し、画面上の唯一のアプリケーションである間も、引き続きカメラを使用できるようになりました。 非推奨 supportPhotoPixelFormatTypes(for:)およびsupportedRawPhotoPixelFormatTypes(for:)は、Swiftで[NSNumber]ではなく[OSType]を返すようになりました。 (64822071) RecommendedVideoSettings(forVideoCodecType:assetWriterOutputFileType:)は、Objective-Cではnullable NSDictionary *の代わりにnullable NSDictionaryを、Swiftでは[AnyHashable:Any]?の代わりに[String: Any]?を返すようになりました。(33784279) cgImageRepresentation()およびpreviewCGImageRepresentation()はUnmanaged<CGImage>?の代わりにCGImage?を返すようになりました。(44734827) recommendedAudioSettingsForAssetWriter(writingTo:)は、Objective-Cではnullable NSDictionary *の代わりにnullable NSDictionaryを、Swiftでは [Any Hashable: Any]?の代わりに[String: Any]?が返すようになりました。(50450334) Core Haptics 新機能 CHHapticEventTypeAudioContinuous、CHHapticEventTypeHapticContinuous、およびCHHapticEventTypeAudioCustomのtypeのイベントは、一時停止したCHHapticAdvancedPatternPlayerが再開した場合、イベントの途中で再生を再開するようになりました。 seek(toOffset:)が特定の時間オフセットでプレーヤーを開始した場合、これらのイベントはイベントの途中で開始されません。 (29274583) タイプリソースにボリュームエンベロープを適用するかどうかを制御できるようになりました。デフォルトでは、これらのリソースは、クリックを回避するために、最初に信号をランプインし最後にランプアウトするビルトインボリュームエンベロープで再生されます。 (75491090)ボリュームエンベロープは、次のいずれかの方法で適用できます。 カスタムオーディオアセットのオーディオリソースIDを登録してインポートする場合は、システムがregisterAudioResource(_:options :)に渡す新しいキー値引数CHHapticAudioResourceKeyUseVolumeEnvelopeを介してこの動作を指定できます。 AHAPファイルまたはCHHapticPatternのinitWithDictionary:error:を使用してオーディオアセットを参照している場合は、CHHapticPatternKeyEventWaveformUseVolumeEnvelopeパターンキーを使用してこの動作を制御できます。 Core ML 既知の問題 automatic reference counting(ARC)モードでは、.dataPointerプロパティを使用すると、コンパイラがMLMultiArrayのライフタイムを予想よりも長くする場合があります。 これにより、メモリ使用量が増える可能性があります。 (80895213)回避策:.dataPointerアクセスを@autoreleasepool {...}ブロックで囲みます。 Create ML 新機能 Create MLフレームワークがiOS & iPadOS15で利用できるようになり、デバイス上のMLを活用するダイナミックなアプリ体験を構築するための新しい機会が得られるようになりました。 画像分類、音声分類、テキスト分類、手の形と手の動きの分類のためのタスクフォーカス型のAPIが、古典的な表形式の分類と回帰のためのAPIとともに利用できます。(37087332) Audio Feature Print-based MLSoundClassifierアルゴリズムは、サウンド分類モデルをより高速に、より精密に、より低いレイテンシーで、より小さなモデルサイズでトレーニングします。 このアルゴリズムは、Create MLのMLSoundClassifierのデフォルトオプションになりました。(70106630) Debugging 既知の問題 macOS 11を実行しているAppleシリコンを搭載したMacのデバイスシミュレータで実行されているiOSアプリでディスパッチセマフォを使用すると、アプリがクラッシュします。(81783378)回避策:Xcodeで、[製品]> [スキーム]> [スキームの編集]を選択し、[実行]> [オプション]> [キューのデバッグ]> [バックトレース記録を有効にする]の選択を解除します。 Find My 既知の問題 iOSデバイスを充電する必要がある場合、Find Myネットワークがアクティブであることを示すテキストは、デバイスの言語が英語に設定されている場合にのみ表示されます。(78547946) Guided Access 既知の問題 VoiceOverでガイドアクセスを使用する場合、ガイドアクセスを終了するためのガイドアクセスパスコードを入力できない場合があります。(79370792)回避策:デバイスのパスコードが設定されている場合は、デバイスを強制的に再起動してガイド付きアクセスを終了します。 Home 既知の問題 Threadを使用するMatterアクセサリとペアリングすることはできません。(80991829) アクセサリがすでに別のアプリとペアリングされている場合、アプリのペアリングフローを通じてサードパーティのアプリをMatterアクセサリとペアリングすることはできません。(80059432)回避策:他のアプリからアクセサリのペアリングを削除してから、サードパーティのアプリをペアリングします。 Apple Homeを作成していない場合、Matterアクセサリを使用してサードパーティアプリにフローを追加することはできません。(80058744)回避策:フローを追加する前に、Homeアプリを起動してHomeを作成します。 Apple TVがWi-Fi経由で接続されている間は、Matterアクセサリにアクセスできません。(79582629)回避策:イーサネット経由でAppleTVを接続します。 Matterアクセサリは、ペアリング後に無応答状態になる場合があります。(76019163)回避策:アクセサリをHomeから削除し、アクセサリをリセットして、Homeに追加し直します。問題が解決しない場合は、HomeハブをHomeから削除して再度追加します。それでも問題が解決しない場合は、Homeを削除して新しいHomeを作成します。 Matterアクセサリとの最初のペアリングの試行には、予想外に長い時間がかかり、最終的に失敗する可能性があります。(77967587)回避策:アクセサリのペアリングを再試行してください。 1つのHomeでペアリングできるMatterアクセサリは最大5つまでです。(77967671) Matterアクセサリをペアリングできるのは、招待されたユーザーではなく、Homeの所有者だけです。(76012945) Home Screen 既知の問題 ウィジェットギャラリーでの検索をキャンセルした後、キャンセルボタンは表示されたままになり、ウィジェットギャラリーが空白になる場合があります。(78572049)回避策:ウィジェットギャラリーを閉じて再度開きます。 iCoud 新機能 iCloud Private Relayは、追加のフィードバックを収集し、Webサイトの互換性を向上させるために、パブリックベータとしてリリースされます。(82150385) 既知の問題 Legacy ContactsはiOSおよびiPadOS15 beta 5から削除され、将来のリリースで復活する予定です。 (81292885) 「+」や「-」などの区切り文字を含むカスタムメールドメインアドレスは設定できません。 (82425376) 別のiTunesアカウントに関連付けられているカスタムメールドメインアドレスは設定できません。 (82358431) 一部のアカウントは、カスタムメールドメインの対象とならない場合があります。 (82421769) Foundation 新機能 Foundationには、自動文法一致エンジンが含まれるようになりました。 これにより、ローカライズされた文字列が自動的に活用されて、複数形、文法的な性別の一致、およびユーザーの住所の用語との一致が考慮されるため、コードが簡素化され、提供するローカライズされた文字列の数が減ります。 英語とスペイン語で利用できます。 (70210115) フォーマットAPIが利用可能になりました。これは、フォーマットに重点を置き、フォーマッターインスタンスを作成、構成、およびキャッシュする必要をなくします。 各Formatterタイプには、フォーマットされた関数があります。 これらの関数には、スタイルの設定とカスタマイズを可能にする引数があります。 (70220307) JSONSerializationとJSONDecoderは、JSON5からのデコードをサポートするようになりました。 (73954652) SortDescriptor、KeyPathComparator、およびSortComparator APIは、値を並べ替えるためのアーカイブ可能なルールを表現するためのSwiftインターフェイスを提供します。 (74264359) Logging 新機能 Swiftのos_signpost(_:dso:log:name:signpostID:)は、すべてのプラットフォームのフレームワークOSの一部です: サブシステムとカテゴリ、既存のOSLogオブジェクト、または既存のLoggerオブジェクトを使用してOSSignposterをインスタンス化します。 OSSignposter APIは、Signpostsを出力するためのメソッドを提供します。 beginInterval(_ )は開始Signpostsを出力し、endInterval(_ :_ :)は終了Signpostsを出力し、emitEvent(_)はイベントSignpostsを出力します。 これらは、Stringとvarargsに基づく既存のos_signpost呼び出しを置き換えます。 APIは、メタデータパラメーターの文字列補間をサポートします。 文字列補間は、Logger APIsで受け入れられるものと同じです。 OSSignposter APIは、すべてのフォーマットおよびプライバシーオプション(以前はos_signpost関数によって提供されていた)をサポートし、Logger APIsと同じ構文に従います。 APIは、従来のAPIよりもパフォーマンスが向上しています。 OSSignposterタイプは、コードのブロックを開始および終了のSignposts、withIntervalSignpost(_ _:around:)で囲むための新しいスコープAPIを提供します。 注: これらのAPIは、iOS14およびiPadOS14以前では使用できません。 ただし、既存のos_signpostAPIは引き続き使用できます。 (54756831) Maps 既知の問題 丸みを帯びた建物の角が消える場合があります。 (80468151) 非推奨 MKPinAnnotationViewとMapPinは、このベータ版では非推奨としてマークされています。 (78536295) Networking 新機能 URLSessionが送信するデフォルトのAccept-Languageヘッダーの形式が更新され、複数のロケールの値が修正されています。 優先言語に加えて、ヘッダーには、優先言語と異なる場合のフォールバックとして現在のシステム言語も含まれます。 この動作は、macOS 12、iOS 15、tvOS 15、およびwatchOS 8SDKに対してリンクするアプリに影響します。 (38772422) URLSessionに非同期関数が含まれるようになりました。 (68890254) For example, a one-shot fetch: let (data, response) = try await URLSession.shared.data(from: URL(string: "https://www.apple.com")!) if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { // Use data. } And support for an AsyncSequence stream of bytes: let (bytes, response) = try await URLSession.shared.bytes(with: URL(string: "https://www.apple.com")!) if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { for try await line in bytes.lines() { // Parse line. } } 非推奨 プロキシ自動設定(PAC)のクリアテキストHTTP URLスキームのサポートは非推奨になりました。 PACにはHTTPS URLスキームのみを使用してください。 これは、設定、システム環境設定、プロファイル、およびconnectionProxyDictionaryやCFNetworkExecuteProxyAutoConfigurationURL(_ :_ :_ :_ :)のようなURLSession APIなどを介して設定されたすべてのPAC構成に影響します。 クリアテキストのHTTP PAC URLを構成すると、PACファイルのロード中にシステムがHTTPSにアップグレードする場合があります。 DNSを介したWebProxy Auto-Discovery(WPAD)プロトコルは影響を受けません。 Dynamic Host Configuration Protocol(DHCP)Option 252 WPADは、PACファイルのロード中にクリアテキストHTTP URLをHTTPSにアップグレードしようとする場合があります。 (61981845) Privacy 新機能 App Privacy Reportのアプリコンテンツを表示するファイルをダウンロードするには、[設定]> [プライバシー]> [アプリのアクティビティを記録]を選択します。(77758720) Reality Composer 既知の問題 Reality Composerで新しいプロジェクトを作成できない場合があります。 (79418400)回避策:macOSのReality Composerで新しいプロジェクトを作成し、AirDropまたはMailを介して.rcprojectファイルをデバイスに転送します。 Safari 新機能 下部のタブバーは、ページコンテンツの下に表示されるように再設計されています。 上部にアドレスバーを表示するオプションも利用できます。 (81118141) 既知の問題 iPhoneのSafari Web Extensionポップオーバーの入力フィールドをタップすると、拡張機能UIが上に移動してキーボード用のスペースが確保されない場合があります。 (81676564) SharePlay 非推奨 beta 7および今後のbetaリリースでのSharePlay開発には、更新されたSharePlay Development Profileのインストールが必要です。 このプロファイルにより、iOS 15、iPadOS 15、tvOS 15 beta 7、およびmacOS Monterey beta 6のGroup Activities APIを介してGroupSessionsを正常に作成および受信できます。(81816137) ShazamKit 既知の問題 SHMediaLibraryのデフォルトインスタンスに追加されたメディアアイテムは、Shazamに表示されません。 (77785557)回避策:音楽認識コントロールセンターモジュールを長押しして、SHMediaLibraryのコンテンツを表示します。 Siri 既知の問題 VoiceOverおよびSpoken Contentのユーザーは、最初は利用可能なすべての音声オプションを表示できない場合があります。 音声オプションは、しばらくすると入力されます。 (79463000) デバイス上の音声認識は、中国語(マンダリン - 中国本土)、英語(オーストラリア)、英語(カナダ)、英語(英国)、および英語(米国)のみをサポートします。 (78483609) SKAdNetwork 新機能 開発者が獲得ポストバックを受け取ることを選択した場合、デバイスは、宣伝されたアプリの開発者に獲得ポストバックのコピーを送信できるようになりました。 (75054513) Swift New Features 新しいSwiftの値タイプAttributedStringが、Swift文字列と同じ文字カウント動作で使用できるようになりました。 完全にローカライズ可能で、Markdown、Codable、強く型付けされた属性などのサポートも含まれています。 (27227292) NotificationCenterは、async/awaitを使用して通知を受信するための新しいAsyncSequence APIを含んでいます。 (74401384) for await note in NotificationCenter.default.notifications(named: .MyNote) { // Use note. } 既知の問題 Combineに依存するSwiftライブラリは、armv7およびi386アーキテクチャを含むターゲット用にビルドできない場合があります。 (82183186、82189214)回避策:影響を受けない更新バージョンのライブラリを使用するか(利用可能な場合)、armv7およびi386のサポートを削除します(たとえば、ライブラリの展開ターゲットをiOS 11以降に増やします)。 iOS 15またはmacOS 12 SDKを使用してRealityKitにリンクしているアプリケーションは、以前のOSでは起動できません。 (79584511)回避策:Xcodeプロジェクト設定にOTHER_LD_FLAGS = -weak_framework RealityFoundationを追加して、古いOSでRealityKitアプリを実行できるようにします。 Settings 既知の問題 Sound Actions機能がSwitch Controlの一部として機能している間、設定アプリのPracticeとマークされた領域でサウンドが検出されません。 (82411537) SwiftUI 新機能 LocalizedStringKeyにMarkdown構文を含めることができるようになりました。 LocalizedStringKeyからTextビューを作成すると、文字列リテラルで作成されたTextビューを含め、マークダウン文字列が解析されます。 システムは、Markdown構成に従ってTextのスタイルを設定します。 (74515884) AttributedString構造からTextを作成できます。 Textは、SwiftUI属性スコープ内の属性を介して提供するスタイルを尊重します。 これらのスタイルは、ビュー修飾子を介して提供するスタイルよりも優先されます。 (74841755) 特定の種類のアニメーションがメインスレッドから実行されるようになったため、新しいスレッドセーフ要件があります。 (70524799)次の関数とタイプがスレッドセーフであることを確認してください: AlignmentID、Animatable、EnvironmentKey、EnvironmentValues、Equatable、GeometryEffect、Hashable、Identizable、PreferenceKey、Shape、VectorArithmeticのプロトコルに準拠するタイプのすべてのメソッドとアクセッサー。 以下のタイプと関数に渡すクロージャ。(ただし、それらを作成したビューにObservableObjectタイプへの参照がない場合に限る):ForEach、GeometryReader、backgroundPreferenceValue(_ :_ :)、overlayPreferenceValue(_ :_ :)、transformPreference(_ :_ :)、anchorPreference(key:value:transform:)、transformAnchorPreference(key:value:transform:)、transformEnvironment(_ :transform:)、transaction(_ :) NSFormatterが提供するTextFieldは、ユーザーが入力するとバインディングを更新するようになりました。 NSFormatterは、ユーザーがフィールドを送信したとき、またはフォーカスがフィールドから離れたときに、フィールドのテキストをフォーマットします。 (67899823) DisclosureGroupは、行をタップしたときに展開を切り替えるようになりました。 (62208702) デフォルトのListStyleがinsetGroupedになりました。 (75072988) TextFieldラベルは、フォームのフィールドの横には表示されません。 プロンプトパラメータを使用して、フィールドの明示的なプレースホルダーを指定します。 (61260160) FormatStyleを使用してTextを初期化できるようになりました。 (72159423) 検索中に、searchCompletion(_ :)修飾子を使用する提案をタップすると、選択した単一の提案が表示されるのではなく、提案リストが非表示になります。 (76965399) 以前のtitleパラメーターの代わりにpromptパラメーターを使用して、検索可能な修飾子が構成する検索フィールドのプロンプトをカスタマイズできるようになりました。 (77988967) SwiftUIはtextSelection修飾子をサポートするようになりました。 (77827592) 境界線の付いたボタンの形状を制御するために使用できるbuttonBorderShapeが追加されました。 (79456465) 新しいAttributedString属性underlineStyleとstrikethroughStyleをAttributeScopes.SwiftUIAttributesに追加しました。 (78437803) Animatableプロトコルに準拠し、さらにViewまたはViewModifierプロトコルのいずれかに準拠するタイプは、値が変更されたときにアニメーションを適用するようになりました。 その結果、AnimatableModifierプロトコルはソフト非推奨になりました。 最新のOSバージョンをターゲットにする場合は、Animableを直接使用します:例えば、struct CustomModifier:AnimatableModifierではなく、struct CustomModifier:ViewModifer, Animatableを使用します。 (76971100) contentShape(_ :eoFill:)修飾子により、さまざまな種類のシェイプをきめ細かく制御できるようになりました。 ドラッグプレビュー、ホバーエフェクト、およびコンテキストメニューの場合、iOS 15.0以降でリンクされたときにプレビューの形状に影響を与えるには、一致するContentShapeKindsが必要です。 デフォルトの動作は、interactionの種類を設定することです。 (60792377) openURLの環境値は、LinkビューでのURL処理やTextビューに埋め込まれたリンクなど、ビュー階層でのURL処理をカスタマイズするために設定し使用できるようになりました。 (78551237) Taskは、新しいタスクを生成するとき、使用される優先度を渡すことができるようになりました。 (80599258) 過剰な行の高さの文字を含むTextビューでは、サイズの大きい文字のクリッピングやオーバーラップを回避するために、デフォルトサイズが大きくなりました。 (80665315) isDetailLink(false)を使用するiPadのサイドバーのNavigationLinkは、詳細エリアではなくサイドバーに正しくプッシュするようになりました。 (80919171) 既知の問題 OutlineGroupへのバインディングを提供するには、init(_ :children:content:)のキーパスパラメーターにwrappedValueを含める必要がある場合があり、iOS & iPadOS 14以前では使用できません。 (77890799) FocusStateを使用して新しく追加されたList行にビューをフォーカスするには、メインの実行ループが次に実行されるときにフォーカス状態プロパティの更新を延期する必要があります。 (78607356) Listは、SwiftUIのセーフエリアインセットを尊重しなくなりました。 (82295913) 非推奨 controlProminenceは非推奨になりました。 代わりに、新しい.borderedProminent ButtonStyleを使用してください。 (78908460) 関数(Fn)ショートカット修飾子は非推奨になり、システムで使用するために予約されています。 (78627099) TabularData 新機能 TabularDataは、表形式データの分析と操作に使用する新しいSwiftフレームワークです。 DataFrameを使用して、CSVファイルとJSONファイルを読み取ったり、データを結合、グループ化、集計したりできます。 (69982458) UIKit 新機能 iOS 15 beta SDKに対してコンパイルされたアプリの場合、テキストビューとテキストフィールドに入力するときに、キーコマンドがテキスト入力とテキスト編集コマンドをインターセプトすることはなくなりました。 たとえば、Deleteキーを押すと常に文字が削除され、Deleteキーコマンドが存在する場合もトリガーされません。 キーコマンドがテキスト入力をインターセプトするようにするには、キーコマンドでwantsPriorityOverSystemBehaviorプロパティをtrueに設定します。 これは、矢印やタブキーの押下などのフォーカスキーボードナビゲーションコマンドよりもキーコマンドを優先させるためにも必要です。 (55118263) iOS 14およびiPadOS 14以前では、autocorrectionTypeがUITextAutocorrectionTypeNoに設定されている場合、QuickTypeバーは無効になります。 iOS 15およびiPadOS 15以降に対してリンクされたアプリの場合、QuickTypeバーが有効になり、スペルチェックの候補が表示されます。 新しい動作がユースケースにとって望ましくない場合は、spellCheckingTypeをUITextSpellCheckingTypeNoに設定して、QuickTypeバーを非表示にします。 (68874861) iOS 15 beta SDKを使用してコンパイルすると、いくつかのキーウィンドウ関連のプロパティ、メソッド、および通知の動作が変わります: isKeyWindowは、ウィンドウがアプリではなくシーンのキーである場合にtrueを返します。 becomeKeyWindowは、シーンの中でウィンドウがアプリからキーになったときに呼び出されます。 didBecomeKeyNotificationは、ウィンドウがアプリではなくシーンのキーになったときに通知します。 resignKeyWindowは、ウィンドウがアプリからシーン内のキーウィンドウステータスでなくなったときに呼び出されます。 didResignKeyNotificationは、ウィンドウがアプリではなくシーン内のキーウィンドウステータスでなくなったときに通知します。 (72873846) Xcode 既知の問題 音楽リクエストを含むコンテンツの読み込みなどのMusicKit機能は、シミュレートされたデバイスでは機能しません。 (78559381)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ios申請

概要 flutterで作成したアプリをapp storeに申請 やり方 基本はこちらのサイトを参考にさせていただきました ポイント archiveする前にビルドする必要があるが、dbの設定をしてあげた 提出したら、appleチームが確認するので、専用のログインIDやパスワードも作成しておいてあげるとよい 1 ビルドする 本番環境でアプリをビルドする flutter run --flavor prod -t lib/main-prod.dart 2 archvieする ターゲットをany ios device(arm64)に設定する xcodeの上のメニューバーからproductを選択 > Archiveを選択する 3 validate appの選択 アーカイブしたら、選択してvalidate appする。このときにxcodeのログインセッションがきれていたらエラーするので、 ログインセッションがきれていたら、上部のxcode>preference>accountを確認する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS × MVC】MVC アーキテクチャとは【コードあり コピペでOK】

目的 iOS開発における、MVCアーキテクチャを理解すること 筆者の備忘録 流れ MVCの誕生 MVCの目的 各レイヤーの役割 MVCの課題 (補足)Presentation Modelパターン 画像表示アプリでMVCの実装 ← コードつき, コピペでOK 1. MVCの誕生 ここではMVCが誕生するまでの流れを説明していきます。 コード(:ViewControllerのようなもの) を ウィジェット(:ボタンやテーブル等のUIコンポーネント) に全て記載していた ↓ メンタルモデルと違うものになってしまう ↓ 1979年にUIの表示をEditor、メンタルモデルをModelと名付けて、責務を分けた。 ↓ その後、Editorを「入力に属するもの(Controller)」「出力に属するもの(View)」に分けた。 ↓ View-Controller-Model:MVCの誕生 補足 メンタルモデルとは 人間が無自覚のうちに持っている、思い込みや価値観 (例:画面は画面のプログラム、処理は処理のプログラムがあるはずだ) 2. MVCの目的 開発時に、ドメインとプレゼンテーションを分けて考えること → アーキテクチャ全般に言えること 補足 ドメイン =Model システム本来の関心領域 (例:足し算の処理) プレゼンテーション =View UIに関係するもの (例:足し算の結果を画面に描画) 3. 各レイヤーの役割 ここでは、MVCのModelViewControllerについて、それぞれ役割の説明をしていく レイヤー 役割 Model コマンドを受けて処理を行い、変数を更新 View Modelの変更を監視し、画面の更新 Controller ユーザーの入力を受けて、Modelにコマンドを送る ↑[1]より参考 ControllerとViewによって構成されるウィジェットが、保持するModelにコマンドを送り、その変更をウィジェットに反映するというもの。 Modelは、複数のViewやController 共有される。 4. MVCの課題 MVCモデルの課題としては以下の3点が挙げられます。 ① プレゼンテーションロジックの表現ができない 年齢を表すModelを考える。あるViewでは「未成年なら赤文字、青年なら青文字」という処理が行われる。また、別のViewでは「還暦以上なら緑文字、還暦未満なら黄色文字」という処理。この「年齢で文字色を変更する という処理」はどこで表現すれば良いのか? ② プレゼンテーション(View)状態を保持できない 「表の選択状態」や「全てのフォームを埋めることで変化するボタン」等の、Viewから得られた情報の管理。 ③ テストが難しい ↓①・②の説明 課題の解決策 上で挙げた課題に対して 以下のような解決策 が考えられます。 この3つの解決策の中でも、Presentation Modelは、良い意味で大きな影響を与えました。 継承 プレゼンテーションロジックごとに新しいウィジェットのサブクラスを作る Pluggable Views 様々な画面を詰め込んだViewクラスを用意して、そこに大量の引数を与えて表示する画面の振る舞いを変える Presentation Model View と Model の間にViewの状態を管理する層を追加する (補足)5. Presentation Modelパターン Presentation Modelパターン は、 MVC は異なるアーキテクチャである。 ViewとModelの間に Presentation Model というレイヤーを置く。そこに プレゼンテーションロジックやUIの状態管理を担わせる。 ↑[1]より参考 ↑[1]より参考 Presentation Modelパターン のModelViewPresentation ModelControllerについて、それぞれ役割を以下の表に示す。また、ここではMVCでの役割との比較も行えるよう表を作成した。 レイヤー Presentation Modelパターン MVC Model MVCと同様 コマンドを受けて処理を行い、変数を更新 View Presentation Modelの変更を監視 Modelの変更を監視し、画面の更新 Presentation Model Modelにコマンドを送る・Modelの変更を監視・プレゼンテーションロジックやUIの状態を管理 - Controller Presentation Modelにコマンドを送る ユーザーの入力を受けて、Modelにコマンドを送る 6. 画像表示アプリでMVCの実装 swift で、画像表示アプリを作成しながら、 MVC の説明を行います。 →プログラムはこちら →アプリ内で使用する画像はこちら 作成する画像表示アプリ 準備 ■ ファイル構成 Modle:Model.swift View:Cell.swift Controller:ViewController.swift ■ 画像 ■ Storyboard ・CollectionViewのCell の identifier をCellにしておく プログラム Model Model.swift import Foundation import UIKit // Viewで使用する変数の定義 // 【流れ】 // ①: ViewController.swift(Controller) からの命令で変数の値が更新されていく // ②: 変数が更新されると View が更新される struct Model { let image: UIImage! let imageName: String! } View Cell.swift import UIKit // 画面(storyboard) で使用する UIコンポーネント を定義 // 【流れ】 // ⓪: ViewController.swift が Data.swift からViewで使用するデータを取得する // ①: ViewController.swift で image,label(Cell.swift) の値が更新される → View が Controller を監視している状態 // ②: 画面を再描画 class Cell: UICollectionViewCell { @IBOutlet weak var image: UIImageView! @IBOutlet weak var label: UILabel! } Controller ViewController.swift import UIKit // Model(Model.swift)で定義した変数を使い、View(Cell.swift)を更新する // 【流れ】 // ①: Model(Model.swift) で定義した変数を使用して、Data.swift から画像データを取得する // ②: ①で取得したデータを、View(Cell.swift) へ伝える class ViewController: UIViewController { var dataList = [Model]() override func viewDidLoad() { super.viewDidLoad() // Model 型のデータを受け取る dataList = Data.getAllData() } } // View を生成する, Model 型のデータを使用して extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource { // セルの個数 // 設置必須な関数 (CollectionViewを使用する場合) func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return dataList.count } // セルの作成 // 設置必須な関数 (CollectionViewを使用する場合) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // Cell.swift の UIコンポーネント(image, label)に代入していく // 画像を表示する際に詰まったこと: https://qiita.com/yoshikoba/items/044925f554b75c53d27c let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell cell.image.image = dataList[indexPath.row].image // image の更新 cell.image.contentMode = .scaleAspectFill cell.image.clipsToBounds = true cell.label.text = dataList[indexPath.row].imageName // label の更新 return cell } } 参考文献 [1], iOSアプリ設計パターン入門 (電子版: 税込3240円) [2], Swift4[Part-2] :- Model View Controller(MVC) With Example in iOS Hindi. - YouTube
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ARの使い道が思いつかない人へ ARとはセンシングであるという考え方と、具体例 ARで紙を切り取る

ARの使い道の考え方と、その具体例です。 具体例では、画像を登録して、現実の紙からその部分を切り取ります。 ARってなかなか使い道がわからない ARって楽しそうなものの、イマイチどういうアプリケーションに使えばいいかわからない。 現実のものとどうインタラクトするかが鍵 リアリティを拡張するもの、ということは、ベースとなるリアルの理解が鍵となります。 ARはセンシングである 現実を拡張するためには、現実を理解する必要があります。 ARとは、コンテンツを表示するもの、というのに加えて、 「センシングである」 というところから考えてみます。 ARコンテンツの配置は、あくまで現実を拡張する手段の一つであって、 コンテンツを現実のどこに配置するか、それによって何の現実を拡張しているか、がARの目的を形作るのではないでしょうか。 となると、まずは現実をどのように認識するのか、というセンシングの部分が重要になります。 ARKitはワールドトラッキングやフェイストラッキングから始まり、最近ではLidarによるオブジェクト理解など、 現実をセンサーによって把握する部分が鍵となっています。 現実から考えよう バーチャルコンテンツではなく、まずは理解すべき現実から考えてみましょう。 あなたの周りにはどんな現実のものがありますか? それは、コンピュータによってどのようにセンシングして理解することが可能でしょうか? 最後に、その現実をどのように拡張すれば、面白い効果、便利な効果が得られるでしょうか? 例 画像を登録して、現実の紙からその部分を切り取ります。 私たちの周りには、様々な画像があります。 雑誌の表紙、ポスター、広告。 そこには、多くの人の顔が写っています。 幸い、機械学習のおかげで、画像を認識し、顔を検出することができます。 ここがセンシングの部分です。 顔という現実をARに与え、拡張してみましょう。 拡張は、何も加えることだけではありません。 それを無くす(減らす)ことも拡張と言えます。 把握した顔を、切り取って無くしてみましょう。 まずは顔の把握フェーズです。 Visionで、カメラフレームから顔を検出して、顔だけの画像を作成します。 let request = VNDetectFaceRectanglesRequest() let pixelBuffer = frame.capturedImage let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer) do { try handler.perform([self.qualityRequest]) guard let result = request?.results?.first as? VNFaceObservation else { return } let boundingBox = result.boundingBox let ciImage = CIImage(cvImageBuffer: pixelBuffer) let faceRect = VNImageRectForNormalizedRect((boundingBox),Int(ciImage.extent.size.width), Int(ciImage.extent.size.height)) let croppedImage = ciImage.cropped(to: faceRect) } catch let error { print(error) } 作成した顔の画像をARReferenceImageとして登録します。 これによって、ARKitは顔の画像を認識し、画像アンカーを作成してコンテンツを配置できるようになります。 guard let cgImage = context.createCGImage(croppedImage, from: croppedImage.extent), let imageData = UIImage(cgImage: cgImage).pngData(), let url = try? FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp.png"), ((try? imageData.write(to: url)) != nil) else {isRequesting = false; return} let referenceImage = ARReferenceImage(cgImage, orientation: .up, physicalWidth: 0.1) let config = ARImageTrackingConfiguration() config.trackingImages = [referenceImage] arView.session.run(config, options: [.removeExistingAnchors]) ARKitで登録した顔を認識し、そこに顔画像を貼り付けたボックスを置いて、移動させることで切り抜きに見せます。 func session(_ session: ARSession, didAdd anchors: [ARAnchor]) { for anchor in anchors { guard let imageAnchor = anchor as? ARImageAnchor else {continue} Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { timer in let anchorEntity = AnchorEntity(anchor: imageAnchor) var material = UnlitMaterial(color: .white) let texture = try? TextureResource.load(contentsOf: self.url!) material.baseColor = MaterialColorParameter.texture(texture!) let faceBox = ModelEntity(mesh: .generateBox(size: [0.1,0.02,Float(imageAnchor.referenceImage.physicalSize.height)]), materials: [material]) let croppedBox = ModelEntity(mesh: .generateBox(size: [0.1,0.02,Float(imageAnchor.referenceImage.physicalSize.height)]), materials: [SimpleMaterial(color: .black, isMetallic: true)]) croppedBox.position = [0,-0.01,0] anchorEntity.addChild(croppedBox) anchorEntity.addChild(faceBox) self.arView.scene.addAnchor(anchorEntity) faceBox.move(to: Transform(translation:[0,0,0.3]), relativeTo: faceBox, duration: 3, timingFunction: .easeInOut) } } } 身の回りからはじめよう あなたの周りにあるもので、コンピュータによって把握できるものはなんですか? コンピュータはどのようにそれらのものを認識しますか? そして、あなたはそれをどのように拡張できますか? ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLやARKitを使ったアプリを作っています。 機械学習/AR関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む