【Swift】WKWebViewでキーボードを開くとレイアウトエラーが発生する

# はじめに
以前からWKWebViewではキーボード関係でレイアウトエラーが発生していましたが、ログ上のエラーだけで見た目には影響はありませんでした。
しかし、今回キーボード関係で見た目に不具合が起きたので原因と解決方法を紹介します。

# 症状1
キーボードを開いて閉じるとキーボードがあった場所に空白ができる
![Simulator Screen Recording - iPhone 12 - 2022-08-24 at 21.49.39.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b650ae1e-9d6b-223c-df57-448d33544fc2.gif)

# 症状2
最初からテキストフィールドにフォーカスされる画面だと上に画面が圧縮されている
![Simulator Screen Recording - iPhone 12 - 2022-08-24 at 21.55.23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/9df7b8be-ead2-d836-ec5c-bc86433c298b.gif)

# 現在のコード
```swift
import SwiftUI

struct ContentView: View {
    @StateObject private var loginViewModel = LoginViewModel()
    var body: some View {
        GeometryReader { geo in
            VStack(alignment: .center, spacing: 15) {
                Button("ログイン") {
                    loginViewModel.isLoginWebView = true
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .scaleEffect(geo.size.width > 450 ? 1.5 : 1.0)
            .sheet(isPresented: $loginViewModel.isLoginWebView) {
                LoginWebView(loginViewModel: loginViewModel)
            }
        }
    }
}
```

```swift
import UIKit
import WebKit
import SwiftUI

struct LoginWebView: UIViewControllerRepresentable {
    @ObservedObject var loginViewModel: LoginViewModel
    func makeUIViewController(context: Context) -> WebViewController {
        return WebViewController(loginViewModel: loginViewModel)
    }
    func updateUIViewController(_ uiViewController: WebViewController, context: Context) { }
}

class WebViewController: UIViewController {
    var loginViewModel: LoginViewModel

    init(loginViewModel: LoginViewModel) {
        self.loginViewModel = loginViewModel
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    var webView: WKWebView!

    override func viewWillLayoutSubviews() {
        webView.widthAnchor.constraint(equalToConstant: view.bounds.width).isActive = true
        webView.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: .zero)

        webView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(webView)

        webView.customUserAgent = "Mozilla/5.0 (iPhone); CPU iPhone OS 15_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1"

        let url = URLRequest(url: URL(string: "https://mobile.twitter.com/i/flow/login")!)

        webView.load(url)
    }
}
```

```swift
import SwiftUI

class LoginViewModel: NSObject, ObservableObject {
    @Published public var isLoginWebView: Bool = false
}
```

# 修正方法1
ContentViewに付けている`.scaleEffect(geo.size.width > 450 ? 1.5 : 1.0)`です。
これを削除すれば修正できました。
しかし、iPadの時はちょっとViewを大きくしたいので、これは削除したくありません。

```diff_swift
import SwiftUI

struct ContentView: View {
    @StateObject private var loginViewModel = LoginViewModel()
    var body: some View {
        GeometryReader { geo in
            VStack(alignment: .center, spacing: 15) {
                Button("ログイン") {
                    loginViewModel.isLoginWebView = true
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
-           .scaleEffect(geo.size.width > 450 ? 1.5 : 1.0)
            .sheet(isPresented: $loginViewModel.isLoginWebView) {
                LoginWebView(loginViewModel: loginViewModel)
            }
        }
    }
}
```

# 修正方法2
`isLoginWebView`をViewModelから使っていることでした。
`@State`で`isLoginWebView`を使えば正常に動作しました。
```diff_swift
import SwiftUI

struct ContentView: View {
-   @StateObject private var loginViewModel = LoginViewModel()
+   @State private var isLoginWebView: Bool = false
    var body: some View {
        GeometryReader { geo in
            VStack(alignment: .center, spacing: 15) {
                Button("ログイン") {
-                   loginViewModel.isLoginWebView = true
+                   isLoginWebView = true
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .scaleEffect(geo.size.width > 450 ? 1.5 : 1.0)
-           .sheet(isPresented: $loginViewModel.isLoginWebView) {
+           .sheet(isPresented: $isLoginWebView) {
                LoginWebView(loginViewModel: loginViewModel)
            }
        }
    }
}
```

# おわり
なぜこんな事が起きているのかは分かりませんがとりあえず直せてよかったです。