SwiftUIでアプリ内のhtmlファイルを表示してJavaScriptでカメラを利用する方法

SwiftUIでアプリ内のhtmlファイルを表示して、さらにJavaScriptでカメラを利用するための作業メモです。
# htmlファイルをXcodeに取り込み
1　Xcodeでプロジェクトを作成します。
2　表示したいhtmlや画像の入っているフォルダをプロジェクトにドラッグ＆ドロップします。
3　Added folder オプションが表示されます。
![スクリーンショット 2024-09-09 8.18.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509199/adee82a1-91ae-2a3d-6481-22311de8012c.png)

4　ここで「Create folder reference」を選択してから「Finish」ボタンを押します。
5　htmlのフォルダが追加されます。これで内部htmlファイルとして利用できます。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509199/d1e66038-884e-5beb-802b-c8b703c06dfe.png">

Added folder オプションの違いについて、詳しくはこちらのサイトを参照願います。
https://qiita.com/OSR108/items/c0bbe020662acc415ddb

# アプリ内htmlファイルの表示
　こちらのサイトの内容をほぼそのまま利用しました。解説を公開していただいていることに感謝いたします。
https://note.com/npaka/n/n3acc1e1b8036
https://qiita.com/kamimi01/items/9cefe1e73ab5b88051c6

# ContentView.swiftにプログラムを記入

```Swift:ContentView.swift
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    @Binding var assetName: String
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let fileName = NSString(string: assetName)
        let pathPrefix = fileName.deletingPathExtension
        let pathExtension = fileName.pathExtension
        if pathPrefix.isEmpty || pathExtension.isEmpty {
            return
        }
        if let url = Bundle.main.url(forResource: pathPrefix, withExtension: pathExtension) {
            uiView.loadFileURL(url, allowingReadAccessTo: url)
        }
    }
}

struct ContentView: View {
    @State private var isShownLocalAsset = true
    @State private var shownAssetName = "html/index.html"
    var body: some View {
        VStack {
            Spacer()
        }
        .fullScreenCover(isPresented: $isShownLocalAsset) {
            WebView(assetName: $shownAssetName)
        }
    }
}
```
 これでhtmlファイルを表示できます。
　shownAssetName = "html/index.html"
 この値はXcodeに取り込んだフォルダ名や表示したいhtmlファイルに従って変更してください。値を「https://qiita.com」に変更すると、「Qiita」を表示するアプリができます。
 
# アプリとして使うための微調整
　WKWebViewを利用してhtmlファイルを表示した場合、次のような特徴があります。
### 〇アドレスが表示されません
　内部htmlを利用するので、これでOKです。
### 〇「＜」もどるボタンが表示されません
　内部htmlを利用するので、これもOKと考えます。
### 〇アンカーリンク（aタグ）でリンク先のページを表示します
×リンクがtarget=”_blank”（ターゲットブランク）が指定されている場合には表示されません。
→　別ウィンドウ、別タブの表示ができません。
→　「こういうもの」という基本で内部htmlファイルを作成しましょう。
→　外部サイト（https）のurlを記入すれば、そのサイトを開くことができます。
　※　httpサイトの場合には、info.plistに許可の追加が必要となります。
### 〇画面より表示が長い場合、スクロールバーが表示されます
　→　表示長が画面内に収まっているとき、タッチして上下をすると、画面がビヨーンと移動（バウンス）します。これを防ぐには、htmlファイルのcssでスクロール禁止にします。
```css
html {
    overflow: hidden;
    }
```
### 〇２本指で拡大縮小（ピンチイン、アウト）ができます
　拡大縮小を禁止したい場合には、htmlファイルで設定します。
```html
<meta content="user-scalable = no">
```
　このタグを記入すると、ページが拡大縮小できないようになります。
### 〇Javascriptでカメラを利用してhtmlに表示したいときには
　info.plistに「Privacy – Camera Usage Description」を追加して「アプリでカメラを利用します」と理由を記入します。
→　しかし、初めの１枚は撮影しますが、Javascriptの作動が止まります。作動していますが、画面表示が更新されません。
→　解決方法がありました。Qiitaはありがたいですね。
https://qiita.com/tomi2518/items/a093dd1a30b26ef62f69
```Swift
    func makeUIView(context: Context) -> WKWebView {
        let config = WKWebViewConfiguration()
        config.allowsInlineMediaPlayback = true // インライン再生を許可
        config.mediaTypesRequiringUserActionForPlayback = [] // すべてのメディアの自動再生を許可
        return WKWebView(frame: .zero, configuration: config)
    }
```
これで、Javascriptでカメラを画像をhtmlファイルに表示して、アプリにできます。

## カメラ画像を表示できるコード
　完成したコードはこちら
```Swift:ContentView.swift
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    @Binding var assetName: String
    func makeUIView(context: Context) -> WKWebView {
        let config = WKWebViewConfiguration()
        config.allowsInlineMediaPlayback = true // インライン再生を許可
        config.mediaTypesRequiringUserActionForPlayback = [] // すべてのメディアの自動再生を許可
        return WKWebView(frame: .zero, configuration: config)
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let fileName = NSString(string: assetName)
        let pathPrefix = fileName.deletingPathExtension
        let pathExtension = fileName.pathExtension
        if pathPrefix.isEmpty || pathExtension.isEmpty {
            return
        }
        if let url = Bundle.main.url(forResource: pathPrefix, withExtension: pathExtension) {
            uiView.loadFileURL(url, allowingReadAccessTo: url)
        }
    }
}


struct ContentView: View {
    @State private var isShownLocalAsset = true
    @State private var shownAssetName = "html/index.html"
    var body: some View {
        VStack {
            Spacer()
        }
        .fullScreenCover(isPresented: $isShownLocalAsset) {
            WebView(assetName: $shownAssetName)
        }
    }
}
```
# 最後に
　表示幅、文字の大きさなどは実機で確認する必要がありますが、htmlで記載しているのでアプリ作成が容易になると思います。
　enchant.jsで記載したhtmlページを入れて動かしてみました。問題なく作動しました。過去の作品を手直しして公開したいと考えています。

 ### kotlinでアプリ内のhtmlファイルを表示する方法は？
 　Android SudioのKotlinでアプリ内のhtmlファイルを表示してJavaScriptでカメラを利用する方法はこちらをご覧ください。
 https://qiita.com/mvm43236/items/7de8906a8ea80ba00603