首页 > 解决方案 > 如何防止我的 SwiftUI 视图在滚动时调整大小

问题描述

我希望有人能指出我正确的方向——我一直在试验 SwiftUI,我创建了一个类似于 Twitter Profile UI 的视图。

当标题崩溃时,我似乎有一个奇怪的效果。

有一个“最佳位置”,其中标题被折叠并且标签栏在滚动视图包含在标题下滚动之前调整了少量大小。

你可以在这个视频中看到。

我根本不希望选项卡段调整大小,而是在标题折叠后它应该保持原位并且滚动视图应该在它下面自由移动。

我确定我遗漏了一些明显的东西,但是一些新鲜的眼睛可能正是我需要的帮助。

对此的任何想法将不胜感激!

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 #\($0)")
                                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)
                    }
                    
                }
            }
        )
    }
}

标签: swiftswiftuiscrollviewgeometryreader

解决方案


在这里,设置 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
}

推荐阅读