20220303のSwiftに関する記事は3件です。

ARKitのフレームを解析するときの注意点

ARKitのCapturedFrameをVisionなどで解析してARインタラクションを作る場合があるが、 この際ARKitSessionDelegateで取得できるフレームとディスプレイに表示されているフレームの領域が違うことがある。(キャプチャされた一部分を表示していることがある) 例えば、iPhone11ではディスプレイが 414x896 だが、キャプチャしたフレームサイズは 1440x1920 である。 ディスプレイ内にフレーム縦幅は全て収まっているが、横は中心部しか収まっていない。 ディスプレイに表示されている領域でフレームをクロップするコード。 func session(_ session: ARSession, didUpdate frame: ARFrame) { let pixelBuffer = frame.capturedImage let ciImage = CIImage(cvPixelBuffer: pixelBuffer, options: [:]).oriented(.right) let aspect = arView.bounds.width / arView.bounds.height let widthForDisplayAspect = ciImage.extent.height * aspect let cropped = ciImage.cropped(to: CGRect(x: ciImage.extent.width/2-widthForDisplayAspect/2, y: 0, width: widthForDisplayAspect, height: ciImage.extent.height)) } これを解析すれば、ディスプレイ表示されたフレームを基準に解析できる。 ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLやARKitを使ったアプリを作っています。 機械学習/AR関連の情報を発信しています。 Twitter Medium GitHub
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUI Tutorial の応用

内容 SwiftUI Tutorial の応用 開発環境 ハードウエア 項目 PC MacBook Air(M1,2020) メモリ:16GB ストレージ:1TB 実機 iPhoneSE(2nd Generation 128GB iOS 15.3.1) PCと実機を接続する USB-C Digital AV Multiportアダプタ ソフトウエア 項目 言語 Swift 5.5.2 IDE Xcode Ver 13.2.1 その他 Visual Studio Code Ver 1.64.2 バージョン管理 GitHub - 実際にコードを確認できます アプリの内容 画面 駅一覧画面 駅詳細画面 機能 駅一覧画面 路線選択タブで路線を切替 お気に入りのみ表示 駅詳細画面 お気に入り機能 隣の駅を表示 ただし大阪梅田駅は、次の駅のみ(宝塚・神戸三宮・京都河原町方向)を表示 ただし宝塚駅、神戸三宮駅および京都河原町駅は、前の駅のみ(大阪梅田方向)を表示 応用 画面上部にタブを追加 LNE / MoneryForward ME 等に実装されている 駅一覧画面 import SwiftUI struct StationListView: View { @EnvironmentObject var modelData: ModelData @State private var showFavoriteOnly = false @State private var selection: Line = .Takarazuka var filteredStations: [Station] { modelData.stations.filter { station in (!showFavoriteOnly || station.isFavorite) && station.line == selection } } var body: some View { // GeometryReader を使うことがポイント GeometryReader { geometry in NavigationView { VStack(spacing: .zero) { // LineTabView() で画面上部のタブを実装 LineTabView(selection: $selection, geometrySize: geometry.size) List{ Toggle(isOn: $showFavoriteOnly){ Text("お気に入りのみ表示") } ForEach(filteredStations) { station in NavigationLink { StationDetail(station: station) } label: { StationRow(station: station) } } } Spacer() } .navigationBarTitle("駅一覧",displayMode: .inline) } .navigationViewStyle(.stack) // iPad のサイドビューでは画面上部タブのレイアウトが崩れるため、.navigationViewStyle を .stack に指定している } } } struct StationListView_Previews: PreviewProvider { static var previews: some View { StationListView() .environmentObject(ModelData()) } } 画面上部のタブ import SwiftUI import MapKit struct LineTabView: View { @Binding var selection: Line var geometrySize: CGSize var body: some View { HStack(spacing: .zero) { ForEach(Line.allCases,id: \.self){ line in VStack { Button { self.selection = line print("\(line.getLineName())を選択しました") } label: { Text("\(line.getLineName())") .font(.body) .fontWeight(.heavy) .foregroundColor(self.selection == line ? line.getLineColor() : .gray) } .frame(width: geometrySize.width / CGFloat(Line.allCases.count), height: 25) Rectangle() .fill(self.selection == line ? line.getLineColor() : .gray) .frame(width: geometrySize.width / CGFloat(Line.allCases.count), height: 2) } } } } } struct LineTabView_Previews: PreviewProvider { static var geometry: CGSize = CGSize(width: 200, height: 44) static var previews: some View { LineTabView(selection: .constant(.Takarazuka), geometrySize: geometry) } } その他 隣の駅を表示する処理のテストコードを書く & XCode でテストを実行 import XCTest @testable import Sample class SampleTests: XCTestCase { // 省略 func testGetNextStation() throws { var count = 0 let stations = ModelData().stations stations.map { station in print("\(count) : \(station)") count += 1 } let takarazukaOsakaUmeda = stations[0] let takarazukaNakatsu = takarazukaOsakaUmeda.getNextStation() // 期待する結果とコードの処理結果が等しければ、SUCCESS XCTAssertEqual(takarazukaNakatsu?.id, "takarazuka02") let takarazukaTakarazuka = stations[18] XCTAssertEqual(takarazukaTakarazuka.getNextStation()?.id, nil) let kobeOsakaUmeda = stations[19] let kobeNakatsu = kobeOsakaUmeda.getNextStation() XCTAssertEqual(kobeNakatsu?.id, "kobe02") let kobeKobeSannomiya = stations[34] XCTAssertEqual(kobeKobeSannomiya.getNextStation()?.id, nil) let kyotoOsakaUmeda = stations[35] let kyotoJuso = kyotoOsakaUmeda.getNextStation() XCTAssertEqual(kyotoJuso?.id, "kyoto03") let kyotoKyotoKawaramachi = stations[62] XCTAssertEqual(kyotoKyotoKawaramachi.getNextStation()?.id, nil) } func testGetPreviousStation() throws { let stations = ModelData().stations let takarazukaOsakaUmeda = stations[0] XCTAssertEqual(takarazukaOsakaUmeda.getPreviousStation()?.id, nil) let takarazukaNakatsu = stations[1] XCTAssertEqual(takarazukaNakatsu.getPreviousStation()?.id, takarazukaOsakaUmeda.id) let takarazukaTakarazuka = stations[18] XCTAssertEqual(takarazukaTakarazuka.getPreviousStation()?.id, "takarazuka55") } // 省略 } テスト実行結果 Test Successed 今後 おでかけスポット画面を追加 GitHub Actions を試してみる 感想 慣れたら UIKit よりもラク ダークモードが自動で反映されるのでラク Enum を上手く使うと改修がラク 参考資料 SwiftUI Tutorial 画面上部のタブを実装する際に参考にした記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】Storyboardを使わずUIを作成するために必要なXCodeの初期設定

目的 Storyboardを使わずにUIを作成する場合、必要となるXCodeの初期設定について説明 流れ ①: プロジェクトを作成し、実行できることを確認(Storyboardあり) ②: Main.storyboard を削除 ③: SceneDelegate.swift のコードを全てコメントアウト ④: MainInterface の設定を変更 ⑤: info.plist の Storyboard Name を削除 ⑥: ViewController のプログラム変更 ⑦: 実行結果 ①: プロジェクトを作成し、実行できることを確認(Storyboardあり) 新規でプロジェクトを作成し、「▶︎」ボタンを押して実行可能なことを確認する ②: Main.storyboard を削除 Main.storyboard上で右クリック → 「Delete」 → 「Move To Trash」 ③: SceneDelegate.swift のコードを全てコメントアウト SceneDelegate.swift のコードを全て選択して、「Command + /」 でコメントアウト AppDelegate.swift のコードを変更 AppDelegate.swift import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? // アプリ起動時に表示する View を格納する変数 // 起動時に実行 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) window?.makeKeyAndVisible() window?.rootViewController = ViewController() // アプリ起動時に表示するViewを指定 return true } // MARK: UISceneSession Lifecycle // // func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // // Called when a new scene session is being created. // // Use this method to select a configuration to create the new scene with. // return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) // } // // func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { // // Called when the user discards a scene session. // // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // // Use this method to release any resources that were specific to the discarded scenes, as they will not return. // } } ④: MainInterface の設定を変更 画面左上にある「"アプリ名"」 → 「Target」 → 「General」 → Deployment Info の 「Main Interface」 → Main を削除(空欄にする) ⑤: info.plist の編集 「info.plist」 → 「Application Scene Manifest」 → 「Application Session Role」 → 「item 0(Default Configuration)」 → 「-」(項目にカーソルを合わせると出てくる) ※ SceneDelegate で画面を呼び出す時は、「item 0(Default Configuration)」を消すのではなく、「item 0(Default Configuration)」でいらないものを消す。いらないもの については今回省略します! ⑥: ViewController のプログラム変更 ViewController.swift import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .green } } ⑦: 実行結果 見事、StoryboardなしでUI作成を行えました! 参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む