SwiftUI 如何根据获取的数据更改视图的背景颜色?
Posted
技术标签:
【中文标题】SwiftUI 如何根据获取的数据更改视图的背景颜色?【英文标题】:SwiftUI How to change background color of the view based on fetched data? 【发布时间】:2020-08-14 07:34:51 【问题描述】:我正在 SwiftUI 上开发一个基本的天气应用程序,它会根据天气状态动态改变颜色。一切正常,但我无法将从 JSON 解析的 OpenWeatherMap 图标数据发送到我的视图。看起来它没有将从另一个 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)
这是我的 CurrentWeather.swift 文件。 @State 颜色变量是我需要更改的变量。
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 颜色变量,我错过了什么?我必须说我不熟悉@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
【讨论】:
感谢您的意见。当我按照您的指示放置代码时,我在 DispatchQueue 行上收到“上下文闭包类型'@convention(block) () -> Void' 需要 0 个参数,但在闭包主体中使用了 1”。self.weather?.color
似乎不存在,我使用了bgColors[self.model?.color ?? "defaultStatus"]
,因为我认为您的意思是 CurrentWeatherViewModel。代码运行良好,但仍获得默认输入。以上是关于SwiftUI 如何根据获取的数据更改视图的背景颜色?的主要内容,如果未能解决你的问题,请参考以下文章
如何根据 SwiftUI 中的 @State 更改导航视图的导航视图样式?
SwiftUI:如何根据滚动视图的用户当前滚动位置同步/更改进度条进度?