【发布时间】:2020-07-05 16:29:56
【问题描述】:
我有以下代码来显示一些卡片,如果用户点击其中一个卡片会展开为全屏:
import SwiftUI
struct ContentView: View {
@State private var cards = [
Card(title: "testA", subtitle: "subtitleA"),
Card(title: "testB", subtitle: "subtitleB"),
Card(title: "testC", subtitle: "subtitleC"),
Card(title: "testD", subtitle: "subtitleD"),
Card(title: "testE", subtitle: "subtitleE")
]
@State private var showDetails: Bool = false
@State private var heights = [Int: CGFloat]()
var body: some View {
ScrollView {
VStack {
if(!cards.isEmpty) {
ForEach(self.cards.indices, id: \.self) { index in
GeometryReader { reader in
CardView(card: self.$cards[index], isDetailed: self.$showDetails)
.offset(y: self.cards[index].showDetails ? -reader.frame(in: .global).minY : 0)
.fixedSize(horizontal: false, vertical: !self.cards[index].showDetails)
.background(GeometryReader {
Color.clear
.preference(key: ViewHeightKey.self, value: $0.frame(in: .local).size.height)
})
.onTapGesture {
withAnimation(.spring()) {
self.cards[index].showDetails.toggle()
self.showDetails.toggle()
}
}
}
.frame(height: self.cards[index].showDetails ? UIScreen.main.bounds.height : self.heights[index], alignment: .center)
.onPreferenceChange(ViewHeightKey.self) { value in
self.heights[index] = value
}
}
} else {
ActivityIndicator(style: UIActivityIndicatorView.Style.medium).frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 2)
}
}
}
.onAppear() {
// load data
}
}
}
struct CardView : View {
@Binding var card : Card
@Binding var isDetailed : Bool
var body: some View {
VStack(alignment: .leading){
ScrollView(showsIndicators: isDetailed && card.showDetails) {
HStack (alignment: .center){
VStack (alignment: .leading){
HStack(alignment: .top){
Text(card.subtitle).foregroundColor(Color.gray)
Spacer()
}
Text(card.title).fontWeight(Font.Weight.bold).fixedSize(horizontal: false, vertical: true)
}
}
.padding([.top, .horizontal]).padding(isDetailed && card.showDetails ? [.top] : [] , 34)
Image("placeholder-image").resizable().scaledToFit().frame(width: UIScreen.main.bounds.width - 60).padding(.bottom)
if isDetailed && card.showDetails {
Text("Lorem ipsum ... ")
}
}
}
.background(Color.green)
.cornerRadius(16)
.shadow(radius: 12)
.padding(isDetailed && card.showDetails ? [] : [.top, .horizontal])
.opacity(isDetailed && card.showDetails ? 1 : (!isDetailed ? 1 : 0))
.edgesIgnoringSafeArea(.all)
}
}
struct Card : Identifiable {
public var id = UUID()
public var title: String
public var subtitle : String
public var showDetails : Bool = false
}
struct ViewHeightKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
现在我想将 ContentView 和 CardView 中的两个(或至少一个)滚动视图更改为列表,因为延迟加载以获得更好的性能。但是更改 ContentView 中的 ScrollView 会导致动画出现故障。如果我将其更改为 CardView 中的列表,则不再显示任何卡片。
知道如何更改代码以使用列表吗?
【问题讨论】:
-
你能发帖
ViewHeightKey,这样我就能得到你的代码。不过,如果您使用列表并枚举卡片,解决方案应该很简单,但请记住使用自定义绑定。 -
测试了将 ScrollView 更改为 List 并再次返回,我仍然没有看到任何故障,您能否在故障动画旁边重新编码预期的动画
-
你指的是像这样的缩放卡片imgur.com/a/f2T41Uy
-
@cyden 您是否将闪烁称为故障?还是当您点击一个项目时它会比前一个项目增长时,列表类型会滚动?我只需将
ScrollView替换为List并将.listStyle(PlainListStyle)和.listRowInsets(.init(top:0,leading:0,bottom:0,trailing:0)添加到第一个vstack 即可将其转换为列表。此外,您不需要将卡片的滚动视图更改为列表,因为它没有加载任何内容。 -
将
.delay(1)添加到.spring()动画时可以看到故障。如果我错了,请纠正我,如果那不是故障。
标签: foreach swiftui lazy-loading swiftui-list