はじめに
「原因がただの凡ミス」系の記事です笑 同様の現象で悩む方はいないかと思いますが、私はすぐに原因に気付けず時間を失ったので一応記事に残しておきます・・・。
開発環境
・macOS Catalina 10.15.7
・Xcode 12.2
本題
謎の余白問題
NavigationLinkで遷移した先の画面で、まず下にスクロールし、その後上にスクロールするとナビゲーションバーとコンテンツの間に余白が発生してしまう。遷移直後は上部に余白はないのになぜ?
※遷移先(お知らせ詳細画面)の表示内容に関して
適当にURLを設定したので、お知らせ詳細画面なのにAppleのサイトが表示されていますがお許しください・・・。
問題発生時のコード
NewsListView.swift
お知らせ一覧画面(遷移前のView)
import SwiftUI struct NewsListView: View { var body: some View { NavigationView { List(newsDataArray) { item in NavigationLink(destination: WebView(urlString: item.urlString)) { NewsListRowView(newsData: item) } } .navigationTitle("お知らせ") } } } struct NewsListView_Previews: PreviewProvider { static var previews: some View { NewsListView() } }
WebView.swift
お知らせ詳細画面(遷移先のView)
※【SwiftUI】WKWebViewを使えるようにする - Swift・iOSの実装と同じです。
import SwiftUI import WebKit struct WebView: UIViewRepresentable { var urlString: String class Coordinator: NSObject, WKUIDelegate, WKNavigationDelegate { var parent: WebView init(_ parent: WebView) { self.parent = parent } // "target="_blank""が設定されたリンクも開けるようにする func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { if navigationAction.targetFrame == nil { webView.load(navigationAction.request) } return nil } // URLごとに処理を制御する func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) { if let url = navigationAction.request.url?.absoluteString { if (url.hasPrefix("https://apps.apple.com/")) { guard let appStoreLink = URL(string: url) else { return } UIApplication.shared.open(appStoreLink, options: [:], completionHandler: { (succes) in }) decisionHandler(WKNavigationActionPolicy.cancel) } else if (url.hasPrefix("http")) { decisionHandler(WKNavigationActionPolicy.allow) } else { decisionHandler(WKNavigationActionPolicy.cancel) } } } } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIView(context: Context) -> WKWebView { let webView = WKWebView() return webView } func updateUIView(_ webView: WKWebView, context: Context) { // makeCoordinatorで生成したCoordinatorクラスのインスタンスを指定 webView.uiDelegate = context.coordinator webView.navigationDelegate = context.coordinator // スワイプで画面遷移できるようにする webView.allowsBackForwardNavigationGestures = true let url = URL(string: urlString)! let request = URLRequest(url: url) webView.load(request) } }
原因
遷移先のnavigationBarTitleDisplayModeの設定が漏れていたため・・・。
※参考
https://developer.apple.com/documentation/swiftui/link/navigationbartitledisplaymode(_:)
遷移前のnavigationBarTitleDisplayModeの設定はデフォルトのままなので、"navigationBarTitleDisplayMode(.large)"。本来は遷移先のnavigationBarTitleDisplayModeの設定も必要だが、ここの対応が漏れたことで意図せず一部遷移前の設定が反映されてしまった可能性がある。謎の余白の正体は、"navigationBarTitleDisplayMode(.large)"の表示領域であり、タイトル名も設定していなかったため余白のように見えていただけ。
解決方法
WebViewを表示するNewsDetailViewを追加して、WebViewにnavigationBarTitleDisplayModeを設定する。
NewsDetailView.swift
import SwiftUI struct NewsDetailView: View { var urlString: String var title: String var body: some View { WebView(urlString: urlString) .navigationTitle(title) .navigationBarTitleDisplayMode(.inline) } } struct NewsDetailView_Previews: PreviewProvider { static var previews: some View { NewsDetailView(urlString: "https://www.apple.com/jp/", title: "") } }
遷移先の画面をNewsDetailViewに変更。
NewsListView.swift
import SwiftUI struct NewsListView: View { var body: some View { NavigationView { List(newsDataArray) { item in NavigationLink(destination: NewsDetailView(urlString: item.urlString, title: item.title)) { NewsListRowView(newsData: item) } } .navigationTitle("お知らせ") } } } struct NewsListView_Previews: PreviewProvider { static var previews: some View { NewsListView() } }
おわりに
これからも凡ミスしながら少しずつSwiftUIを学んでいきたいと思います・・・笑
参考
・https://developer.apple.com/documentation/swiftui/link/navigationbartitledisplaymode(_:)
・詳細! SwiftUI iPhoneアプリ開発入門ノート iOS 13 + Xcode11対応
↓上記書籍のXcode 12対応版です。