根据 SwiftUI 中的最大内容高度动态设置水平滚动视图高度

问题描述 投票:0回答:1

我正在尝试使用 SwiftUI ScrollView 创建水平轮播

这就是我到目前为止所做的:

struct CarouselView<Content: View>: View {
    let content: Content
    @State private var currentIndex = 0
    @State private var contentSize: CGSize = .zero
    
    private var showsIndicators: Bool
    private var spacing: CGFloat
    private var offset: CGFloat
    private var shouldSnap: Bool
    
    init(showsIndicators: Bool = true,
         spacing: CGFloat = 0,
         offset: CGFloat = 0,
         shouldSnap: Bool = false,
         @ViewBuilder content: @escaping () -> Content) {
        self.content = content()
        self.showsIndicators = showsIndicators
        self.spacing = spacing
        self.offset = offset
        self.shouldSnap = shouldSnap
    }
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: showsIndicators) {
            LazyHStack(spacing: spacing) {
                content
                    .offset(x: offset)
            }.apply {
                if #available(iOS 17.0, *), shouldSnap {
                    $0.scrollTargetLayout()
                } else {
                    $0
                }
            }
            
        }
        .apply {
            if #available(iOS 17.0, *), shouldSnap {
                $0.scrollTargetBehavior(.viewAligned)
            } else {
                $0
            }
        }
    }
}

extension View {
    func apply<V: View>(@ViewBuilder _ block: (Self) -> V) -> V { block(self) }
}

然后我按如下方式使用它:

struct ContentView: View {
    
    let imagesNames: [String] = ["img-1", "img-2", "img-3"]
    
    var body: some View {
        VStack(spacing: 10, content: {
            pagingCarousel
            continuousCarousel
        })
    }
    
    private var pagingCarousel: some View {
        CarouselView(showsIndicators: false,
                     spacing: 10,
                     shouldSnap: true) {
            
            ForEach(imagesNames, id: \.self) { image in
                createTile(with: image)
            }
        }
    }
    
    private var continuousCarousel: some View {
        CarouselView(showsIndicators: true,
                     spacing: 20,
                     offset: 20) {
            
            ForEach(imagesNames, id: \.self) { image in
                createTile(with: image)
            }
        }
    }
    
    private func createTile(with image: String) -> some View {
        ZStack {
            Image(image)
                .resizable()
                .scaledToFill()
                .overlay(Color.black.opacity(0.5))
            
            Text("Headline")
                .bold()
                .foregroundStyle(.white)
            
        }
        .frame(height: 200)
        .cornerRadius(30)
        .clipped()
    }
}

功能方面,这很好用,给了我这个:

SwiftUI Carousel ScrollView LazyHStack

我目前遇到的问题是,我希望滚动视图仅占用与其内容相对于其高度一样多的空间。

虽然上面的示例中不太清楚,但如果我在轮播视图中添加背景,您可以看到占用了多少额外空间。

所以现在更新:

var body: some View {
    VStack(spacing: 10, content: {
        pagingCarousel
            .background(.yellow)
        
        continuousCarousel
            .background(.green)
    })
}

给了我这个:

SwiftUI ScrollView HStack LazyHStack Carousel Geometry Reader

如何强制滚动视图根据内容的高度设置其高度?

我尝试了其中一些解决方案,但它只是导致我的内容消失:

  1. 解决方案1
  2. 解决方案2
ios swift swiftui swiftui-scrollview
1个回答
0
投票

这是因为

LazyHStack
被设计为延迟绘制其视图。它不会尝试将所有东西都布置好并找到它需要的最大高度(这根本不是懒惰的)。

因此,在这种情况下,它会尝试填充所有可用空间。 在其他情况下,需要使用其理想大小时,它将仅使用堆栈中的第一个视图来计算其理想大小。

在您的玩具示例中,所有图像的高度均为 200,因此通过使用

.frame(height: 200)
设置轮播的高度来解决此问题很简单。一般来说,您需要一些其他方法来找出最大高度,并执行类似
.frame(height: findMaxHeight(data))
的操作。例如,图像可能来自 API 调用,该调用也恰好返回图像的大小。然后您可以使用该信息来找到最大高度,而无需绘制所有视图。

如果除了绘制视图之外没有办法知道最大高度,则应该使用非惰性

HStack
来代替。

© www.soinside.com 2019 - 2024. All rights reserved.