SwiftUI 框架

利用 ScrollViewReader 輕鬆讓滾動視圖滾動到特定位置

在新版 SwiftUI 中,其中一個我最喜歡的新功能就是 ScrollViewReader。有了 ScrollViewReader 之後,我們只需要幾行程式碼,就可以使滾動視圖滾動到特定位置。
利用 ScrollViewReader 輕鬆讓滾動視圖滾動到特定位置
利用 ScrollViewReader 輕鬆讓滾動視圖滾動到特定位置
In: SwiftUI 框架

在新版 SwiftUI 中,其中一個我最喜歡的新功能就是 ScrollViewReader。在 iOS 14 推出之前,要控制內置 ScrollView 的滾動位置並不容易。如果想要滾動視圖滾動到特定位置,就需要自己想出解決方案。

有了 ScrollViewReader 之後,我們只需要幾行程式碼,就可以使滾動視圖滾動到特定位置。在這篇教學文章中,我們會探索這個新視圖元件,讓大家了解如何把它應用在 App 上。

創建一個水平的 ScrollView

現在,讓我們從一個簡單的範例看看 ScrollViewReader 的用法。先看看以下的程式碼:

struct ContentView: View {

    let photos = [ "bigben", "bridge", "canal", "effieltower", "flatiron", "oia" ]

    var body: some View {
        GeometryReader { geometry in
            ScrollView(.horizontal) {
                HStack(alignment: .center) {
                    ForEach(photos.indices) { index in
                        Image(photos[index])
                            .resizable()
                            .scaledToFill()
                            .frame(width: geometry.size.width - 50)
                            .cornerRadius(25)
                            .padding(.horizontal, 25)
                    }
                }
            }
        }
    }
}

我們建立了一個水平滾動視圖,來顯示一組照片。如果你在模擬器或預覽面板中執行程式碼,就可以水平滑動以滾動瀏覽照片。

scrollviewreader-app-demo

如果我們現在想添加一些按鈕,讓使用者瀏覽不同的照片,應該怎樣做呢?或者這樣說,我們如何讓滾動視圖滾動到特定圖像?

使用 ScrollViewReader

ScrollViewReader 就是上面問題的解決方法!這個新視圖元件的用法非常簡單。首先,給滾動視圖中的每個元素一個獨特的 ID。在這個範例中,我們可以將 .id 修飾符加到 Image 視圖,並將其數值設置為照片的索引 (index):

Image(photos[index])
    .resizable()
    .scaledToFill()
    .frame(width: geometry.size.width - 50)
    .cornerRadius(25)
    .padding(.horizontal, 25)
    .id(index)

接下來,讓我們利用 ScrollViewReader 包裝滾動視圖。ScrollViewReader 的內容視圖建構器接收 ScrollViewProxy 實例:

GeometryReader { geometry in
    VStack {
        ScrollViewReader { scrollView in
            ScrollView(.horizontal) {
                HStack(alignment: .center) {
                    ForEach(photos.indices) { index in
                        Image(photos[index])
                            .resizable()
                            .scaledToFill()
                            .frame(width: geometry.size.width - 50)
                            .cornerRadius(25)
                            .padding(.horizontal, 25)
                            .id(index)
                    }
                }
            }

            // Navigation Buttons
            HStack {
                // To be complete
            }
            .frame(height: 70)
        }
    }
}

在我們得到 Proxy 後,可以呼叫 scrollTo 函式,以滾動到特定索引。以下面這行程式碼為例,我們要求滾動視圖滾動到最後一張照片:

scrollView.scrollTo(photos.count - 1)

最後,我們可以聲明一個狀態變數 (state variable),來追踪當前的照片索引:

@State private var currentIndex = 0

然後,你可以這樣在 HStack 中編寫程式碼:

HStack {
    Button(action: {
        withAnimation {
            scrollView.scrollTo(0)
        }
    }) {
        Image(systemName: "backward.end.fill")
            .font(.system(size: 50))
            .foregroundColor(.black)
    }

    Button(action: {
        withAnimation {
            currentIndex = (currentIndex == 0) ? currentIndex : currentIndex - 1
            scrollView.scrollTo(currentIndex)
        }
    }) {
        Image(systemName: "arrowtriangle.backward.circle")
            .font(.system(size: 50))
            .foregroundColor(.black)
    }

    Button(action: {
        withAnimation {
            currentIndex = (currentIndex == photos.count - 1) ? currentIndex : currentIndex + 1
            scrollView.scrollTo(currentIndex)
        }
    }) {
        Image(systemName: "arrowtriangle.forward.circle")
            .font(.system(size: 50))
            .foregroundColor(.black)
    }

    Button(action: {
        withAnimation {
            scrollView.scrollTo(photos.count - 1)
        }
    }) {
        Image(systemName: "forward.end.fill")
            .font(.system(size: 50))
            .foregroundColor(.black)
    }
}
.frame(height: 70)

對於每個按鈕,我們都會呼叫 scrollTo 函式來滾動到特定的照片。利用 withAnimation 包裝程式碼後,App 會呈現一個漂亮的滾動動畫。

scrollviewreader-perform-scrolling

在 List 上應用

ScrollViewReader 不僅可以用來包裝 ScrollView,在 List 上也可以應用。假設我們把 App 轉換為列表形式,而不是水平滾動視圖:

ScrollViewReader { scrollView in
    List {
        ForEach(photos.indices) { index in
            Image(photos[index])
                .resizable()
                .scaledToFill()
                .cornerRadius(25)
                .id(index)
        }
    }
 
    .
    .
    .
}

我們還是可以應用 scrollTo 函式,要求列表滾動到特定元素。

總結

ScrollViewReader 是 SwiftUI 框架中很好的新增功能。現在我們不再需要開發自己的解決方案,就可以輕鬆地指示任何滾動視圖滾動到特定位置。希望你喜歡這篇教學文章,並看到這個新元件的用處。如果想更深入學習 SwiftUI 框架,並了解其他技巧,可以閱讀我們《精通 SwiftUI》一書。

譯者簡介:Kelly Chan-AppCoda 編輯小姐。
原文How to Use ScrollViewReader to Perform Programmatic Scrolling

作者
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 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。