カテゴリー: SwiftUI

  • 【Vision OS】AppIcon をApp上でも表示する。

    【Vision OS】AppIcon をApp上でも表示する。

    これは何?

    Vision OS の AppIcon をViewの中で呼び出す方法についてです。

    環境

    2024/7/5現在

    • VisionOS 1.1
    • XCode Version 15.4 (15F31d)

    やり方

    現状はまとめて取ってくるといったことができないようなので、
    下記のように別々にレイヤーを取得してZStackで重ねるしかないようです。

    ZStack {
                    Image("AppIcon/Back/Content"  )
                        .resizable()
                        .frame(width: 300, height: 300)
                        .scaledToFit()
                        .clipShape(Circle())
                        
                    Image("AppIcon/Middle/Content")
                        .resizable().frame(width: 300, height: 300)
                        .scaledToFit()
                        .clipShape(Circle())
                        
                        .offset(z: 10)
                    Image("AppIcon/Front/Content" )
                        .resizable()
                        .frame(width: 300, height: 300)
                        .offset(z: 20).scaledToFit()
                        .clipShape(Circle())
                }
                .padding(.top, 70)

    しかしこれだと、層にはなるものの球体に張り付いたような形にはなりません。
    どうしても実装したいならば、別ソフトでオブジェクトを作ってきて、シーンとして読み込むのが良さそうです。

    ⚠️ できなかったこと

    下記のような書き方できませんでした。(この書き方でそのまま球面になっているものが取得できれば本当にありがたかった……。)

    Image("AppIcon/AppIcon/Content")
    
    Image("AppIcon")

    参考

    https://stackoverflow.com/questions/78153249/how-to-render-a-visionos-icon-in-a-view

  • Vision OS 音量をコントロールする

    Vision OS 音量をコントロールする

    ❓ これは何

    VisionOS での音(AudioPlaybackController)を Slider を使って音量をコントロールする方法についてです。

    🪴 環境

    • sonoma 14.5(23F79)
    • Apple M1
    • 16GB

    🛠️ やり方

    今回は自然の音を再生するアプリを例に作っていきます。

    NatureEntity.swift

    import SwiftUI
    import RealityKit
    
    class NatureEntity: Entity {
        private var fireAudioController: AudioPlaybackController?
        
        @MainActor required init() {
            super.init()
        }
        
        init(fireAudio: AudioPlaybackController?, rainAudio: AudioPlaybackController?, insectAudio: AudioPlaybackController?) {
            super.init()
            self.fireAudioController = fireAudio
        }
        
        func updateGlobalVolume(_ volume: Double) {
            fireAudioController?.gain = volume
        }
    }
    

    続いて、Sliderでの操作部分の作成です。

    NatureControls.swift

    import SwiftUI
    
    struct NatureControls: View {
        @Binding var globalVolume: Float
        
        var body: some View {
            VStack {
                Text("Global Volume")
                Slider(value: $globalVolume, in: 0...1)
                    .padding()
            }
        }
    }

    最後に、これらをまとめて表示する部分です。

    SoundView.swift

    import SwiftUI
    import RealityKit
    
    struct SoundView: View {
        @State private var globalVolume: Double = -10
        @State private var natureEntity: NatureEntity?
    
        var body: some View {
            ZStack {
                RealityView { content in
                    let natureEntity = NatureEntity(fireAudio: nil) // 実際のオーディオコントローラーを渡す
                    content.add(natureEntity)
                    self.natureEntity = natureEntity
                }
                
                NatureControls(globalVolume: $globalVolume)
                    .onChange(of: globalVolume) { volume in
                        natureEntity?.updateGlobalVolume(volume)
                    }
                    .padding()
                    .background(Color.black.opacity(0.7))
                    .cornerRadius(10)
                    .padding()
            }
        }
    }

    これで、音量調整のできる機能が備わったと思います。

    ポイントは fireAudioController?.gain = volume という部分で、
    音量調整は gain で行うということです。Doubleで渡してあげたところうまくいきました。

  • 【iOS, mac, vision os】Apple Store に申請する

    【iOS, mac, vision os】Apple Store に申請する

    ❓ これは何

    iOS, mac os, VisionOS などをストア申請するまでの手順の解説記事です。

    この記事は2024年7月現在の内容です。

    📗 手順

    🛠️ Xcode での操作

    xcode を開いて、Product から Archive を選択します。

    ポップアップが出てくるので、App Store Connect を選択します。

    あとは基本的にOKを押して進めていけば大丈夫です。

    💻 App Store Connect での操作

    https://appstoreconnect.apple.com/apps にアクセスすると、先ほどディストリビュートしたAppがあるはずです。

    スクリーンショットを追加しましょう。
    VisionOSの場合は3840*2160px である必要があります。
    事前に整えておきましょう。

    解像度の変更方法はこちらをご覧ください。

    • プロモーション用テキスト
    • 概要
    • キーワード
    • サポートURL

    を入力します。

    ビルドを追加しましょう。先ほどディストリビュートしたので選択できるはずです。
    コンプライアンスの部分も設定してください。クリックして次へを選択していけば基本的に大丈夫です。(フランスへの配信は別途捜査が必要)

    ログインが必要なAppの場合は App Reviewに関する情報からサインイン情報を追加します。
    不要な場合はチェックを外してください。

    できたらページ右上のボタンから保存を押しましょう。

    一般

    一般タグに切り替えて、名前とサブタイトルを追加します。

    一般情報の欄の「コンテンツ配信権」「年齢制限」「カテゴリ」をそれぞれ選択してください。

    できたらページ右上のボタンから保存を押しましょう。

    アプリのプライバシー

    「アプリのプライバシー」タブに移動します。
    「プライバシーポリシーURL」「プロダクトページのプレビュー」をそれぞれ設定してください。
    プライバシーポリシーのページがない場合はhttps://studio.design/などでサクッと制作すると良いでしょう。

    できたらページ右上の「公開」ボタンを押しましょう。

    価格および配信状況

    価格を選択してください。
    有料アプリの場合は別途設定が必要になるはずです。(規約に同意の必要がある)

    「アプリの配信状況」から配信する国を設定してください。

    アプリの提出

    全てできたら、「配信用に追加」ボタンを押して、「App Review」に提出しましょう。

    終わりに

    以上です。お疲れ様でした。

    記事をご覧いただきありがとうございます!!!

  • Distribute App ができない Unable to process request – PLA Upadata Available

    Distribute App ができない Unable to process request – PLA Upadata Available

    これは何

    App Store で product/archive からディストリビュートに進んで下記のエラーが出た際の対処法です。

    手順

    Developperアカウントを持っているのに下記のエラーが出た場合は、新しい利用規約に同意していない場合があります。久しぶりにアップする方はこの場合を確認しましょう。

    Unable to process request - PLA Upadata Available

    または下記のエラーメッセージの場合もあります。

    App record creation error
    
    "STATE_ERROR.APP_CREATE.PLATFORM_NOT_ALLOWED_DUE_TO_CONTRACT_STATE", and reason "One or more platforms cannot be created for this app due to your provider's contract state. Creation of apps for the platform(s) xrOS is not available due to your provider's contract state."

    この場合は Storeconnect にwebからアクセスして、新しい利用規約に同意しましょう。

    appstoreconnect.apple.com にアクセスすると、下記のような警告が出てると思うので、それに従いましょう。

    これでも解決しない場合 https://appstoreconnect.apple.com/business/ のページにアクセスして、自分の登録名をクリックした下層ページにある有料アプリ契約、無料アプリ契約の欄を確認してください、もしかしたら同意できていない契約があるかもしれません。

  • 【SwiftUI】箇条書きにしたい!

    【SwiftUI】箇条書きにしたい!

    SwiftUIを使って番号付きの箇条書きにしたい!

    これをやると下記のようになります。

    以下コードです。自分の環境に合わせて置き換えてください。

    struct TutorialView: View {
        let items = [
            "Connect your iPhone to your Mac with a cable.",
            "Open QuickTime Player on your Mac.",
            "Select File > New Movie Recording.",
            "Click the arrow next to the record button and select your iPhone.",
            "In the movie recording screen, device information will be displayed in the detailed settings. Click the arrow next to the red recording button and select ‘iPhone’ for the Screen."
        ]
        
        var body: some View {
            Text("How to mirror on Mac using QuickTime Player")
                .font(.title)
            VStack(alignment: .leading) {
                ForEach(items, id: \.self) { item in
                    HStack(alignment: .top) {
                        Text("\(items.firstIndex(of: item)! + 1).")
                            .font(.body)
                            .fontWeight(.bold)
                        Text(item)
                            .font(.body)
                    }
                    .padding(.top, 6)
                }
            }
            .padding()
        }
    }
    
    #Preview {
        TutorialView()
    }
    
  • 【SwiftUI】ポップアップに”二度と表示しない”のトグルをつける

    【SwiftUI】ポップアップに”二度と表示しない”のトグルをつける

    最初だけポップアップが表示されて、次回からはユーザーがポップアップの有無を選べるトグル。

    あれの実装方法です。

    .sheetでポップアップを表示します。AppStorageを使って「二度と表示しない」をBooleanで設定して置けばOK

    import SwiftUI
    
    struct ContentView: View {
        @AppStorage("dontShowAgain") var dontShowAgain = false
        @State private var showingPopup = false
    
        var body: some View {
            VStack {
                Button("Show Popup") {
                    if !dontShowAgain {
                        showingPopup = true
                    }
                }
            }
            .sheet(isPresented: $showingPopup) {
                HowToSetIcon(dontShowAgain: $dontShowAgain)
            }
        }
    }
    

    popupViewの中身

    import SwiftUI
    
    struct PopupView: View {
        @Environment(\.presentationMode) var presentationMode
        @Binding var dontShowAgain: Bool
    
        var body: some View {
            VStack {
                Text("This is a popup")
    
                Toggle(isOn: $dontShowAgain) {
                    Text("二度と表示しない")
                }
    
                Button("Close") {
                    self.presentationMode.wrappedValue.dismiss()
                }
            }
        }
    }
  • 【SwiftUI】動画を角丸にしたい

    【SwiftUI】動画を角丸にしたい

    AvKitで読み込んだ動画にCornerRadiusをかけると、動画自体が飛び出してしまいます。下の感じ。

    VideoPlayer(player: player)
        .aspectRatio(4 / 3, contentMode: .fit)
        .overlay(
            RoundedRectangle(cornerRadius: 20)
                .stroke(Color.purple, lineWidth: 2)
        )

    これを直すには以下のように.clipShapeを追加すること。意外と簡単だったね。

    VideoPlayer(player: player)
        .aspectRatio(4 / 3, contentMode: .fit)
        .clipShape(RoundedRectangle(cornerRadius: 20))
        .overlay(
            RoundedRectangle(cornerRadius: 20)
                .stroke(Color.purple, lineWidth: 2)
        )
  • 【SwiftUI】画像を追加する機能と、プレビュー、そして削除する機能

    【SwiftUI】画像を追加する機能と、プレビュー、そして削除する機能

    こんにちは!今日は、SwiftUIで画像を選択する機能を作りました。下の画像のような感じです。

    コードの名前はSelectImage.swiftです。

    コードの全体像

    import SwiftUI
    import UniformTypeIdentifiers
    
    struct SelectImage: View {
        @Binding var image: NSImage?
        @State private var isHovering = false
        
        var body: some View {
            // ここに色々なコードが入ります
        }
    }
    

    まず、import SwiftUIimport UniformTypeIdentifiersで必要なライブラリをインポートします。次に、SelectImageという名前のViewを作ります。このViewには2つの変数があります。@Binding var image: NSImage?は選択した画像を保持し、@State private var isHovering = falseはマウスが画像の上にあるかどうかをチェックします。

    画像を選ぶ部分

    Button("Select Image") {
        let panel = NSOpenPanel()
        panel.allowedContentTypes = [UTType.image]
        panel.allowsMultipleSelection = false
        if panel.runModal() == .OK {
            let url = panel.url
            self.image = NSImage(contentsOf: url!)
        }
    }
    

    “Select Image”というボタンを作ります。このボタンをクリックすると、画像を選ぶダイアログが開きます。選んだ画像はNSImageとしてimage変数に保存されます。

    画像を表示する部分

    if let img = image {
        ZStack {
            Rectangle()
                .fill(Color.gray)
                .frame(width: 44, height: 44)
                .cornerRadius(4)
            Image(nsImage: img)
                .resizable()
                .scaledToFit()
                .frame(width: 40, height: 40)
        }
    }
    

    選んだ画像は、四角形の枠内に表示されます。画像は自動的に枠に合わせてリサイズされます。

    画像を削除する部分

    Button {
        self.image = nil
    } label: {
        Image(systemName: "xmark.circle")
            .foregroundColor(.white)
            .font(.system(size: 16, weight: .bold))
    }
    .disabled(image == nil)
    .padding(2)
    .background(Color.black)
    .clipShape(Circle())
    .opacity(isHovering ? 0.9 : 0)
    .onHover { hovering in
        isHovering = hovering
    }
    

    画像の上にマウスを持っていくと、削除ボタンが現れます。このボタンを押すと、選んだ画像が削除されます。

    まとめ

    以上が、私が作ったSelectImage.swiftの解説です。このコードを使えば、SwiftUIで簡単に画像選択機能を追加することができます。皆さんもぜひ試してみてください。それでは、また次回!

Home
About
Blog
Works
Contact