将SwiftUI列表滚动到新选择 [英] Scroll SwiftUI List to new selection

查看:277
本文介绍了将SwiftUI列表滚动到新选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您有一个SwiftUI列表,该列表允许单选,则可以通过单击列表(大概使它成为按键响应者),然后使用箭头键来更改选择.如果该选择到达可见区域的末尾,它将滚动整个列表以使选择保持可见.

If you have a SwiftUI List with that allows single selection, you can change the selection by clicking the list (presumably this makes it the key responder) and then using the arrow keys. If that selection reaches the end of the visible area, it will scroll the whole list to keep the selection visible.

但是,如果以其他方式(例如使用按钮)更新了选择对象,则列表不会滚动.

However, if the selection object is updated in some other way (e.g. using a button), the list will not be scrolled.

以编程方式设置时,是否有任何方法可以强制列表滚动到新选择?

Is there any way to force the list to scroll to the new selection when set programmatically?

示例应用程序:

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")
                }
            }
        }
    }
}

推荐答案

我尝试了几种解决方案,其中一种是我在项目中使用的解决方案(我需要对3个列表进行水平分页).这是我的观察结果:

I tried several solutions, one of them I'm using in my project (I need horizontal paging for 3 lists). And here are my observations:

  1. 在SwiftUI中我没有找到任何滚动列表的方法,文档中还没有提及它;
  2. 您可以尝试使用ScrollView(下面是我的变体,此处是其他解决方案),但是这些东西看起来像是类似动物;
  3. 也许最好的方法是使用UITableView:Apple的教程并尝试scrollToRowAtIndexPath方法(例如答案).
  1. I didn't find any methods to scroll List in SwiftUI, there is no mention about it in documentation yet;
  2. You may try ScrollView (my variant below, here is other solution), but these things might look monstroid;
  3. Maybe the best way is to use UITableView: tutorial from Apple and try scrollToRowAtIndexPath method (like in this answer).

正如我所写的,这是我的示例,当然,这需要改进.首先,ScrollView必须位于GeometryReader中,您才能了解内容的实际大小.第二件事是您需要控制手势,这可能很困难.最后一个:您需要计算ScrollViews内容的当前偏移量,它可能不是我的代码中的值(请记住,我试图给您提供示例):

As I wrote, here is my example, which, of course, requires refinement. First of all ScrollView needs to be inside GeometryReader and you can understand the real size of content. The second thing is that you need to control your gestures, which might be difficult. And the last one: you need to calculate current offset of ScrollViews's content and it could be other than in my code (remember, I tried to give you example):

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")
                }
            }
        }
    }
}

当然,您需要创建自己的列表行":

of course you need to create your own "list row":

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
        }

    }

}

希望会有所帮助.

这篇关于将SwiftUI列表滚动到新选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆