
I've got a theory that if you give 100% all of the time, somehow things will work out in the end.
- Larry Bird
需要在 App 中顯示網頁內容是很常見的事情,iOS SDK 提供了三種選項來讓開發者顯示網頁內容:

在本章中,我將引導你完成所有的選項,並向你展示如何使用它們來顯示網頁內容。對於 WKWebView 與SFSafariViewController,我們需要使用 UIViewRepresentable 與 UIView ControllerRepresentable 來結合這些元件,因為它們只在 UIKit 中可用。
為了示範如何在 SwiftUI 中顯示網頁內容,我們將建立About 標籤來顯示三種選項,如圖 22.1 所示:
聽起來很有趣,對吧?我們開始吧 !
首先,將本章所準備的圖片包( http://www.appcoda.com/resources/swift53/abouticons.zip ))匯入 Assets.xcasset,如圖 22.2 所示。

接下來,我們為 About 視圖建立單獨的檔案,在專案導覽器中的「View」資料夾上按右鍵,並選擇「New File...」,然後選取「SwiftUI View」模板,將檔案命名為「AboutView. swift」。
建立後,更新 AboutView 結構如下:
struct AboutView: View {
var body: some View {
NavigationStack {
List {
Image("about")
.resizable()
.scaledToFit()
Section {
Label("Rate us on App Store", image: "store")
Label("Tell us your feedback", image: "chat")
}
Section {
Label("Twitter", image: "twitter")
Label("Facebook", image: "facebook")
Label("Instagram", image: "instagram")
}
}
.listStyle(.grouped)
.navigationTitle("About")
.navigationBarTitleDisplayMode(.automatic)
}
}
}
我們利用一個 List 視圖來顯示可用的選項。SwiftUI 中的 List 視圖內建了對區塊的支援。在上列的程式碼中,我們建立了兩個區塊,一個區塊用來顯示使用者意見回饋的按鈕,另一個區塊用來顯示社群資訊。透過將清單樣式設定為「.grouped」,SwiftUI 會自動以灰色空白列來分隔各個區塊,從而增強清單的視覺組織及清晰度。
要顯示導覽列標題,則我們將 List 視圖嵌入到導覽視圖中,圖 22.3 顯示了帶有區塊的 List 視圖的預覽。

當使用者點擊其中一個選項時,App 將會開啟對應的網頁連結。我們將使用列舉來儲存選項的連結,在 AboutView 中宣告以下的列舉:
enum WebLink: String {
case rateUs = "https://www.apple.com/ios/app-store"
case feedback = "https://www.appcoda.com/contact"
case twitter = "https://www.twitter.com/appcodamobile"
case facebook = "https://www.facebook.com/appcodamobile"
case instagram = "https://www.instagram.com/appcodadotcom"
}
正如我在本章開頭所說,我將展示三種不同顯示網頁的方式。對於「Rate us on App Store」選項,App 會將使用者導引到內建的Safari 瀏覽器來顯示URL,SwiftUI 有一個名為「Link」的原生視圖元件用於此目的,你只需像下列程式碼這樣將「Rate us on App Store」的Label 包裹進去,即可啟用網頁連結:
Link(destination: URL(string: WebLink.rateUs.rawValue)!, label: {
Label("Rate us on App Store", image: "store")
.foregroundStyle(.primary)
})
destination 參數接受目標 URL 的 URL 物件,這裡我們將儲存在 WebLink 列舉中的 rateUS URL 傳送給它。我們新增 foregroundStyle 修飾器至 Label,以將文字顏色更改為黑色。預設上,如果你不進行任何更改,則所有的網頁連結都會顯示為藍色。
你不能在預覽窗格中測試該 Link 功能,不過為了在模擬器中測試它,我們需要修改 MainView.swift 檔案。在 MainView 中,將 Text("About") 替換為 AboutView(),如下所示:
AboutView()
.tabItem {
Label("About", systemImage: "square.stack")
}
.tag(2)
我們現在使用 AboutView,而不是顯示文字視圖。在模擬器中執行這個 App,並切換到 About 標籤,當你點擊「Rate us on App Store」按鈕時,App 將會開啟 Safari 瀏覽器, 如圖 22.4 所示。

現在你應該充分了解如何使用 Link 視圖,我們來探討如何將網頁視圖嵌入你的 App 中。對於「Tell us your feedback」及社群資訊來說,我們將專注在 App 中嵌入網頁瀏覽器,你可以使用 WKWebView 或 SFSafariViewController 這兩種方式來顯示網頁內容。在本小節中,我會示範如何使用 WKWebView 來實現,下一小節將介紹 SFSafariViewController 的用法。
同樣的, 這兩個元件在 SwiftUI 中不可用, 因此我們需要利用 UIKit 框架並使用 UIViewRepresentable 來建立自己的視圖。
在專案導覽器中的「View」資料夾上按右鍵,並選擇「New File...」,然後選取「Swift File」模板,將檔案命名為「WebView.swift」。檔案內容替換如下:
import SwiftUI
import WebKit
struct WebView: UIViewRepresentable {
var url: URL
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ webView: WKWebView, context: Context) {
let request = URLRequest(url: url)
webView.load(request)
}
}
WKWebView 是 WebKit 框架的一部分,因此必須在程式碼開頭匯入它。WebView 結構遵循UIViewRepresentable 協定,並實作所需的方法,即在 makeUIView 方法中,我們回傳 WKWebView 物件,並在updateUIView 方法中,我們載入指定的URL。
現在已經在我們的 SwiftUI 專案中使用 WebView 了。切換到 AboutView.swift,並新增一個狀態變數來儲存目前的連結:
@State private var link: WebLink?
要開啟網頁視圖,則將 .sheet 修飾器加到 List 視圖:
.sheet(item: $link) { item in
if let url = URL(string: item.rawValue) {
WebView(url: url)
}
}
sheet 修飾器監看 link 變數的變化,如果它設定為特定的 URL,我們會建立一個 WebView 的實例,並顯示網頁內容。
一旦你新增 sheet 修飾器,Xcode 就會顯示下列的錯誤:
Instance method 'sheet(item:onDismiss:content:)' requires that 'AboutView.WebLink' conform to 'Identifiable'
根據上列的敘述,WebLink 型別應該要遵循 Identifiable 協定。如官方文件中所述, Identifiable 協定用於為類別或值型別建立一致的識別,在這種情況下,sheet 修飾器要求我們提供遵循Identifiable 協定的項目,以正確識別及管理所呈現的工作表。
那麼,我們該如何修正這個錯誤呢?或者我們如何讓 WebLink 遵循 Identificable 協定?
採用該協定非常容易,你只需要在 WebLink 列舉中新增一個 id 屬性,如下所示:
enum WebLink: String, Identifiable {
case rateUs = "https://www.apple.com/ios/app-store"
case feedback = "https://www.appcoda.com/contact"
case twitter = "https://www.twitter.com/appcodamobile"
case facebook = "https://www.facebook.com/appcodamobile"
case instagram = "https://www.instagram.com/appcodadotcom"
var id: UUID {
UUID()
}
}
這裡我們利用通用唯一識別碼(UUID,Universally Unique Identifier )作為識別碼,以確保任何時候的唯一性。當你相應更新 WebLink 列舉後,Xcode 錯誤應該會消失。
最後,除了「Rate us on App Store」標籤以外,其餘標籤皆使用 .onTapGesture 修飾器更新:
Section {
Link(destination: URL(string: WebLink.rateUs.rawValue)!, label: {
Label("Rate us on App Store", image: "store")
.foregroundStyle(.primary)
})
Label("Tell us your feedback", image: "chat")
.onTapGesture {
link = .feedback
}
}
Section {
Label("Twitter", image: "twitter")
.onTapGesture {
link = .twitter
}
Label("Facebook", image: "facebook")
.onTapGesture {
link = .facebook
}
Label("Instagram", image: "instagram")
.onTapGesture {
link = .instagram
}
}
點擊任何標籤時,我們設定 link 變數為對應的 URL,然後在預覽窗格或模擬器中執行這個 App,當點擊任何標籤時,網頁視圖將顯示為模態視圖(Modal View ),如圖 22.5 所示。

之前我們使用 WKWebView 在 App 中顯示網頁內容,而在本小節中,我們將探討使用 SFSafariViewController 來嵌入網頁瀏覽器。如果你不熟悉 SFSafariViewController 的話, 它是一個類別,提供全功能的網頁瀏覽器體驗,類似於內建在 iOS 中。
與 WKWebview 類似,我們需要使用 UIViewRepresentable 為 SFSafariViewController 建立自訂的 SwfitUI 視圖。為此,在專案導覽器中的「View」資料夾上按右鍵,並建立一個新檔案,然後選取「Swift File」模板,將檔案命名為「SafariView」。更新檔案內容如下:
import SwiftUI
import SafariServices
struct SafariView: UIViewControllerRepresentable {
var url: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
return SFSafariViewController(url: url)
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
}
}
這個 SafariView 接受 URL 物件,然後我們在 makeUIViewController 中使用該 URL 來實例化 SFSafariViewController,這就是我們將 SFSafariViewController 與 SwiftUI 專案整合的方式。
現在切換到 AboutView.swift,將 WebView 替換為 SafariView,如下所示:
.sheet(item: $link) { item in
if let url = URL(string: item.rawValue) {
SafariView(url: url)
}
}
App 將使用 SafariView 取代 WebView 來顯示網頁內容。執行 App 來進行測試,如圖 22.6 所示,這個 Safari 視圖帶有「返回」與「往前」按鈕,這便是 SFSafariViewController 的強大之處。如果你需要在 App 中顯示全功能的瀏覽器,則你可以選擇 SFSafariView Controller。

我們探討了三種顯示網頁內容的選項,你不必要在你的 App 中使用所有的這些選項, 因為我們已經將它們用於示範目的。
SFSafariViewController 類別提供一個在你的 App 中嵌入網頁瀏覽器的便利方式,如果你的 App 需要為使用者提供無縫且功能豐富的瀏覽體驗,與建立你自己的自訂網頁瀏覽器相比,使用 Safari 視圖控制器可以節省大量時間及精力。
然而,在某些情況下,你可能只需要一些基本的網頁視圖來顯示網頁內容,在這種場景下,WKWebView 可能是更合適的選擇。花一些時間研究及評估我們涵蓋的所有網路瀏覽選項,然後選擇最適合你的特定需求及請求的選項。
在本章所準備的範例檔中,有最後完整的 Xcode 專案可供你下載參考: http://www.appcoda.com/resources/swift59/swiftui-foodpin-webview.zip 。