I am trying to assign each weather location in my app a page in a tab view but am unsure on how to achieve this. I have a currentPage variable in my location view which should add 1 every time a weather location is shown on screen but instead it just keeps adding one throughout the entire foreach loop I have so a page number isn't actually assigned to a weather view. I was wondering if anyone knew how I could assign a page number to each location when iterating through my for each loop. My location view is shown below. The code I tried is at the bottom in the onAppear modifier in the if statement but it doesn't work like I said above and I am not sure if this is even the right approach for doing something like this. Any help would be great!
LocationView
'''
import SwiftUI
struct LocationView: View {
@ObservedObject var weatherVM: WeatherViewViewModel
@ObservedObject var multiLocationVM: MultiLocationViewModel
@State private var pageSelection = 1
@State var currentPage: Int = 0
var body: some View {
ZStack {
LinearGradient(colors: [Color("BackgroundColorSun"), Color("BackgroundColorSky")], startPoint: .topLeading, endPoint: UnitPoint(x: 0.5, y: 0.5)).ignoresSafeArea()
if multiLocationVM.weatherVM.count == 0 {
BlankLocationView(multiLocationVM: multiLocationVM)
} else {
TabView(selection: $pageSelection) {
ForEach(multiLocationVM.weatherVM, id: \.self.city) { location in
VStack(spacing: 0) {
VStack(spacing: -10) {
ZStack(alignment: .center) {
Text("\(location.getWeatherIconFor(icon: location.weatherIcon))")
//.innerShadow(Color(.red))
.font(.custom("SF Pro Text", size: 64))
.innerShadow()
//.foregroundColor(.gray)
.offset(x: -70, y: -35)
Text("\(location.getTempFor(temp: location.weather.current.temp))°")
.font(.system(size: 96, weight: .semibold, design: .rounded))
.tracking(-0.41)
.shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4)
}
.offset(x: 15)
Text(location.conditions)
.font(.custom("SF Pro Display", size: 24))
}
.padding(EdgeInsets(top: -8, leading: 0, bottom: -8, trailing: 0))
VStack(spacing: -20) {
HourlyWeatherView(weatherVM: location)
DailyWeatherView(weatherVM: location)
}
Spacer()
}
.onAppear() {
if location.city != weatherVM.city {
weatherVM.city = location.city
currentPage = multiLocationVM.weatherVM.firstIndex(of: location)!
location.page = currentPage
print("current \(currentPage)")
print("Location \(location.page)")
} else {
return
}
}
.tag(currentPage)
}
}
.tabViewStyle(.page(indexDisplayMode: .automatic))
.indexViewStyle(.page(backgroundDisplayMode: .interactive))
}
}
.padding(.bottom, 70)
.ignoresSafeArea()
}
}
struct LocationView_Previews: PreviewProvider {
static var previews: some View {
LocationView(weatherVM: WeatherViewViewModel(city: "Phoenix"), multiLocationVM: MultiLocationViewModel())
}
}
'''
CodePudding user response:
IMHO you don't need the currentPage counter as TabView(selection: )
is handling that for you. The selection, in your case pageSelection
will update when the user swipes, or you can set it programmatically. You just have to provide a fitting .tag
:
// dummy test data
struct Weather {
var city: String
var icon: String
var temp: Int
var condition: String
}
let multiLocationVM = [
Weather(city: "Vancouver", icon: "cloud", temp: 64, condition: " Mostly Cloudy"),
Weather(city: "New York", icon: "cloud.sun", temp: 78, condition: " Mostly Sunny"),
Weather(city: "Los Angeles", icon: "sun.max", temp: 91, condition: " Sunny"),
]
struct ContentView: View {
@State private var pageSelection = "Vancouver" // default here
@State var currentPage: Int = 0
var body: some View {
ZStack {
LinearGradient(colors: [Color(.yellow), Color(.blue)], startPoint: .topLeading, endPoint: UnitPoint(x: 0.5, y: 0.5)).ignoresSafeArea()
if multiLocationVM.count == 0 {
// BlankLocationView(multiLocationVM: multiLocationVM)
} else {
TabView(selection: $pageSelection) {
ForEach(multiLocationVM, id: \.city) { location in
LocalWeather(location: location)
.tag(location.city) // set tab id here
}
}
.tabViewStyle(.page(indexDisplayMode: .automatic))
}
}
.padding(.bottom, 70)
.ignoresSafeArea()
}
}
struct LocalWeather: View {
let location: Weather
var body: some View {
VStack(alignment: .center, spacing: 0) {
VStack(alignment: .center, spacing: -10) {
ZStack(alignment: .center) {
Image(systemName: location.icon)
.font(.custom("SF Pro Text", size: 32))
.offset(x: -90, y: -35)
Text("\(location.temp)")
.font(.system(size: 96, weight: .semibold, design: .rounded))
.tracking(-0.41)
.shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4)
}
.offset(x: 15)
Text(location.condition)
.font(.custom("SF Pro Display", size: 24))
}
.padding(EdgeInsets(top: -8, leading: 0, bottom: -8, trailing: 0))
Spacer()
}
}
}