简体   繁体   English

ScrollView 或 List,如何制作特定的可滚动视图 (SwiftUI)

[英]ScrollView or List, How to make a particular scrollable view (SwiftUI)

So I am trying to make this List or ScrollView in SwiftUI (not fully iOS 14 ready yet, I have to use the old api, so I can't use ScrollViewReader or other fantasy from iOS 14+ SwiftUI api). So I am trying to make this List or ScrollView in SwiftUI (not fully iOS 14 ready yet, I have to use the old api, so I can't use ScrollViewReader or other fantasy from iOS 14+ SwiftUI api). The goal is to have only a specific number of rows visible on the view and be able to center the last one.目标是在视图中仅显示特定数量的行,并能够将最后一行居中。

Images always make it easier to explain.图像总是更容易解释。 So it should look somewhat like this.所以它应该看起来像这样。 在此处输入图像描述

the first two rows have a specific color applied to them, the middle one also but has to be center vertically.前两行应用了特定的颜色,中间的行也是,但必须垂直居中。 then there are eventually 2 more rows under but they are invisible at the moment and will be visible by scrolling down and make them appear.然后最终还有2行,但它们目前是不可见的,并且通过向下滚动并使它们出现可见。

The closest example i have of this, is the Apple Music Lyrics UI/UX if you are familiar with it.我最接近的例子是 Apple Music Lyrics UI/UX,如果你熟悉的话。

I am not sure how to approach this here.我不知道如何在这里解决这个问题。 I thought about create a List that will have a Text with a frame height defined by the height of the view divided by 5. But then I am not sure how to define a specific color for each row depending of which one is it in the view at the moment.我考虑过创建一个List ,该Text的框架高度由视图高度除以 5 定义。但是我不确定如何为每一行定义特定颜色,具体取决于视图中的哪一个眼下。

Also, It would be preferable if I can just center the selected row and let the other row have their own sizes without setting one arbitrary.此外,如果我可以将所选行居中并让另一行有自己的大小而不设置任意大小,那将是更好的选择。

Banging my head on the walls atm, any help is welcomed.将我的头撞在墙上的自动取款机上,欢迎任何帮助。 thank you guys.感谢你们。

Edit 1: Screenshot added as a result from @nicksarno answer.编辑 1:由于@nicksarno 的回答而添加了屏幕截图。 Definitely looking good.绝对好看。 The only noted issue atm is that it highlights multiple row with the same color instead of one.唯一注意到的问题 atm 是它突出显示具有相同颜色而不是一个颜色的多行。 在此处输入图像描述

Edit 2: I did some adjustment to the code @nicksarno published.编辑2:我对@nicksarno 发布的代码做了一些调整。

        let middleScreenPosition = geometryProxy.size.height / 2

        return ScrollView {
            VStack(alignment: .leading, spacing: 20) {
                Spacer()
                    .frame(height: geometryProxy.size.height * 0.4)
                ForEach(playerViewModel.flowReaderFragments, id: \.id) { text in
                    Text(text.content) // Outside of geometry ready to set the natural size
                        .opacity(0)
                        .overlay(
                            GeometryReader { geo in
                                let midY = geo.frame(in: .global).midY

                                Text(text.content) // Actual text
                                    .font(.headline)
                                    .foregroundColor( // Text color
                                        midY > (middleScreenPosition - geo.size.height / 2) && midY < (middleScreenPosition + geo.size.height / 2) ? .white :
                                            midY < (middleScreenPosition - geo.size.height / 2) ? .gray :
                                            .gray
                                    )
                                    .colorMultiply( // Animates better than .foregroundColor animation
                                        midY > (middleScreenPosition - geo.size.height/2) && midY < (middleScreenPosition + geo.size.height/2) ? .white :
                                            midY < (middleScreenPosition - geo.size.height/2) ? .gray :
                                            .clear
                                    )
                                    .animation(.easeInOut)
                            }
                        )
                }
                Spacer()
                    .frame(height: geometryProxy.size.height * 0.4)

            }
            .frame(maxWidth: .infinity)
            .padding()
        }
        .background(
            Color.black
                .edgesIgnoringSafeArea(.all)
        )

Now it is closer to what I am looking for.现在它更接近我正在寻找的东西。 Also, I added some Spacer (not dynamic yet) that will allow to center the text if needed in the middle.另外,我添加了一些Spacer (还不是动态的),如果需要的话,可以让文本居中。 Still need to figure how to put the text in the middle when selected one of the element. Still need to figure how to put the text in the middle when selected one of the element.

在此处输入图像描述

Here's how I would do it.这就是我将如何做到的。 First, add a GeometryReader to get the size of the entire screen (screenGeometry).首先,添加一个 GeometryReader 来获取整个屏幕的大小(screenGeometry)。 Using that, you can set up the boundaries for your text modifiers (upper/lower).使用它,您可以设置文本修饰符的边界(上/下)。 Finally, add a GeoemteryReader behind each of the Text() and (here's the magic) you can use the.frame(in: global) function to find its current location in the global coordinate space.最后,在每个 Text() 后面添加一个 GeoemteryReader 并且(这就是魔法)您可以使用 .frame(in: global) function 在全局坐标空间中找到它的当前位置。 We can then compare the frame coordinates to our upper/lower boundaries.然后我们可以将帧坐标与我们的上/下边界进行比较。

A couple notes:几点注意事项:

  • The current boundaries are at 40% and 60% of the screen.当前边界在屏幕的 40% 和 60% 处。
  • I'm currently using the.midY coordinate of the Text geometry, but you can also use.minY or.maxY to get more exact.我目前正在使用 Text 几何的 .midY 坐标,但您也可以使用 .minY 或 .maxY 来获得更准确的信息。
  • The natural behavior of a GeometryReader sizes to the smallest size to fit, so to keep the natural Text sizes, I added a first Text() with 0% opacity (for the frame) and then added the GeometryReader + the actual Text() as an overlay. GeometryReader 的自然行为大小调整为适合的最小大小,因此为了保持自然的文本大小,我添加了第一个不透明度为 0% 的 Text()(对于框架),然后添加了 GeometryReader + 实际的 Text() 作为叠加层。
  • Lastly, the.foregroundColor doesn't really animate, so I also added a.colorMultiply on top, which does animate.最后,the.foregroundColor 并没有真正的动画,所以我还在顶部添加了 a.colorMultiply,它确实动画。

Code:代码:

 struct ScrollViewPlayground: View {
        
        let texts: [String] = [
            "So I am trying to make this List or ScrollView in SwiftUI (not fully iOS 14 ready yet, I have to use the old api, so I can't use ScrollViewReader or other fantasy from iOS 14+ SwiftUI api). The goal is to have only a specific number of rows visible on the view and be able to center the last one.",
            "Images always make it easier to explain. So it should look somewhat like this.",
            "the first two rows have a specific color applied to them, the middle one also but has to be center vertically. then there are eventually 2 more rows under but they are invisible at the moment and will be visible by scrolling down and make them appear.",
            "The closest example i have of this, is the Apple Music Lyrics UI/UX if you are familiar with it.",
            "I am not sure how to approach this here. I thought about create a List that will have a Text with a frame height defined by the height of the view divided by 5. But then I am not sure how to define a specific color for each row depending of which one is it in the view at the moment.",
            "Also, It would be preferable if I can just center the selected row and let the other row have their own sizes without setting one arbitrary.",
            "Banging my head on the walls atm, any help is welcomed! thank you guys."
        ]
        
        var body: some View {
            GeometryReader { screenGeometry in
                let lowerBoundary = screenGeometry.size.height * 0.4
                let upperBoundary = screenGeometry.size.height * (1.0 - 0.4)
                
                ScrollView {
                    VStack(alignment: .leading, spacing: 20) {
                        ForEach(texts, id: \.self) { text in
                            Text(text) // Outside of geometry ready to set the natural size
                                .opacity(0)
                                .overlay(
                                    GeometryReader { geo in
                                        let midY = geo.frame(in: .global).midY
    
                                        Text(text) // Actual text
                                            .font(.headline)
                                            .foregroundColor( // Text color
                                                midY > lowerBoundary && midY < upperBoundary ? .white :
                                                midY < lowerBoundary ? .gray :
                                                .gray
                                            )
                                            .colorMultiply( // Animates better than .foregroundColor animation
                                                midY > lowerBoundary && midY < upperBoundary ? .white :
                                                midY < lowerBoundary ? .gray :
                                                .clear
                                            )
                                            .animation(.easeInOut)
                                    }
                                )
                        }
    
                    }
                    .frame(maxWidth: .infinity)
                    .padding()
                }
                .background(
                    Color.black
                        .edgesIgnoringSafeArea(.all)
                )
            }
        }
    }
    
    struct ScrollViewPlayground_Previews: PreviewProvider {
        static var previews: some View {
            ScrollViewPlayground()
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM