You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入NavigationView的Geometry View顶部间距问题求助

问题:嵌入NavigationView后"TOP NEWS"与导航栏间出现多余间距

我参照教程设计SwiftUI视图,将其嵌入NavigationView后,"TOP NEWS"文本与导航栏之间出现了多余的间距。尝试修改标题样式和代码中的数值参数,问题仍未解决,作为iOS初级开发者寻求帮助。

以下是我的代码(与教程代码略有不同):

struct allArticlesListView: View {
    
    @State var title = "Sports"
    @Environment(\.colorScheme) var colorScheme
    @State var isGotData = false
//    let fetchData = FetchNews()
    @State var articles : [Article] = []
    
    
    
    var body: some View {
        
        if isGotData {
            
            ScrollView(.vertical){
                
                VStack(spacing : 0){
                    
                    VStack(alignment: .leading,spacing: 15) {
                        HStack{
                            Text("TOP NEWS")
                                .font(.largeTitle.bold())
                                .frame(height: 45)
                                .padding(.horizontal,15)
                        }
                        
                        GeometryReader{
                            
                            let rect = $0.frame(in: .scrollView(axis : .vertical))
                            let minY = rect.minY
                            let topValue : CGFloat = 75.0
                            
                            let offset = min(minY - topValue, 0)
                            let progress = max(min( -offset / topValue, 1), 0)
                            let scale = 1 + progress
                            
                            
                            ZStack{
                                Rectangle()
                                    .fill(Color.blue)
                                    .overlay(alignment: .leading) {
                                        Circle()
                                            .fill(Color.blue)
                                            .overlay {
                                                Circle()
                                                    .fill(.white.opacity(0.2))
                                                
                                            }
                                            .scaleEffect(2,anchor: .topLeading)
                                            .offset(x: -50,y: -40)
                                    }
                                    .clipShape(RoundedRectangle(cornerRadius: 25,style: .continuous))
                                    .scaleEffect(scale,anchor: .bottom)
                                
                                VStack(alignment: .center, spacing: 4){
                                    Text("\(title)")
                                        .font(.title.bold())
                                }
                                .foregroundStyle(.white)
                                .frame(maxWidth: .infinity, alignment : .leading)
                                .padding(15)
                                .offset(y : progress * -25 )
                                
                            }
                            .offset(y : -offset)
                            //Moving till top value
                            .offset(y : progress * -topValue)
                            
                        }
                        .padding(.horizontal,15)
                        .containerRelativeFrame(.horizontal)
                        .frame(height: 125)

                        LazyVStack(spacing: 15) {
                            ForEach(articles, id: \.self) { article in
                                NavigationLink(destination: detailedArticleView(singleArticle: article)) {
                                    VStack(alignment: .leading) {
                                        ArticleView(article: article, category: title)
                                    }
                                }
                            }

                        }
                        .padding(15)
                        .mask{
                            Rectangle()
                                .visualEffect { content, geometryProxy in
                                    content
                                        .offset(y : backgroundLimitOffset(geometryProxy))
                                }
                        }
                        .background{
                            GeometryReader {
                                
                                let rect = $0.frame(in: .scrollView)
                                let minY = min(rect.minY - 125, 0)
                                let progress = max(min(-minY / 25, 1), 0)
                                
                                RoundedRectangle(cornerRadius: 30 * progress,style: .continuous)
                                    .fill(colorScheme == .dark ? .black : .white)
                                    .overlay(alignment: .top){
                                        
                                    }
                                /// Limiting Background Scroll below the header
                                    .visualEffect { content, geometryProxy in
                                        content
                                            .offset(y : backgroundLimitOffset(geometryProxy))
                                    }
                                
                            }
                        }
                        
                    }
                    .padding(.vertical,15)
                    
                }
                
            }
            .scrollTargetBehavior(CustomScrollBehaviourSample())
            .scrollIndicators(.hidden)
            .background {
                LinearGradient(colors: [.green, .cyan, .cyan ,.indigo], startPoint: .topLeading, endPoint: .bottomTrailing)
                    .ignoresSafeArea()
            }
        }else{
            
            ProgressView()
                .onAppear {
                    //                    fetchData.fetchData(category: title) { allArticles in
                    //                        self.articles = allArticles
                    //                        print("fetched the data")
                    //                        isGotData = true
                    //                    }
                    fetch()
                    
                }
            
        }
        
        
    }
    
    
    /// Background Limit Offset
    func backgroundLimitOffset(_ proxy : GeometryProxy) -> CGFloat {
        
        let minY = proxy.frame(in: .scrollView).minY
        
        return minY < 75 ? -minY + 75 : 0
        
    }
    
    func fetch(){
        
        if let filePath = Bundle.main.url(forResource: "dummy.json", withExtension: ""){
            if let data = try? Data(contentsOf: filePath){
                if let content = try? JSONDecoder().decode(Welcome.self, from: data){
                    articles = content.articles
                    isGotData = true
                }else{
                    print("error in data decoding")
                }
            }else{
                print("error in data fetching")
            }
        }else{
            print("error in file path")
        }
        
    }
    
}

/// Custom Scroll Target Behaviour
/// AKA scrollWillEndDragging in UIKit

struct CustomScrollBehaviourSample : ScrollTargetBehavior {
    
    func updateTarget(_ target: inout ScrollTarget, context: TargetContext) {
        if target.rect.minY < 75 {
            target.rect = .zero
        }
    }
    
}

解决方案

针对这个间距问题,可以从以下几个方面调整代码:

  1. 处理NavigationView与ScrollView的安全区域冲突
    NavigationView默认会给子视图添加上部安全区域间距,而你的ScrollView背景已经用了.ignoresSafeArea(),两者叠加会产生多余间距。给ScrollView添加.edgesIgnoringSafeArea(.top),让它忽略顶部安全区域:

    ScrollView(.vertical){
        // 原有内容
    }
    .scrollTargetBehavior(CustomScrollBehaviourSample())
    .scrollIndicators(.hidden)
    .edgesIgnoringSafeArea(.top) // 新增这一行
    .background {
        LinearGradient(colors: [.green, .cyan, .cyan ,.indigo], startPoint: .topLeading, endPoint: .bottomTrailing)
            .ignoresSafeArea()
    }
    
  2. 移除不必要的垂直padding
    外层VStack的.padding(.vertical,15)会给整个内容块添加上下间距,直接导致"TOP NEWS"上方多出15pt空白。把它改成只给底部加padding:

    VStack(alignment: .leading,spacing: 15) {
        // "TOP NEWS"及后续内容
    }
    .padding(.bottom,15) // 替换原来的.padding(.vertical,15)
    
  3. 调整自定义滚动行为的阈值
    你的CustomScrollBehaviourSample中用了75作为滚动阈值,但NavigationView的导航栏(含状态栏)默认高度约为92pt,数值不匹配会导致滚动计算偏差。把阈值改成92:

    struct CustomScrollBehaviourSample : ScrollTargetBehavior {
        func updateTarget(_ target: inout ScrollTarget, context: TargetContext) {
            // 用92替换原来的75,适配导航栏高度
            if target.rect.minY < 92 {
                target.rect = .zero
            }
        }
    }
    
  4. 简化"TOP NEWS"的frame设置
    当前Text("TOP NEWS")的.frame(height:45)可能和上方间距叠加,试试移除这个frame设置,让文本高度自适应:

    Text("TOP NEWS")
        .font(.largeTitle.bold())
        .padding(.horizontal,15)
        // 移除.frame(height: 45)
    

内容的提问来源于stack exchange,提问作者KARAN _186

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:25:58