1
3

More than 1 year has passed since last update.

【Swift】Viewが生成されてからレイアウトしたい

Posted at

はじめに

Qiitaに書くほどではないとは思いますが、
UIKit初心者の自分からしたらどうやってやるの?ポイントだったので記事にしておきます

やろうとしてること

① UIViewControllerでWKWebViewを表示します
② 作ったUIViewControllerをUIViewControllerRepresentableでViewにします
③ SwiftUI側でsheetで表示します

問題点

iPadのsheetは全画面ではないのでWebViewがはみ出る

iPhone iPad
simulator_screenshot_C3E6D1A2-91C1-4992-A399-57FD25C93EA8.png simulator_screenshot_9881A23D-FBC7-43C2-831E-173F1E330CA2.png

修正前のコード

WebView
import UIKit
import WebKit
import SwiftUI

struct WebView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> WebViewController {
        return WebViewController()
    }
    func updateUIViewController(_ uiViewController: WebViewController, context: Context) { }
}

class WebViewController: UIViewController {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: view.frame)

        view.addSubview(webView)

        webView.customUserAgent = "Mozilla/5.0 (\(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad"); CPU \(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad") OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) 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)
    }
}
ContentView
import SwiftUI

struct ContentView: View {
    @State var isWebView: Bool = false
    var body: some View {
        Button("ログイン画面表示") {
            isWebView = true
        }
        .sheet(isPresented: $isWebView) {
            WebView()
        }
    }
}

修正後のコード

WebView
import UIKit
import WebKit
import SwiftUI

struct WebView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> WebViewController {
        return WebViewController()
    }
    func updateUIViewController(_ uiViewController: WebViewController, context: Context) { }
}

class WebViewController: UIViewController {

    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: view.frame)
+       webView = WKWebView(frame: .zero)

+       webView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(webView)

        webView.customUserAgent = "Mozilla/5.0 (\(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad"); CPU \(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad") OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) 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)
    }
}
ContentView
import SwiftUI

struct ContentView: View {
    @State var isWebView: Bool = false
    var body: some View {
        Button("ログイン画面表示") {
            isWebView = true
        }
        .sheet(isPresented: $isWebView) {
            WebView()
        }
    }
}
iPhone iPad
simulator_screenshot_C3E6D1A2-91C1-4992-A399-57FD25C93EA8.png simulator_screenshot_9411B724-75B8-4BC2-8CD4-390CD4E2C03F.png

解説

ViewControllerのライフサイクル.png

UIViewControllerのライフサイクルを理解していなかったのが原因だと思います。
viewDidLoadsheetのframeがとれるわけが無いので今考えたら当然って感じがしますが笑

試したこと1

最初にやったのはviewWillLayoutSubviewsでWebViewを生成してみました。

    override func viewWillLayoutSubviews() {
        webView = WKWebView(frame: view.frame)

        view.addSubview(webView)

        webView.customUserAgent = "Mozilla/5.0 (\(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad"); CPU \(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad") OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) 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)
    }

これは見た目的にはいい感じになったのですが、
viewWillLayoutSubviewsは画面をちょっと動かすと何度でも呼ばれるっぽく、
その度に再読み込みが発生してまともに使えませんでした。

試したこと2

1で再読み込みが発生するのはviewWillLayoutSubviewsの中にwebView.load(url)があるからだ!と思い、レイアウトが終わってすぐwebView.load(url)をしようとしました。

    override func viewWillLayoutSubviews() {
        webView = WKWebView(frame: view.frame)

        view.addSubview(webView)
    }

    override func viewDidAppear(_ animated: Bool) {
        webView.customUserAgent = "Mozilla/5.0 (\(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad"); CPU \(UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad") OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) 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)
    }

ライフサイクル表を見る限りこれは成功しそうです。
しかし、原因は分かりませんが、WebViewは真っ白で読み込みが行われませんでした。

おわり

おそらくこれは基本中の基本だと思いますが、大切だと思ったのでQiitaに残しておきます。
UIKit初心者は同じ問題にぶち当たると思うのでこの記事が役に立ては嬉しいです。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3