[英]SwiftUI | GeometryReader: Smooth resizable Header when scrolling through List
我是 SwiftUI 的新手,我想从联系人应用程序重新创建联系人卡片视图。 在下面的列表中滚动时,我正在努力调整顶部图像的大小。
我曾尝试使用 GeometryReader,但在那里遇到了问题。 例如,当向上滚动时,图片大小会突然跳到我指定的 minimumPictureSize。 向上滚动时会发生相反的情况:当我停止滚动时,它会突然停止调整大小。
通缉行为: https : //gifyu.com/image/Ai04
当前行为: https : //gifyu.com/image/AjIc
struct SwiftUIView: View {
@State var startOffset: CGFloat = 0
@State var offset: CGFloat = 0
var minPictureSize: CGFloat = 100
var maxPictureSize: CGFloat = 200
var body: some View {
VStack {
Image("person")
.resizable()
.frame(width: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)),
height: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)))
.mask(Circle())
Text("startOffset: \(startOffset)")
Text("offset: \(offset)")
List {
Section {
Text("Top Section")
}.overlay(
GeometryReader(){ geometry -> Color in
let rect = geometry.frame(in: .global)
if startOffset == 0 {
DispatchQueue.main.async {
startOffset = rect.minY
}
}
DispatchQueue.main.async {
offset = rect.minY - startOffset
}
return Color.clear
}
)
ForEach((0..<10)) { row in
Section {
Text("\(row)")
}
}
}.listStyle(InsetGroupedListStyle())
}.navigationBarHidden(true)
}
}
不是一个完美的解决方案,但您可以在ZStack
中将 header 和List
分成 2 层:
struct SwiftUIView: View {
@State var startOffset: CGFloat!
@State var offset: CGFloat = 0
let minPictureSize: CGFloat = 100
let maxPictureSize: CGFloat = 200
var body: some View {
ZStack(alignment: .top) {
if startOffset != nil {
List {
Section {
Text("Top Section")
} header: {
// Leave extra space for `List` so it won't clip its content
Color.clear.frame(height: 100)
}
.overlay {
GeometryReader { geometry -> Color in
DispatchQueue.main.async {
let frame = geometry.frame(in: .global)
offset = frame.minY - startOffset
}
return Color.clear
}
}
ForEach((0..<10)) { row in
Section {
Text("\(row)")
}
}
}
.listStyle(InsetGroupedListStyle())
.padding(.top, startOffset-100) // Make up extra space
}
VStack {
Circle().fill(.secondary)
.frame(width: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)),
height: max(minPictureSize, min(maxPictureSize, minPictureSize + offset)))
Text("startOffset: \(startOffset ?? -1)")
Text("offset: \(offset)")
}
.frame(maxWidth: .infinity)
.padding(.bottom, 20)
.background(Color(uiColor: UIColor.systemBackground))
.overlay {
if startOffset == nil {
GeometryReader { geometry -> Color in
DispatchQueue.main.async {
let frame = geometry.frame(in: .global)
startOffset = frame.maxY + // Original small one
maxPictureSize - minPictureSize -
frame.minY // Top safe area height
}
return Color.clear
}
}
}
}
.navigationBarHidden(true)
}
}
请注意, Color.clear.frame(height: 100)
和.padding(.top, startOffset-100)
旨在为List
留出额外的空间,以避免被剪裁,这将导致滚动条被剪裁。 或者, UIScrollView.appearance().clipsToBounds = true
将起作用。 但是,它会使移动到List
边界之外的元素消失。 不知道是不是bug。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.