SwiftUI Как изменить цвет фона представления на основе выбранных данных?

#swift #swiftui

#swift #swiftui

Вопрос:

Я работаю над базовым приложением погоды на SwiftUI, которое динамически меняет цвет в зависимости от состояния погоды. Все работает отлично, но я не могу отправить данные значка OpenWeatherMap, которые были проанализированы из JSON, в мое представление. Похоже, что он не выделяет информацию, которая была отправлена из другого файла Swift, в объявленную мной переменную. У меня есть некоторый опыт работы с обычным Swift, но концепции SwiftUI определенно не знакомы.

Вот мой contentView.swift ниже.

    import SwiftUI

struct ContentView: View {
    @State private var selected = 0
    @ObservedObject var weather = CurrentWeatherViewModel()
    @State var city : String = ""
    private var height : CGFloat = UIScreen.main.bounds.height
        var body: some View {
            VStack{
                HStack{
                    TextField("Enter your city", text: $city){
                    self.weather.fetchmetric(self.city)
                    }.padding(.horizontal)
                    
                }
                    GeometryReader{ gr in
                        CurrentWeather(weather: self.weather.current, height: self.selected == 0 ? gr.size.height : (gr.size.height*0.75)).frame(width: 375.0, height: 770).modifier(currentViewModifier()).animation(.easeInOut(duration: 0.5))
                    }.edgesIgnoringSafeArea(.all)
                    
            }.frame(width: 375, height: 735, alignment: .center)
            
        
            
        }
    }

  

Это мой текущий файл Weather.swift. переменная @State color — это переменная, которую мне нужно изменить.

     import SwiftUI
import UIKit

struct CurrentWeather: View {
    // Added color themes for potential weather scenarios
    let bgColors = [
        "Clear":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.6544341662, green: 0.9271220419, blue: 0.9764705896, alpha: 1)), Color( #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Sunny":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), Color( #colorLiteral(red: 0.9529411793, green: 0.8685067713, blue: 0.1800223484, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Partly cloudy":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.5644291786, green: 0.6156922265, blue: 0.8125274491, alpha: 1)), Color( #colorLiteral(red: 0.3611070699, green: 0.3893437324, blue: 0.5149981027, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Cloudy":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.5088317674, green: 0.5486197199, blue: 0.7256778298, alpha: 1)), Color( #colorLiteral(red: 0.3843137255, green: 0.4117647059, blue: 0.5450980392, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Broken clouds":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.4714559888, green: 0.41813849, blue: 0.4877657043, alpha: 1)), Color( #colorLiteral(red: 0.3823538819, green: 0.3384427864, blue: 0.3941545051, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Mist":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.8536048541, green: 0.8154317929, blue: 0.6934956985, alpha: 1)), Color( #colorLiteral(red: 0.5, green: 0.3992742327, blue: 0.3267588525, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Patchy rain possible":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.422871705, green: 0.486337462, blue: 0.7241632297, alpha: 1)), Color(#colorLiteral(red: 0.3826735404, green: 0.4012053775, blue: 0.9529411793, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Patchy snow possible":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.8229460361, green: 0.8420813229, blue: 0.9764705896, alpha: 1)), Color( #colorLiteral(red: 0.6424972056, green: 0.9015246284, blue: 0.9529411793, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Patchy sleet possible":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.9764705896, green: 0.7979655136, blue: 0.9493740175, alpha: 1)), Color( #colorLiteral(red: 0.6843526756, green: 0.7806652456, blue: 0.9529411793, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Patchy freezing drizzle possible":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.6207757569, green: 0.9686274529, blue: 0.9110963382, alpha: 1)), Color( #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Thundery outbreaks possible":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1)), Color( #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Blowing snow":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)), Color( #colorLiteral(red: 0.09019608051, green: 0, blue: 0.3019607961, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Thunderstorm":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.9551106616, green: 0.9764705896, blue: 0.9351792135, alpha: 1)), Color( #colorLiteral(red: 0.6891936611, green: 0.7095901305, blue: 0.9529411793, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Fog":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.6324083141, green: 0.8039215803, blue: 0.7850640474, alpha: 1)), Color( #colorLiteral(red: 0.4545597353, green: 0.393878495, blue: 0.5369011739, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Freezing fog":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)), Color( #colorLiteral(red: 0.4545597353, green: 0.393878495, blue: 0.5369011739, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Patchy light drizzle":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.5892893535, green: 0.7170531098, blue: 0.9764705896, alpha: 1)), Color( #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Light rain":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)), Color( #colorLiteral(red: 0.2854045624, green: 0.4267300284, blue: 0.6992385787, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Moderate rain":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.3437546921, green: 0.6157113381, blue: 0.7179171954, alpha: 1)), Color( #colorLiteral(red: 0.4118283819, green: 0.5814552154, blue: 0.6975531409, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Heavy rain":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)), Color( #colorLiteral(red: 0.1596036421, green: 0, blue: 0.5802268401, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Light freezing rain":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.7433765433, green: 0.9529411793, blue: 0.8886958889, alpha: 1)), Color( #colorLiteral(red: 0.4561494407, green: 0.6342332627, blue: 0.7568627596, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "Heavy rain at times":LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)), Color( #colorLiteral(red: 0.1596036421, green: 0, blue: 0.5802268401, alpha: 1))]), startPoint: .top, endPoint: .bottom),
        "defaultStatus":LinearGradient(gradient: Gradient(colors: [Color(#colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)),Color(#colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1))]), startPoint: .top, endPoint: .bottom)
    ]
    
    var weather : Weather?
    var height : CGFloat = 0
    
   @State var color : String = "defaultStatus"
    
    var body: some View {
        NavigationView{
        VStack(alignment: .center, spacing: 10) {
            Image(weather?.weather.last?.icon ?? "01d")
                .resizable()
                .frame(width: 130, height: 130)
                .aspectRatio(contentMode: .fit)
            Text("Today in (weather?.name ?? "Unknown")")
                .font(.title)
                .foregroundColor(.white)
            .bold()
            .padding()
            HStack{
                Text("(weather?.main.temp ?? 0)°")
                    .foregroundColor(.white)
                    .fontWeight(.heavy)
                    .font(.system(size: 50))
               
            }
            Text("(weather?.weather.last?.description ?? "Unknown")")
                .foregroundColor(.white)
                .font(.body)
            
        }.frame(width: height, height: height)
            .background(bgColors[color])
            
        .navigationBarItems(trailing:
                                NavigationLink(destination: Settings()) {
                                    Image(systemName: "gear").imageScale(.large).accentColor(.black)
                            })
        }.onAppear {} // Code Execution at startup
    }
}

struct currentViewModifier : ViewModifier{
    private var radius : CGFloat = 20
    private var xAxis : CGFloat = 20
    private var yAxis : CGFloat = 20
    
    func body(content: Content) -> some View {
        content
            .cornerRadius(radius)
            
            .opacity(1.0)
        
    }
}
  

Это мой файл WeatherModel.swift, в котором у меня есть функции для выполнения выборки и функция background () для запуска кода.

     import Foundation
import Combine

final class CurrentWeatherViewModel : ObservableObject{
    @Published var current : Weather?
    
    init() {
        DispatchQueue.main.async {
            //if Settings().selected == 0{
                self.fetchmetric()
            //}
           // else{
                self.fetchimperial()
           // }
        }
    }
}

// fetch functions for metric and imperial and set color at the home screen

extension CurrentWeatherViewModel {
    func fetchmetric(_ city : String = "london"){
        let icon = current?.weather.last?.icon
        API.fetchCurrentmetricWeather(by: city) {
            // Work In Progress
            
            self.current = $0
            CurrentWeather().color = self.backgroundColor(code: icon ?? "aaa")
        }
    }
    
    func fetchimperial(_ city : String = "london"){
        API.fetchCurrentimperialWeather(by: city) {
            self.current = $0
            
        }
    }
    func backgroundColor(code : String) -> String {
        
        switch code {
            case "01d":
                return "Clear"
            case "02d":
                return "Partly cloudy"
            case "02n":
                return "Partly cloudy"
            case "03d":
                return "Cloudy"
            case "03n":
                return "Cloudy"
            case "04d":
                return "Broken clouds"
            case "04n":
                return "Broken clouds"
            case "09d":
                return "Moderate Rain"
            case "09n":
                return "Heavy rain"
            case "10d":
                return "Moderate Rain"
            case "10n":
                return "Heavy rain"
            case "11d":
                return "Thunderstorm"
            case "11n":
                return "Thunderstorm"
            case "13d":
                return "Snow"
            case "13n":
                return "Snow"
            case "50d":
                return "Mist"
            case "50n":
                return "Mist"
            default:
                return "defaultStatus"
            }
        }
        
}
  

Мой вопрос в том, чего мне не хватает, что он не отправляет данные значка в переменную currentWeather color? Я должен сказать, что я не знаком с переменными @State и пытаюсь изучить SwiftUI.

Ценю любой вклад в код.

Ответ №1:

Выражение CurrentWeather().color = self.backgroundColor(code: icon ?? "aaa") бессмысленно, потому что изменяет локально созданное значение ( CurrentWeather() создает значение структуры).

Вместо этого вам нужно создать опубликованное свойство в модели представления для цвета и использовать его, например

 final class CurrentWeatherViewModel : ObservableObject{
    @Published var current : Weather?
    @Publisehd var color: String = "aaa"

  

затем обновите его как

 API.fetchCurrentmetricWeather(by: city) { weather in
    // Work In Progress
    
    DispatchQueue.main.async { // update published on main queue !!!
       self.current = weather
       self.color = self.backgroundColor(code: icon ?? "aaa")
    }
}
  

затем используйте его в представлении, подобном

     }.frame(width: height, height: height)
        .background(bgColors[self.weather?.color ?? "defaultStatus"])

  

и локальный вид @State var color не нужен

Комментарии:

1. Спасибо за ваш вклад. Когда я ввожу код в соответствии с вашими инструкциями, я получаю «Контекстный тип закрытия ‘@convention(block) () -> Void’ ожидает 0 аргументов, но 1 использовался в теле закрытия» в строке DispatchQueue.

2. self.weather?.color похоже, что он не существует, я использовал bgColors[self.model?.color ?? "defaultStatus"] , поскольку я предполагал, что вы имели в виду CurrentWeatherViewModel. Код работает нормально, но он по-прежнему получает входные данные по умолчанию.