SwiftUI 框架

在 SwiftUI 處理 ScrollView:簡單以編程方式滾動到特定行列

在 SwiftUI 中,即使是執行簡單的任務時,處理 ScrollView 的步驟都可以變得十分繁瑣;更令人沮喪的是,同一個情況在 UIKit 明明就非常簡單。在這篇文章中,Alessandro 會帶大家實作一個簡單的範例 App,只需要 30 多行程式碼,就可以以編程方式滾動列表,到我們所需要的行列了。
在 SwiftUI 處理 ScrollView:簡單以編程方式滾動到特定行列
在 SwiftUI 處理 ScrollView:簡單以編程方式滾動到特定行列
In: SwiftUI 框架
本篇原文(標題:SwiftUI: How to Programmatically Scroll to a Row)刊登於作者 Medium,由 Alessandro Manilii 所著,並授權翻譯及轉載。

SwiftUI 中,即使是執行簡單的任務時,處理 ScrollView 的步驟都可以變得十分繁瑣;更令人沮喪的是,同一個情況在 UIKit 明明就非常簡單。

不幸的是,其中一個繁瑣的步驟,就是滾動到所需的行列。

在 UIKit 中,每個 UIScrollView 都有一個非常方便的方法,來以編程方式滾動視圖:

func setContentOffset(CGPoint, animated: Bool)

同樣地,每種子類都有相似的方法。例如,我們可以在 UITableView 中使用:

func scrollToRow(at: IndexPath, at: UITableView.ScrollPosition, animated: Bool)

然而,目前 SwiftUI 還沒有這些簡單的功能,因此我們就需要編寫一些比較複雜的程式碼,來執行相同的任務。

在這篇文章中,我們將會一起實作一個這樣的範例 App:

首先,讓我們編寫出簡單的 UI,我們只需要用到 ListPicker 視圖。

struct ContentView: View {

    @State var selectedIndex: Int = 0

    var body: some View {

        NavigationView {
            VStack {

                List {
                    ForEach(0..<100, id:\.self) { index in
                        Text("Row number \(index)")
                            .padding()
                    }
                }

                Divider()

                Picker("", selection: $selectedIndex) {
                    ForEach(0..<100, id: \.self) { index in
                        Text("Go to Row \(index)")
                    }
                }
                .pickerStyle(.wheel)
            }
            .navigationTitle("Scroll To Demo")
        }
    }
}

當然,這個簡單的 UI 是無法操作的。我們可以試著 wheel picker 選擇一個項目,你會發現它沒有回應。

我們需要一些元件,來觀察及讀取 ScrollView 內發生的事情。

Apple 在 iOS 14 中引入了一個名為 ScrollViewProxystruct,它的定義是這樣的:

掃描 Proxy 的所有滾動視圖 (scroll view),找出第一個有標識符 (identifier) `id` 的子視圖,然後滾動到該視圖。

在 SwiftUI 官方文檔的同一部分,我們也看到:

我們不會直接創建 ScrollViewProxy 的實例。相反,ScrollViewReader 會在其內容視圖構建器中接收 ScrollViewProxy 的實例。

因此,我們會按 Apple 所說,使用 ScrollViewReader 視圖!

這個新的元件是一個內建 proxy scrollView 的 View。讓我們這樣開始修改 UI:

struct ContentView: View {

    @State var selectedIndex: Int = 0

    var body: some View {
        VStack {
            NavigationView {
            ScrollViewReader { scrollView in
                List {
                    ForEach(0..<100, id:\.self) { index in
                        Text("Row number \(index)").id(index) // <- NOTE HERE
                            .padding()
                    }
                }
                Divider()

                Picker("", selection: $selectedIndex) {
                    ForEach(0..<100, id: \.self) { index in
                        Text("Go to Row \(index)")
                    }
                    
                    // More to come...
                }
                .pickerStyle(.wheel)
            }
            .navigationTitle("Scroll To Demo")
            }
        }
    }
}

從上面的程式碼,我們做了以下的事:

  • 在第 8 行,我們使用了 ScrollViewReader 視圖。
  • 在第 11 行,我們在 Text 視圖添加了 .id(index) 修飾符。在 SwiftUI 中,我們經常會用一個獨特的 id 來標識一個元素;這正是我們在這行程式碼所做的事。

現在,讓我們編寫程式碼,來以編程方式滾動列表吧!我在第 22 行添加了註解 // More to come...,我們將在這裡綁定 picker 的選擇和操作。

讓我們加入以下程式碼:

.onReceive(Just(selectedIndex)) { value in
    scrollView.scrollTo(selectedIndex, anchor: .center)
}

由於 Just 運算符 (operator) 是來自 Combine 框架的,因此我們需要在程式碼開頭匯入這個框架:#import Combine

完成了!

現在來當我們選擇一個項目時,上方的 List 就會滾動到指定的行列。

不過你可能也覺得,這段程式碼雖然可以運作,但還是不夠好,那個「跳轉」影響了整個 UX。

讓我們為滾動動作添加一個簡單而好看的動畫:

.onReceive(Just(selectedIndex)) {value in
            withAnimation {
                scrollView.scrollTo(
                    selectedIndex, anchor: .center
                )
            }
        }

範例 App 完成了!以下是完整的程式碼:

import SwiftUI
import Combine

struct ContentView: View {

    @State var selectedIndex: Int = 0

    var body: some View {
        VStack {
            NavigationView {
            ScrollViewReader { scrollView in
                List {
                    ForEach(0..<100, id:\.self) { index in
                        Text("Row number \(index)").id(index)
                            .padding()
                    }
                }
                              
                Divider()

                Picker("", selection: $selectedIndex) {
                    ForEach(0..<100, id: \.self) { index in
                        Text("Go to Row \(index)")
                    }
                    .onReceive(Just(selectedIndex)) {value in
                        withAnimation {
                            scrollView.scrollTo(
                                selectedIndex, anchor: .center
                            )
                        }
                    }
                }
                .pickerStyle(.wheel)
            }
            .navigationTitle("Scroll To Demo")
            }
        }
    }
}

只需要 30 多行程式碼,我們就實作好需要的功能了。

如你所見,明白了操作的機制之後,整個實作過程其實非常簡單,只需要為要滾動到的項目添加一個 id 就可以了。

謝謝你的閱讀。如果你想看看這篇文章的教學影片,可以到我的 YouTube 頻道觀看:

本篇原文(標題:SwiftUI: How to Programmatically Scroll to a Row)刊登於作者 Medium,由 Alessandro Manilii 所著,並授權翻譯及轉載。
作者簡介:Alessandro Manilii,一位意大利的專業 iOS 開發者,Wakala 的 iOS 技術主管。如果你有興趣閱讀我的其他文章,可以在 Medium 上訂閱。
譯者簡介:Kelly Chan-AppCoda 編輯小姐。

作者
AppCoda 編輯團隊
此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。
評論
更多來自 AppCoda 中文版
iOS 18 新API:使用 Navigation Transition 創建 Hero 動畫式過場
SwiftUI 框架

iOS 18 新API:使用 Navigation Transition 創建 Hero 動畫式過場

Apple 的工程師可能早已認識到,許多 iOS 開發者都希望能夠重現 App Store 應用程式中的優雅 Hero 動畫。由於從頭實現這種動畫通常需要耗費大量時間與精力,Apple 在 iOS 18 SDK 中納入了這項功能。 透過這次更新,你現在只需少量的程式碼就能在自己的應用程式中實現類似的動畫過渡效果。這項重大改進讓開發者能夠創造出更具視覺吸引力且流暢的過渡效果,
如何使用 Vision APIs 從圖像中辨識文字
AI

如何使用 Vision APIs 從圖像中辨識文字

Vision 框架長期以來一直包含文字識別功能。我們已經有詳細的教程,向你展示如何使用 Vision 框架掃描圖像並執行文字識別。之前,我們使用了 VNImageRequestHandler 和 VNRecognizeTextRequest 來從圖像中提取文字。 多年來,Vision 框架已經顯著演變。在 iOS 18 中,Vision
iOS 18更新:SwiftUI 新功能介紹
SwiftUI 框架

iOS 18更新:SwiftUI 新功能介紹

SwiftUI的技術不斷演進,每次更新都讓 iOS 應用程式開發變得更加便捷。隨著 iOS 18 Beta 的推出,SwiftUI 引入了多個令人興奮的新功能,使開發者僅需幾行程式碼即可實現出色的效果。 本教學文章旨在探索這個版本中的幾項主要改進,幫助你了解如何運用這些新功能。 浮動標籤列 (Floating Tab Bar)SwiftUI中的標籤視圖(Tab
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。