I am new to Xcode and swiftUI so this might be an easy for you but I appreciate all help. I am making a live scoring app. Under my dates bar (to show the scores from different dates), there should be my live scoring bar, however there is a black frame directly under it, then under that there is my scoring boards. If I try and scroll up on it, or put negative padding, it gets put under the black bar which makes it no longer visible.
Here is my code.
// ScoreView.swift
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
ScrollView(.horizontal, showsIndicators: false) {
ScrollViewReader { scrollView in
LazyHStack(spacing: 10) {
ForEach(vm.dates.indices, id: \.self) { index in
VStack {
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(8)
.cornerRadius(8)
.onTapGesture {
withAnimation {
vm.selectedDate = vm.dates[index]
}
}
Rectangle()
.frame(height: 16)
.foregroundColor(vm.dates[index] == vm.selectedDate ? .blue : .clear)
.cornerRadius(1.8)
}
.id(index)
.frame(width: geometry.size.width / 5.4)
}
}
.padding(.top, 4.0)
.background(Color(red: 27/255, green: 28/255, blue: 30/255))
.frame(maxHeight: 37)
.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
}
}
}
}
}
// Live Scores List
ScrollView {
VStack {
Section(header: Text("Favourites").foregroundColor(.white)) {
ForEach(vm.favouriteScores, id: \.self) { score in
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
}
}
Section(header: Text("OJCS").foregroundColor(.white)) {
ForEach(vm.ojcsScores, id: \.self) { score in
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
}
}
Section(header: Text("Andersons Weeknights").foregroundColor(.white)) {
ForEach(vm.andersonsWeeknightsScores, id: \.self) { score in
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
ScoreRowView(score: score)
}
}
}
.background(Color.black)
}
}
.edgesIgnoringSafeArea(.bottom) // Ensure that ScrollView extends below the date selector
}
}
.navigationBarHidden(true) // Hide the navigation bar
}
}
struct ScoreRowView: View {
var score: LiveScoreModel
var body: some View {
HStack {
VStack {
Image("Logo_Icon")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.horizontal)
Image("Logo_Icon")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.horizontal)
}
VStack(alignment: .leading) {
Text(score.teamName)
Text(score.teamName)
}
Spacer()
VStack(alignment: .trailing) {
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())
}
}
If you would like to run this program, create a file called HomeViewModel and paste this code into it:
import Foundation
import Combine
class HomeViewModel: ObservableObject {
@Published var searchText: String = ""
@Published var favouriteScores: [LiveScoreModel] = []
@Published var ojcsScores: [LiveScoreModel] = []
@Published var andersonsWeeknightsScores: [LiveScoreModel] = []
@Published var dates: [String] = []
@Published var selectedDate: String = ""
init() {
// Sample data
let sampleScores: [LiveScoreModel] = [
LiveScoreModel(teamLogo: "team1_logo", teamName: "Team 1", category: "Favourites", score: 10, gameTime: "2:30 PM"),
LiveScoreModel(teamLogo: "team2_logo", teamName: "Team 2", category: "OJCS", score: 8, gameTime: "3:45 PM"),
LiveScoreModel(teamLogo: "team3_logo", teamName: "Team 3", category: "Andersons Weeknights", score: 12, gameTime: "5:15 PM"),
// Add more
]
self.favouriteScores = sampleScores.filter { $0.category == "Favourites" }
self.ojcsScores = sampleScores.filter { $0.category == "OJCS" }
self.andersonsWeeknightsScores = sampleScores.filter { $0.category == "Andersons Weeknights" }
let currentDate = Date()
self.dates = generateDates(from: currentDate, daysBefore: 7, daysAfter: 7)
}
func todayString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd"
return dateFormatter.string(from: Date())
}
private func generateDates(from startDate: Date, daysBefore: Int, daysAfter: Int) -> [String] {
var dates: [String] = []
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
for day in -daysBefore...daysAfter {
if let date = Calendar.current.date(byAdding: .day, value: day, to: startDate) {
let dateString = formatter.string(from: date)
dates.append(dateString)
}
}
return dates
}
}
Here are some images to help.
I've figured out that the bug is in the GeometryReader
part of my date selector. That is the part that is causing the error.
I've spent the past couple days staring at this code trying to figure it out but I can't. I've tried removing the ZStack, but that just messes up everything and keeps the black bar there, I've tried adding paddings in different places and tried manually setting .frame(height: )
, but it doesn't change anything.
I appreciate any help. Thank you for your time.
The issue is due to the GeometryReader where you render the Date selector is occupying all that black space you see. You can either limit it or you use an overlay like I did:
// Date Selector
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
ScrollViewReader { scrollView in
LazyHStack(spacing: 10) {
ForEach(vm.dates.indices, id: \.self) { index in
VStack {
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(8)
.cornerRadius(8)
.onTapGesture {
withAnimation {
vm.selectedDate = vm.dates[index]
}
}
Rectangle()
.frame(height: 16)
.foregroundColor(vm.dates[index] == vm.selectedDate ? .blue : .clear)
.cornerRadius(1.8)
}
.id(index)
.frame(width: geometry.size.width / 5.4)
}
}
.padding(.top, 4.0)
.background(Color(red: 27 / 255, green: 28 / 255, blue: 30 / 255))
.frame(maxHeight: 37)
.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
}
}
}
}
} //: GEOMETRY
.overlay(alignment: .center) {
VStack {
Text("Place Score bar here")
.foregroundStyle(.white)
}
}
Let me know if this can be a good solution for you!