SwiftUI 框架

利用新的 ImageRenderer API 輕鬆把 SwiftUI 視圖轉換為圖像

iOS 16 為 SwiftUI 帶來的另一個 API 就是 ImageRenderer。我們可以利用這個 API,輕鬆把 SwiftUI 視圖轉換為圖像。在這篇文章中,我會帶大家試試使用這個新的 API。
利用新的 ImageRenderer API 輕鬆把 SwiftUI 視圖轉換為圖像
Photo by Amjith S on Unsplash
利用新的 ImageRenderer API 輕鬆把 SwiftUI 視圖轉換為圖像
Photo by Amjith S on Unsplash
In: SwiftUI 框架

iOS 16 為 SwiftUI 帶來的另一個 API 就是 ImageRenderer。有了這個 API,我們可以輕鬆把 SwiftUI 視圖轉換為圖像。這個實作十分簡單,讓我們利用想要轉換為圖像的視圖,來實例化 ImageRenderer 的實例:

let renderer = ImageRenderer(content: theView)

然後,我們就可以存取 cgImageuiImage 屬性,來取得轉換後的圖像。

一如以往,我喜歡利用範例來示範一個 API 的用法。較早之前,我們用了新的 Charts 框架來構建折線圖。這次,讓我們來看看如何讓使用者把折線圖保存為 Photo Album 中的圖像,並使用 ShareLink 進行分享。

重溫 Chart 視圖

swiftui-line-chart

首先,讓我們來重溫一下 ChartView 範例。我們使用了 Charts 框架的新 API 來構建一個有關氣溫的折線圖。程式碼如下:

var body: some View {
    VStack {
        Chart {
            ForEach(chartData, id: \.city) { series in
                ForEach(series.data) { item in
                    LineMark(
                        x: .value("Month", item.date),
                        y: .value("Temp", item.temperature)
                    )
                }
                .foregroundStyle(by: .value("City", series.city))
                .symbol(by: .value("City", series.city))
            }
        }
        .chartXAxis {
            AxisMarks(values: .stride(by: .month)) { value in
                AxisGridLine()
                AxisValueLabel(format: .dateTime.month(.defaultDigits))

            }
        }
        .chartPlotStyle { plotArea in
            plotArea
                .background(.blue.opacity(0.1))
        }
        .chartYAxis {
            AxisMarks(position: .leading)
        }
        .frame(width: 350, height: 300)
        .padding(.horizontal)

    }
}

要使用 ImageRenderer,我們首先要把程式碼重構 (refactor) 為一個方法:

private func makeChartView() -> some View {
    VStack {
        Chart {
            ForEach(chartData, id: \.city) { series in
                ForEach(series.data) { item in
                    LineMark(
                        x: .value("Month", item.date),
                        y: .value("Temp", item.temperature)
                    )
                }
                .foregroundStyle(by: .value("City", series.city))
                .symbol(by: .value("City", series.city))
            }
        }
        .chartXAxis {
            AxisMarks(values: .stride(by: .month)) { value in
                AxisGridLine()
                AxisValueLabel(format: .dateTime.month(.defaultDigits))

            }
        }
        .chartPlotStyle { plotArea in
            plotArea
                .background(.blue.opacity(0.1))
        }
        .chartYAxis {
            AxisMarks(position: .leading)
        }
        .frame(width: 350, height: 300)

        .padding(.horizontal)

    }
}

然後,讓我們宣告一個變數來保存視圖:

var chartView = ChartView()

利用 ImageRenderer 把視圖轉換為圖像

現在,我們可以把 Chart 視圖轉換為圖像了。我們要添加一個名為 Save to Photos 的按鈕,來把 Chart 視圖圖像儲存到 Photo Album 中。

讓我們如此實作按鈕:

var body: some View {

    VStack(spacing: 20) {
        chartView

        HStack {
            Button {
                let renderer = ImageRenderer(content: chartView)

                if let image = renderer.uiImage {
                    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
                }
            } label: {
                Label("Save to Photos", systemImage: "photo")
            }
            .buttonStyle(.borderedProminent)
        }
    }

}

在按鈕的閉包中,我們利用 chartView 來建立一個 ImageRenderer 的實例,並使用 uiImage 屬性來擷取生成的圖像。然後,讓我們調用 UIImageWriteToSavedPhotosAlbum 把圖像儲存到 Photo Album 中。

備註:我們需要在 info.plist 中添加一個 key  Privacy - Photo Library Usage Description,讓 App 可以把圖像儲存到內置的 Photo Album 中。

添加 Share 按鈕

swiftui-share-imagerenderer

在上一篇文章中,我們學了如何使用 ShareLink 來顯示一個 Share Sheet。我們可以搭配 ImageRanderer 使用,輕鬆地建立讓使用者分享 Chart 視圖的功能。

為方便起見,讓我們將渲染圖像 (rendered image) 的程式碼重構為一個獨立的方法:

@MainActor
private func generateSnapshot() -> UIImage {
    let renderer = ImageRenderer(content: chartView)

    return renderer.uiImage ?? UIImage()
}

這個 generateSnapshot 方法會把 chartView 轉換為圖像。

備註:如果你沒有用過 @MainActor,可以參考這篇文章

有了這個 helper 方法,我們就可以如此在 VStack 視圖中建立一個 ShareLink

ShareLink(item: Image(uiImage: generateSnapshot()), preview: SharePreview("Weather Chart", image: Image(uiImage: generateSnapshot())))
.buttonStyle(.borderedProminent)

現在,當我們點擊 Share 按鈕,App 就會擷取折線圖,並讓我們以圖像形式分享圖表。

swiftui-weather-chart-imagerenderer

調整圖像比例

你可能會發現渲染圖像的解析度有點低。ImageRenderer 類別有一個 scale 屬性,用來調整渲染圖像的比例。在預設情況下,這個屬性的值是 1.0。如果我們想提高圖像的解析度,可以將它設置為 2.03.0。或者,我們可以將值設置為螢幕比例:

renderer.scale = UIScreen.main.scale

總結

有了 ImageRenderer 類別,我們就可以簡單地把任何 SwiftUI 視圖轉換成圖像。如果你的 App 支援 iOS 16 以上的版本,就可以用這個新 API 為使用者建立一些方便的功能。除了渲染圖像外,我們還可以使用 ImageRenderer 來渲染 PDF 文件。詳情可以參閱 Apple 的官方文件

此外,Apple 也提供了一個更具體的渲染器:ChartRenderer,來將圖表匯出為圖像。我們會在之後的文章再探索這個類別。

譯者簡介:Kelly Chan-AppCoda 編輯小姐。
作者
Simon Ng
軟體工程師,AppCoda 創辦人。著有《iOS 18 App 程式設計實戰心法》、《iOS 18 App程式設計進階攻略》以及《精通SwiftUI》。曾任職於HSBC, FedEx等跨國企業,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda業務,致力於iOS程式教學、產品設計及開發。你可以到推特與我聯絡。
評論
更多來自 AppCoda 中文版
如何在 SwiftUI App 中開發 Live Activities
SwiftUI 框架

如何在 SwiftUI App 中開發 Live Activities

Live Activities 首次於 iOS 16 推出,是 Apple 最令人興奮的更新之一,能讓 App 與使用者在即時互動上更有連結。它不再需要使用者不斷打開 App,Live Activities 可以讓資訊直接顯示在鎖定畫面和 Dynamic Island 上。
使用 Tool Calling 強化 Foundation Models 功能
AI

使用 Tool Calling 強化 Foundation Models 功能

在前幾篇教學中,我們介紹了 Foundation Models 在 iOS 26 中的運作方式,以及如何使用這個全新框架打造具備 AI 功能的應用。我們也介紹了 @Generable 巨集,它能輕鬆地將模型回應轉換為結構化的 Swift 類型。 現在,在這個 Foundation
活用 Foundation Models 的 @Generable 與 @Guide 製作測驗 App
AI

活用 Foundation Models 的 @Generable 與 @Guide 製作測驗 App

在前一篇教學中,我們介紹了 Foundation Models 框架,並示範了如何用它來進行基本的內容生成。那個過程相當簡單——你提供一個提示詞(prompt),等幾秒鐘,就能獲得自然語言的回應。在我們的範例中,我們建立了一個簡單的問答 App,讓使用者可以提問,App 則直接顯示生成的文字。 但如果回應變得更複雜——你需要把非結構化文字轉換為結構化的物件呢? 舉例來說,
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。