如果您有一个SwiftUI列表,该列表允许单选,则可以通过单击列表(大概使它成为按键响应者),然后使用箭头键来更改选择。如果该选择到达可见区域的末尾,它将滚动整个列表以保持选择可见。
但是,如果选择对象以其他某种方式(例如,使用按钮)更新,则列表不会滚动。
以编程方式设置时,是否有任何方法可以强制列表滚动到新选择?
示例应用程序:
import SwiftUI
struct ContentView: View {
@State var selection: Int? = 0
func changeSelection(_ by: Int) {
switch self.selection {
case .none:
self.selection = 0
case .some(let sel):
self.selection = max(min(sel + by, 20), 0)
}
}
var body: some View {
HStack {
List((0...20), selection: $selection) {
Text(String($0))
}
VStack {
Button(action: { self.changeSelection(-1) }) {
Text("Move Up")
}
Button(action: { self.changeSelection(1) }) {
Text("Move Down")
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试了几种解决方案,其中一种是我在我的项目中使用的(我需要对 3 个列表进行水平分页)。这是我的观察:
scrollToRowAtIndexPath方法(就像在这个答案中一样)。正如我所写,这是我的示例,当然,它需要改进。首先 ScrollView 需要在 GeometryReader 内部,你才能了解内容的真实大小。第二件事是你需要控制你的手势,这可能很困难。最后一个:你需要计算 ScrollViews 内容的当前偏移量,它可能不是我的代码(记住,我试图给你举个例子):
struct ScrollListView: View {
@State var selection: Int?
@State private var offset: CGFloat = 0
@State private var isGestureActive: Bool = false
func changeSelection(_ by: Int) {
switch self.selection {
case .none:
self.selection = 0
case .some(let sel):
self.selection = max(min(sel + by, 30), 0)
}
}
var body: some View {
HStack {
GeometryReader { geometry in
VStack {
ScrollView(.vertical, showsIndicators: false) {
ForEach(0...29, id: \.self) { line in
ListRow(line: line, selection: self.$selection)
.frame(height: 20)
}
}
.content.offset(y: self.isGestureActive ? self.offset : geometry.size.height / 4 - CGFloat((self.selection ?? 0) * 20))
.gesture(DragGesture()
.onChanged({ value in
self.isGestureActive = true
self.offset = value.translation.width + -geometry.size.width * CGFloat(self.selection ?? 1)
})
.onEnded({ value in
DispatchQueue.main.async { self.isGestureActive = false }
}))
}
}
VStack {
Button(action: { self.changeSelection(-1) }) {
Text("Move Up")
}
Spacer()
Button(action: { self.changeSelection(1) }) {
Text("Move Down")
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
当然,您需要创建自己的“列表行”:
struct ListRow: View {
@State var line: Int
@Binding var selection: Int?
var body: some View {
HStack(alignment: .center, spacing: 2){
Image(systemName: line == self.selection ? "checkmark.square" : "square")
.padding(.horizontal, 3)
Text(String(line))
Spacer()
}
.onTapGesture {
self.selection = self.selection == self.line ? nil : self.line
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望它会有所帮助。
在适用于 iOs 14 和 MacOs Big Sur 的 SwiftUI 新版本中,他们添加了使用新的 ScrollViewReader 以编程方式滚动到特定单元格的功能:
struct ContentView: View {
let colors: [Color] = [.red, .green, .blue]
var body: some View {
ScrollView {
ScrollViewReader { value in
Button("Jump to #8") {
value.scrollTo(8)
}
ForEach(0..<10) { i in
Text("Example \(i)")
.frame(width: 300, height: 300)
.background(colors[i % colors.count])
.id(i)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
.scrollTo()然后你可以使用这样的方法
value.scrollTo(8, anchor: .top)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
654 次 |
| 最近记录: |