I have following example:
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
}
It's a list of cards which expand if the user taps on it. The problem here is the .frame(height: self.showDetails ? UIScreen.main.bounds.height : 80, alignment: .center)
line. Depending on how much text a Card-Object has for its title or subtitle, the CardView has to be smaller or larger than 80. I need to calculate the height and use that instead of the fixed 80.
How it looks:
Any idea how I can use the GeometryReader with a variable height for the CardView children?
Thanks in advance!
Ultimately, I want to recreate the expanded card view of the app store: imgur.com/a/1Jd4bI5. I already posted an other Stackoverflow question for this: stackoverflow.com/questions/62331530/…. Everything works except having cards with differenz sizes.
Ok, I used code from that accepted post as entry point (as you said it satisfies you except different height support)
So here is a solution to support different height cells in that code using view preferences.
Tested with Xcode 12b (however I did not use SwiftUI2 features, just in case).
Only changed part:
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()
}
}
backup
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With