问题描述
我是 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,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)
}
}
解决方法
不是一个完美的解决方案,但您可以将标题和 List
分成 ZStack
中的 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,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。