如何防止我的 SwiftUI 视图在滚动时调整大小
How can I prevent my SwiftUI view resizing on scroll
我希望有人能给我指出正确的方向——我一直在试验 SwiftUI,我创建了一个有点类似于 Twitter 个人资料的视图 UI。
我好像在header崩溃的时候有一个奇怪的效果
有一个“最有效点”,其中 header 折叠并且标签栏在滚动视图包含在 header 下滚动之前稍微调整大小。
您可以在this video.
中看到
我根本不希望选项卡段调整大小,相反,一旦 header 折叠并且滚动视图应在其下方自由移动,它应该保持原位。
我确定我遗漏了一些明显的东西,但是一些新鲜的眼睛可能正是我需要的帮助。
如有任何想法,我们将不胜感激!
import SwiftUI
struct ContentView: View {
@State private var safeArea: EdgeInsets = EdgeInsets(.zero)
@State private var offset: CGFloat = 0
@State private var tabBarOffset: CGFloat = 0
@State var currentTab = "Tab #1"
@Namespace var animation
var body: some View {
GeometryReader { foo in
ScrollView {
VStack(spacing: 0) {
// Header
GeometryReader { proxy -> AnyView in
// Sticky Header
DispatchQueue.main.async {
offset = proxy.frame(in: .global).minY
safeArea = foo.safeAreaInsets
}
return AnyView(makeHeader())
}
.frame(height: 180)
.zIndex(1)
// Profile
VStack(spacing: 0) {
VStack(spacing: 0) {
HStack(spacing: 0) {
TabButton(title: "Tab #1", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
TabButton(title: "Tab #2", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
TabButton(title: "Tab #3", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
}
Divider()
}
.padding(.top, 20)
.background(Color.white)
.offset(y: tabBarOffset < 90 ? -tabBarOffset + 90 : 0)
.overlay(
GeometryReader { proxy -> Color in
DispatchQueue.main.async {
tabBarOffset = proxy.frame(in: .global).minY
}
return Color.clear
}
.frame(width: 0, height: 0),
alignment: .top
)
.zIndex(1)
VStack {
ForEach((0..<50)) {
Text("Row #\([=10=])")
Divider()
}
}
.padding(.top)
.zIndex(0)
}
}
}
.ignoresSafeArea(.all, edges: .top)
}
}
@ViewBuilder func makeHeader() -> some View {
ZStack {
// Cover
Color.gray
.frame(maxWidth: .infinity)
.frame(height: offset > 0 ? 180 + offset : 180)
}
.clipped()
// Stretchy Effect
.frame(height: offset > 0 ? 180 + offset : nil)
.offset(y: offset > 0 ? -offset : -offset < 80 ? 0 : -offset - 80)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct TabButton: View {
var title: String
@Binding var currentTab: String
var animation: Namespace.ID
var body: some View {
Button(
action: {
withAnimation { currentTab = title }
},
label: {
LazyVStack(spacing: 12) {
Text(title)
.fontWeight(.semibold)
.foregroundColor(currentTab == title ? .blue : .gray)
.padding(.horizontal)
if currentTab == title {
Capsule()
.fill(Color.blue)
.frame(height: 1.2)
.matchedGeometryEffect(id: "TAB", in: animation)
} else {
Capsule()
.fill(Color.clear)
.frame(height: 1.2)
}
}
}
)
}
}
在这里,设置 90 而不是 80。因为你的设置顶部标签栏视图 y 偏移 90 并且你的整个逻辑基于 90 值。
@ViewBuilder func makeHeader() -> some View {
ZStack {
// Cover
Color.red
.frame(maxWidth: .infinity)
.frame(height: offset > 0 ? 180 + offset : 180)
}
.clipped()
// Stretchy Effect
.frame(height: offset > 0 ? 180 + offset : nil)
.offset(y: offset > 0 ? -offset : -offset < 90 ? 0 : -offset - 90) //<=== Here
}
我希望有人能给我指出正确的方向——我一直在试验 SwiftUI,我创建了一个有点类似于 Twitter 个人资料的视图 UI。
我好像在header崩溃的时候有一个奇怪的效果
有一个“最有效点”,其中 header 折叠并且标签栏在滚动视图包含在 header 下滚动之前稍微调整大小。
您可以在this video.
中看到我根本不希望选项卡段调整大小,相反,一旦 header 折叠并且滚动视图应在其下方自由移动,它应该保持原位。
我确定我遗漏了一些明显的东西,但是一些新鲜的眼睛可能正是我需要的帮助。
如有任何想法,我们将不胜感激!
import SwiftUI
struct ContentView: View {
@State private var safeArea: EdgeInsets = EdgeInsets(.zero)
@State private var offset: CGFloat = 0
@State private var tabBarOffset: CGFloat = 0
@State var currentTab = "Tab #1"
@Namespace var animation
var body: some View {
GeometryReader { foo in
ScrollView {
VStack(spacing: 0) {
// Header
GeometryReader { proxy -> AnyView in
// Sticky Header
DispatchQueue.main.async {
offset = proxy.frame(in: .global).minY
safeArea = foo.safeAreaInsets
}
return AnyView(makeHeader())
}
.frame(height: 180)
.zIndex(1)
// Profile
VStack(spacing: 0) {
VStack(spacing: 0) {
HStack(spacing: 0) {
TabButton(title: "Tab #1", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
TabButton(title: "Tab #2", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
TabButton(title: "Tab #3", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
}
Divider()
}
.padding(.top, 20)
.background(Color.white)
.offset(y: tabBarOffset < 90 ? -tabBarOffset + 90 : 0)
.overlay(
GeometryReader { proxy -> Color in
DispatchQueue.main.async {
tabBarOffset = proxy.frame(in: .global).minY
}
return Color.clear
}
.frame(width: 0, height: 0),
alignment: .top
)
.zIndex(1)
VStack {
ForEach((0..<50)) {
Text("Row #\([=10=])")
Divider()
}
}
.padding(.top)
.zIndex(0)
}
}
}
.ignoresSafeArea(.all, edges: .top)
}
}
@ViewBuilder func makeHeader() -> some View {
ZStack {
// Cover
Color.gray
.frame(maxWidth: .infinity)
.frame(height: offset > 0 ? 180 + offset : 180)
}
.clipped()
// Stretchy Effect
.frame(height: offset > 0 ? 180 + offset : nil)
.offset(y: offset > 0 ? -offset : -offset < 80 ? 0 : -offset - 80)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct TabButton: View {
var title: String
@Binding var currentTab: String
var animation: Namespace.ID
var body: some View {
Button(
action: {
withAnimation { currentTab = title }
},
label: {
LazyVStack(spacing: 12) {
Text(title)
.fontWeight(.semibold)
.foregroundColor(currentTab == title ? .blue : .gray)
.padding(.horizontal)
if currentTab == title {
Capsule()
.fill(Color.blue)
.frame(height: 1.2)
.matchedGeometryEffect(id: "TAB", in: animation)
} else {
Capsule()
.fill(Color.clear)
.frame(height: 1.2)
}
}
}
)
}
}
在这里,设置 90 而不是 80。因为你的设置顶部标签栏视图 y 偏移 90 并且你的整个逻辑基于 90 值。
@ViewBuilder func makeHeader() -> some View {
ZStack {
// Cover
Color.red
.frame(maxWidth: .infinity)
.frame(height: offset > 0 ? 180 + offset : 180)
}
.clipped()
// Stretchy Effect
.frame(height: offset > 0 ? 180 + offset : nil)
.offset(y: offset > 0 ? -offset : -offset < 90 ? 0 : -offset - 90) //<=== Here
}