简体   繁体   English

SwiftUI 具有基于内容的 PageTabViewStyle 动态高度的 TabView

[英]SwiftUI TabView with PageTabViewStyle dynamic height based on Content

How do I make my SwiftUI TabView with a PageTabViewStyle adjust its height to the height of the content?如何使带有TabViewPageTabViewStyle将其高度调整为内容的高度?

I have a SwiftUI view like follows:我有一个SwiftUI视图,如下所示:

struct TabViewDynamicHeight: View {
    var body: some View {
        VStack {
            TabView {
                ForEach(0..<5, id: \.self) { index in
                    VStack {
                        Text("Text \(index)")
                        Text("Text \(index)")
                        Text("Text \(index)")
                    }
                }
            }
            .tabViewStyle(PageTabViewStyle())
            .background(Color.red)
            .fixedSize(horizontal: false, vertical: true)
        }
        .background(Color.blue)
    }
}

This produces an output like this:这会产生如下所示的 output:

在此处输入图像描述

You can see, that the content of the TabView is cut off.可以看到, TabView的内容被截断了。 I'm aware, that I can remove .fixedSize , but than the view looks like this:我知道,我可以删除.fixedSize ,但视图看起来像这样:

在此处输入图像描述

I would like the TabView to respond to the height of the content.我希望 TabView 响应内容的高度。 Any ideas on how to achieve that?关于如何实现这一目标的任何想法?

A possible approach is to fetch content rect dynamically in run-time and transfer to parent via view prefs, so parent can set it as frame to fit content.一种可能的方法是在运行时动态获取内容 rect 并通过视图首选项传输给父级,因此父级可以将其设置为适合内容的框架。

Tested with Xcode 13.3 / iOS 15.4用 Xcode 13.3 / iOS 15.4 测试

演示

Here is main part:这是主要部分:

VStack {
    Text("Text \(index)")
    Text("Text \(index)")
    Text("Text \(index)")
}
.frame(maxWidth: .infinity)
.background(GeometryReader {
    Color.clear.preference(key: ViewRectKey.self,
                                  value: [$0.frame(in: .local)])
})

// ...

.frame(height: rect.size.height
         + 60 /* just to avoid page indicator overlap */)
.onPreferenceChange(ViewRectKey.self) { rects in
    self.rect = rects.first ?? .zero
}

Complete test code in project is here 项目中完整的测试代码在这里

I took inspiration from Asperi' answer, however I had to modify some things to make it work in my case.我从 Asperi 的回答中获得了灵感,但是我必须修改一些东西才能使其适用于我的情况。

On the 'Content' of the TabView, I added these:在 TabView 的“内容”中,我添加了这些:

.overlay(GeometryReader { proxy in
   Color.clear.preference(key: ViewRectKey.self, value: proxy.size)
 })
 .onPreferenceChange(ViewRectKey.self) { size in
    if self.viewPagerSize.height == .zero {
      self.viewPagerSize = size
    }
 }

Where viewPagerSize is initially:其中 viewPagerSize 最初是:

@State var viewPagerSize: CGSize = .zero

My PreferenceKey looks like this:我的 PreferenceKey 看起来像这样:

struct ViewRectKey: PreferenceKey {
   static let defaultValue: CGSize = .zero
   static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
      value = nextValue()
   }
}

In total the code looks like this:总的来说,代码如下所示:

@ViewBuilder
func createViewPager() -> some View {
    TabView {
        ForEach(views(), id: \.id) { _ in
            createInformationSquareView()
                .background(Color(.systemGray6))
                .overlay(GeometryReader { proxy in
                    Color.clear.preference(key: ViewRectKey.self, value: proxy.size)
                })
                .onPreferenceChange(ViewRectKey.self) { size in
                    if self.viewPagerSize.height == .zero {
                        self.viewPagerSize = size
                    }
                }
        }
    }
    .cornerRadius(10)
    .frame(height: self.viewPagerSize.height + 60)
    .tabViewStyle(.page)
    .indexViewStyle(.page(backgroundDisplayMode: .always))
    .padding()
}

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

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