【问题标题】:ForEach and GeometryReader: variable height for children?ForEach 和 GeometryReader:儿童的可变高度?
【发布时间】:2020-10-03 03:08:42
【问题描述】:

我有以下例子:

import SwiftUI

struct TestSO: View {

    @State var cards = [
        Card(title: "short title text", subtitle: "short title example"),
        Card(title: "medium title text text text text text", subtitle: "medium title example"),
        Card(title: "long title text text text text text text text text text text text text text text text text text",
         subtitle: "long title example"),
        Card(title: "medium title text text text text text", subtitle: "medium title example"),
        Card(title: "short title text", subtitle: "short title example"),
    ]

    @State var showDetails = false

    var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    ForEach(cards.indices) { index in
                        GeometryReader { reader in
                            CardView(showDetails: self.$showDetails, card: self.cards[index])
                                .offset(y: self.showDetails ? -reader.frame(in: .global).minY : 0)
                                .onTapGesture {
                                    self.showDetails.toggle()
                                    self.cards[index].showDetails.toggle()
                            }
                        }.frame(height: self.showDetails ? UIScreen.main.bounds.height : 80, alignment: .center)
                    }
                }
            }.navigationBarTitle("Content", displayMode: .large)
        }
    }
}

struct CardView : View {

    @Binding var showDetails : Bool

    var card : Card

    var body: some View {
        VStack(alignment: .leading){
            HStack{
                Text(card.subtitle).padding([.horizontal, .top]).fixedSize(horizontal: false, vertical: true)
                Spacer()
            }
            Text(card.title).fontWeight(Font.Weight.bold).padding([.horizontal, .bottom]).fixedSize(horizontal: false, vertical: true)
            if(card.showDetails && showDetails) {
                Spacer()
            }
        }
        .background(Color.white)
        .cornerRadius(16)
        .shadow(radius: 12)
        .padding()
        .opacity(showDetails && card.showDetails ? 1 : (!showDetails ? 1 : 0))
    }
}

struct Card : Identifiable{
    var id = UUID()
    var title : String
    var subtitle : String
    var showDetails : Bool = false
}

这是一张卡片列表,如果用户点击它就会展开。这里的问题是.frame(height: self.showDetails ? UIScreen.main.bounds.height : 80, alignment: .center) 行。根据 Card-Object 的标题或副标题有多少文本,CardView 必须小于或大于 80。我需要计算高度并使用它而不是固定的 80。

外观:

知道如何为 CardView 子级使用具有可变高度的 GeometryReader 吗?

提前致谢!

【问题讨论】:

  • 如果 Card 正确计算自己的尺寸,为什么还需要 GeometryReader?看起来你引入了没有的问题,而不是解决真正的问题。
  • 老实说,这比我们想象的要困难得多(至少对我来说:D)Apple 限制预览中的文本长度是有原因的。特别是在我们介绍了GeomtryReader 之后,我知道我们需要它,但我们需要它来扩展视图,所以除非有更好的方法来扩展视图,否则我认为这是一项相当艰巨的任务(再次,至少对我来说)。跨度>
  • 好的,我发现问题出在使用的硬代码上(这里和那里),但我不明白你想得到什么。哪个是正确的预期行为、外观和感觉?

标签: foreach swiftui geometryreader


【解决方案1】:

最后,我想重新创建应用商店的扩展卡片视图:imgur.com/a/1Jd4bI5。我已经为此发布了另一个 Stackoverflow 问题:stackoverflow.com/questions/62331530/...。除了尺寸不同的卡片之外,一切都可以正常工作。

好的,我使用该已接受帖子中的代码作为入口点(正如您所说,除了不同的高度支持之外它满足您)

因此,这是一个使用视图首选项支持该代码中不同高度单元格的解决方案。

使用 Xcode 12b 测试(但我没有使用 SwiftUI2 功能,以防万一)。

仅更改部分:

struct ContentView: View {
    @State var selectedForDetail : Post?
    @State var showDetails: Bool = false

    // Posts need to be @State so changes can be observed
    @State var posts = [
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor..."),
        Post(subtitle: "test1", title: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor", extra: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor..."),
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor..."),
        Post(subtitle: "test1", title: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis", extra: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis..."),
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor...")
    ]

    @State private var heights = [Int: CGFloat]()   // store heights in one update
    var body: some View {
        ScrollView {
            VStack {
                ForEach(self.posts.indices) { index in
                    GeometryReader { reader in
                        PostView(post: self.$posts[index], isDetailed: self.$showDetails)
                            .fixedSize(horizontal: false, vertical: !self.posts[index].showDetails)
                            .background(GeometryReader {
                                Color.clear
                                    .preference(key: ViewHeightKey.self, value: $0.frame(in: .local).size.height)
                            })
                            .offset(y: self.posts[index].showDetails ? -reader.frame(in: .global).minY : 0)
                            .onTapGesture {
                                if !self.posts[index].showDetails {
                                    self.posts[index].showDetails.toggle()
                                    self.showDetails.toggle()
                                }
                            }
                            // Change this animation to what you please, or change the numbers around. It's just a preference.
                            .animation(.spring(response: 0.6, dampingFraction: 0.6, blendDuration: 0))
                            // If there is one view expanded then hide all other views that are not
                            .opacity(self.showDetails ? (self.posts[index].showDetails ? 1 : 0) : 1)
                    }
                    .frame(height: self.posts[index].showDetails ? UIScreen.main.bounds.height : self.heights[index], alignment: .center)
                    .onPreferenceChange(ViewHeightKey.self) { value in
                        self.heights[index] = value
                    }
                    .simultaneousGesture(
                        // 500 will disable ScrollView effect
                        DragGesture(minimumDistance: self.posts[index].showDetails ? 0 : 500)
                    )
                }
            }
        }
    }
}

struct ViewHeightKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

【讨论】:

    猜你喜欢
    • 2017-02-02
    • 2012-05-25
    • 1970-01-01
    • 2016-06-17
    • 2019-10-30
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    • 2019-01-29
    相关资源
    最近更新 更多