当前位置: 首页 > 知识库问答 >
问题:

ScrollView无法与GeometryReader一起正常工作

阳长恨
2023-03-14

我对scrollview和GeometryReader有一些问题。我想要一张图片下的物品清单。每个项目都应该有以下宽度和高度:
((整个屏幕的宽度-前导填充-尾随填充)/2)

我为我的用例尝试了两种方法。这是我的第一个代码结构:

ScrollView
   - VStack
      - Image
      - GeometryReader
         - ForEach
            - Text

我正在使用几何体读取器来获取VStack的宽度,因为它有一个填充,我不想获得滚动视图的全宽。

但对于GeometryReader,UI上只显示ForEach循环中的最后一项。而GeometryReader只有很小的高度。见截图。

import SwiftUI

struct Item: Identifiable {
    var id = UUID().uuidString
    var value: String
}

struct ContentView: View {

    func items() -> [Item] {
        var items = [Item]()
        for i in 0..<100 {
            items.append(Item(value: "Item #\(i)"))
        }
        return items
    }

    var body: some View {
        ScrollView {
            VStack {
                Image(systemName: "circle.fill")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 150, height: 150)
                    .padding()
                    .foregroundColor(.green)

                GeometryReader { geometry in
                    ForEach(self.items()) { item in
                        Text(item.value)
                            .frame(width: geometry.size.width / CGFloat(2), height: geometry.size.width / CGFloat(2))
                            .background(Color.red)
                    }
                }
                .background(Color.blue)
            }
            .frame(maxWidth: .infinity)
            .padding()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

红色是ForEach循环中的项目。蓝色是GeometryReader,绿色只是图像。



ScrollView
   -GeometryReader
      - VStack
         - Image
         - ForEach
            -Text

然后,我的ForEach循环中的项目被正确呈现,但不能再滚动了。

import SwiftUI

struct Item: Identifiable {
    var id = UUID().uuidString
    var value: String
}

struct ContentView: View {

    func items() -> [Item] {
        var items = [Item]()
        for i in 0..<100 {
            items.append(Item(value: "Item #\(i)"))
        }
        return items
    }

    var body: some View {
        ScrollView {
            GeometryReader { geometry in
                VStack {
                    Image(systemName: "circle.fill")
                        .resizable()
                        .scaledToFill()
                        .frame(width: 150, height: 150)
                        .padding()
                        .foregroundColor(.green)

                    ForEach(self.items()) { item in
                        Text(item.value)
                            .frame(width: geometry.size.width / CGFloat(2), height: geometry.size.width / CGFloat(2))
                            .background(Color.red)
                    }
                }
                .frame(maxWidth: .infinity)
            }
            .padding()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

如何存档以正确显示UI。我是不是错过了什么

我非常感谢您的帮助<谢谢。



我找到了一个解决办法,可以用一个工作的滚动视图正确地呈现UI,但在我看来,这很有问题
我正在使用PreferenceKey来解决这个问题

我正在scrollview中使用高度为0的geometryReader。只是为了得到我的VStack的宽度。在preferenceKeyChange上,我正在更新一个状态变量,并使用它为我的项目设置宽度和高度。

import SwiftUI

struct Item: Identifiable {
    var id = UUID().uuidString
    var value: String
}

struct ContentView: View {
    @State var width: CGFloat = 0

    func items() -> [Item] {
        var items = [Item]()
        for i in 0..<100 {
            items.append(Item(value: "Item #\(i)"))
        }
        return items
    }

    struct WidthPreferenceKey: PreferenceKey {
        typealias Value = [CGFloat]

        static var defaultValue: [CGFloat] = [0]

        static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
            value.append(contentsOf: nextValue())
        }
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 0) {
                Image(systemName: "circle.fill")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 150, height: 150)
                    .padding()
                    .foregroundColor(.green)

                GeometryReader { geometry in
                    VStack {
                        EmptyView()

                    }.preference(key: WidthPreferenceKey.self, value: [geometry.size.width])
                }
                .frame(height: 0)

                ForEach(self.items()) { item in
                    Text(item.value)
                        .frame(width: self.width / CGFloat(2), height: self.width / CGFloat(2))
                        .background(Color.red)
                }
            }
            .padding()
        }
        .onPreferenceChange(WidthPreferenceKey.self) { value in
            self.width = value[0]
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


这是唯一的方法,还是有更优雅、更简单的方法?

共有1个答案

邵逸明
2023-03-14

首先,由于您使用的是VStack,这意味着您需要进行垂直滚动。因此,需要设置maxHeight属性,而不是maxWidth。

在您的第一种方法中,您正在将for...循环包装在GeometryReader中,然后再将其包装在VStack中。因此,SwiftUI直到构建GeometryReader及其子级之前才会识别出您希望将这些项目放在垂直堆栈中。因此,一个好的解决方法是将GeometryReader作为scrollView之前的第一个块以使其工作:

import SwiftUI

struct Item: Identifiable {
    var id = UUID().uuidString
    var value: String
}

struct ContentView: View {

    func items() -> [Item] {
        var items = [Item]()
        for i in 0..<100 {
            items.append(Item(value: "Item #\(i)"))
        }
        return items
    }

    var body: some View {
        GeometryReader { geometry in
        ScrollView {
            VStack {

                Image(systemName: "circle.fill")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 150, height: 150)
                    .padding()
                    .foregroundColor(.green)

                    ForEach(self.items()) { item in
                        Text(item.value)
                            .frame(width: geometry.size.width / CGFloat(2), height: geometry.size.width / CGFloat(2))
                            .background(Color.red)
                    }
            }
            .frame(maxHeight: .infinity)
            .padding()
        }
        }
        .background(Color.blue)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

第二种方法的问题是,将无限高VStack包装在GeometryReader中,它不一定具有无限高。此外,GeometryReader是一个快速的UI块,用于从外部块读取尺寸,而不是从其子块获取尺寸。

一般来说,如果要使用正在制作的当前主要SwiftUI组件的几何图形(或者设备屏幕,如果是全屏组件),请将GeometryReader置于其他组件的父级。否则,需要将GeometryReader组件作为定义良好的维度SwiftUI块的唯一子级。

已编辑:要添加填充,只需在滚动视图中添加所需的填充:

ScrollView{
  //...
}
.padding([.leading,.trailing],geometry.size.width / 8)

或者只在一个方向:

ScrollView{
  //...
}
.padding(leading,geometry.size.width / 8) // or any other value, e.g. : 30

如果只需要为for循环中的项目添加填充,只需将for循环放在另一个VStack中,并在上面添加填充即可。

 类似资料:
  • 问题内容: 在我编写的flask应用程序中,我使用了一个外部库,该库可以使用环境变量进行配置。注意:我自己编写了这个外部库。因此,如有必要,我可以进行更改。从命令行运行时,运行带有以下内容的烧瓶服务器: 一切都如预期。但将它部署到Apache后,用它不工作了。事实上,打印出到(所以它在Apache日志中显示出来显示,该wsgi过程似乎是在一个非常不同的环境(一个,好像是这样了。其实,它指向我的发展

  • 问题内容: 我构建了一个PyQt5 GUI来进行一些selenium测试。除PyQt进度条外,其他所有操作均按预期进行。 在下面的第一个示例中,当我使用Selenium浏览器时,最后,当浏览器关闭时,进度条只会跳到100%。但是,selenium工作正常。 但是,在下面的此版本中,在Selenium浏览器被注释掉的情况下,进度条可以按预期工作。 问题答案: 阻塞任务与在其中执行GUI的事件循环不友

  • 我的片段包含一个viewpager,它包含一个listView 此listView行在左侧显示一个缩略图,在右侧显示一些文本。就像gmail应用一样,当我按下缩略图时,我希望它切换到ActionMode。按下其他位置时,将打开与按下的特定行相关的不同活动。这部分工作正常。 当不在actionMode中时,调用onItemClick,但一旦在actionMode中,就不再调用了... 在操作模式下,

  • 我正在尝试使用Micrometer从我的spring boot应用程序中公开度量。 以下配置由于某种原因不起作用: spring说: 但是对于完全相同的注释配置,它可以很好地工作: 日志说: 完整的示例如下:https://filebin.net/gjxnwwksaqs1x8zg

  • 问题内容: 我想知道为什么没有确定的合作方式。我只想解析字符串: 但是我真的很困惑应该导入什么。根据此链接,我尝试导入。但是我得到这个编译错误: 然后我尝试导入和。因此,没有编译错误,但是我得到了此运行时异常(在mapper定义行中): 请指导我,我应该导入什么才能使用。谢谢 问题答案: 使用这些依赖项 jackson-databind jackson-annotations jackson- c

  • 问题内容: 因此,我的设置无法按我想要的方式工作。因此,每当我运行该程序时,它就会立即从0变为100。我尝试使用,任务,并尝试了,但没有任何尝试。 这是我的程序: @MadProgrammer这是我尝试做一名摆动工作人员并将每个名称写入文档并更新进度栏的尝试。该程序将达到86%左右并停止运行,永远不会创建完成的文档。该程序将创建一个空白文档。这是我首先创建的SwingWorker对象,这是两种方法