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

SwiftUI实时计分应用日期选择器下方黑条问题求助

问题解决:SwiftUI实时计分应用日期选择器下方黑条遮挡计分栏问题

问题现象

开发实时计分应用时,日期选择栏下方出现黑条,原本的实时计分栏被挤至黑条下方,向上滚动或设置负内边距会导致计分栏被黑条遮挡无法显示,已定位问题出在日期选择器的GeometryReader部分,尝试移除ZStack、调整内边距、设置frame(height)等方法未解决。

问题根源

  1. GeometryReader无限制拉伸:GeometryReader默认会占据父视图提供的所有垂直空间,未设置明确高度时会产生额外空白区域(黑条)。
  2. ScrollView与ScrollViewReader层级错误:将ScrollViewReader嵌套在ScrollView内部,导致布局计算异常,同时内部元素的frame设置不合理,进一步加剧空间占用问题。

解决方案

修改日期选择器的布局结构,明确GeometryReader高度,调整ScrollViewReader层级,并优化内部元素布局:

修改后的关键代码(日期选择器部分)

// Date Selector
GeometryReader { geometry in
    ScrollViewReader { scrollView in
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack(spacing: 10) {
                ForEach(vm.dates.indices, id: \.self) { index in
                    VStack(spacing: 4) {
                        Text(vm.dates[index] == vm.todayString() ? "Today" : vm.dates[index])
                            .foregroundColor(vm.dates[index] == vm.selectedDate ? .white : Color(red: 122/255, green: 122/255, blue: 124/255))
                            .bold()
                            .padding(.vertical, 6)
                            .padding(.horizontal, 8)
                            .cornerRadius(8)
                            .onTapGesture {
                                withAnimation {
                                    vm.selectedDate = vm.dates[index]
                                }
                            }

                        Rectangle()
                            .frame(height: 3) // 缩小指示器高度,减少空间占用
                            .foregroundColor(vm.dates[index] == vm.selectedDate ? .blue : .clear)
                            .cornerRadius(1.8)
                    }
                    .id(index)
                    .frame(width: geometry.size.width / 5.4)
                }
            }
            .padding(.horizontal, 8)
        }
        .background(Color(red: 27/255, green: 28/255, blue: 30/255))
        .onReceive(vm.$selectedDate) { selectedDate in
            if let selectedIndex = vm.dates.firstIndex(of: selectedDate) {
                withAnimation {
                    scrollView.scrollTo(selectedIndex, anchor: .center)
                }
            }
        }
        .onAppear {
            if !initialScrollDone, let todayIndex = vm.dates.firstIndex(of: vm.todayString()) {
                vm.selectedDate = vm.todayString()
                scrollView.scrollTo(todayIndex)
                initialScrollDone = true
            }
        }
    }
}
.frame(height: 50) // 固定GeometryReader高度,消除多余黑块
.background(Color(red: 27/255, green: 28/255, blue: 30/255))

核心修改点

  1. 调整ScrollViewReader层级:将ScrollViewReader放在ScrollView外层,确保滚动控制逻辑正常,避免嵌套布局异常。
  2. 固定GeometryReader高度:通过.frame(height: 50)限制日期选择器容器高度,防止无限制拉伸产生黑条。
  3. 优化指示器与内边距:缩小底部选中指示器高度,给LazyHStack添加水平内边距,提升布局紧凑性与视觉效果。

完整修改后的ScoreView代码

import SwiftUI

struct ScoreView: View {
    @EnvironmentObject private var vm: HomeViewModel
    @State private var initialScrollDone = false

    var body: some View {
        NavigationView {
            ZStack {
                Color.black.edgesIgnoringSafeArea(.all)

                VStack(spacing: 0) {
                    // Top Bar
                    HStack {
                        Button(action: {
                            // Account/log in action
                        }) {
                            Image("Logo_Icon")
                                .resizable()
                                .scaledToFit()
                                .frame(height: 35)
                        }

                        SearchBarView(searchText: $vm.searchText)

                        Spacer()
                    }
                    .padding(.horizontal)
                    .background(Color(red: 27/255, green: 28/255, blue: 30/255))

                    // Date Selector
                    GeometryReader { geometry in
                        ScrollViewReader { scrollView in
                            ScrollView(.horizontal, showsIndicators: false) {
                                LazyHStack(spacing: 10) {
                                    ForEach(vm.dates.indices, id: \.self) { index in
                                        VStack(spacing: 4) {
                                            Text(vm.dates[index] == vm.todayString() ? "Today" : vm.dates[index])
                                                .foregroundColor(vm.dates[index] == vm.selectedDate ? .white : Color(red: 122/255, green: 122/255, blue: 124/255))
                                                .bold()
                                                .padding(.vertical, 6)
                                                .padding(.horizontal, 8)
                                                .cornerRadius(8)
                                                .onTapGesture {
                                                    withAnimation {
                                                        vm.selectedDate = vm.dates[index]
                                                    }
                                                }

                                            Rectangle()
                                                .frame(height: 3)
                                                .foregroundColor(vm.dates[index] == vm.selectedDate ? .blue : .clear)
                                                .cornerRadius(1.8)
                                        }
                                        .id(index)
                                        .frame(width: geometry.size.width / 5.4)
                                    }
                                }
                                .padding(.horizontal, 8)
                            }
                            .background(Color(red: 27/255, green: 28/255, blue: 30/255))
                            .onReceive(vm.$selectedDate) { selectedDate in
                                if let selectedIndex = vm.dates.firstIndex(of: selectedDate) {
                                    withAnimation {
                                        scrollView.scrollTo(selectedIndex, anchor: .center)
                                    }
                                }
                            }
                            .onAppear {
                                if !initialScrollDone, let todayIndex = vm.dates.firstIndex(of: vm.todayString()) {
                                    vm.selectedDate = vm.todayString()
                                    scrollView.scrollTo(todayIndex)
                                    initialScrollDone = true
                                }
                            }
                        }
                    }
                    .frame(height: 50)
                    .background(Color(red: 27/255, green: 28/255, blue: 30/255))

                    // Live Scores List
                    ScrollView {
                        VStack(spacing: 16) {
                            Section(header: Text("Favourites").foregroundColor(.white).padding(.horizontal)) {
                                ForEach(vm.favouriteScores, id: \.self) { score in
                                    ScoreRowView(score: score)
                                }
                            }

                            Section(header: Text("OJCS").foregroundColor(.white).padding(.horizontal)) {
                                ForEach(vm.ojcsScores, id: \.self) { score in
                                    ScoreRowView(score: score)
                                }
                            }

                            Section(header: Text("Andersons Weeknights").foregroundColor(.white).padding(.horizontal)) {
                                ForEach(vm.andersonsWeeknightsScores, id: \.self) { score in
                                    ScoreRowView(score: score)
                                }
                            }
                        }
                        .padding(.horizontal)
                        .background(Color.black)
                    }
                }
                .edgesIgnoringSafeArea(.bottom)
            }
        }
        .navigationBarHidden(true)
    }
}

struct ScoreRowView: View {
    var score: LiveScoreModel

    var body: some View {
        HStack {
            HStack(spacing: 16) {
                Image("Logo_Icon")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 20, height: 20)

                Image("Logo_Icon")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 20, height: 20)
            }
            .padding(.horizontal, 8)

            VStack(alignment: .leading, spacing: 4) {
                Text(score.teamName)
                Text(score.teamName)
            }

            Spacer()

            VStack(alignment: .trailing, spacing: 4) {
                Text("Score: \(Int.random(in: 0...10)) - \(Int.random(in: 0...10))")
                Text("Time: \(score.gameTime)")
            }
        }
        .listRowBackground(Color.clear)
        .padding(.vertical, 8)
        .background(Color(red: 36/255, green: 37/255, blue: 39/255))
        .foregroundColor(.white)
        .cornerRadius(8)
    }
}

struct ScoreView_Previews: PreviewProvider {
    static var previews: some View {
        ScoreView()
            .environmentObject(HomeViewModel())
    }
}

额外优化

  • 给计分列表Section标题添加水平内边距,避免贴边
  • 调整ScoreRowView内部元素间距,优化布局规整度
  • 移除测试用的重复ScoreRowView,减少冗余代码

内容的提问来源于stack exchange,提问作者Justin Anderson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:14:52