函数在初始化之前使用的变量
Variable used by function before being initialized
我在下面的函数中更改 'cityName' 时遇到问题
private func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
@Binding var cityName:String?
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
cityName = placemark.locality
} else {
print("No matching address found")
}
} }
它在 cityName = placemark.locality
上抛出的错误是 Variable '_cityName' used by function definition before being initialized
。我在另一个名为 ContentView.swift 的文件中用 @State var cityName:String = ""
初始化了 cityName
我是 Swift 的新手,如有任何帮助,我们将不胜感激。
编辑: 这是上下文的更多代码
ContentView.swift
import SwiftUI
struct ContentView: View {
@State var cityName:String = ""
@State var temp:Double = 0
@State var condition:String = ""
var body: some View {
VStack{
UserLocatonButtonView()
//Text view
weatherInfo(cityName: $cityName, temp: $temp, condition: $condition)
//Text("Temperature: \(temp), Condition: \(condition)")
}
}
}
struct weatherInfo: View {
@Binding var cityName: String
@Binding var temp: Double
@Binding var condition: String
var body: some View {
HStack {
Text(self.$cityName.wrappedValue.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", $temp.wrappedValue))
.padding()
.font(.title)
Text($condition.wrappedValue)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}
UserLocationManager.swift(processResponse 位于该文件的底部)
import CoreLocation
import CoreLocationUI
import SwiftUI
struct UserLocatonButtonView: View {
@StateObject private var locationManager = UserLocationManager()
var body: some View {
VStack {
if let location = locationManager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
LocationButton(.currentLocation) {
locationManager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}
final class UserLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var location = CLLocation()
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()
self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
processResponse(withPlacemarks: placemarks, error: error)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}
}
/************
*
* Helper function for coverting coordinates to city location
*
************/
private func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
@Binding var cityName: String
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
cityName = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
如果不进行大的重构,最快的更改可能是通过视图将 Binding
显式传递给 cityName
并传递给 UserLocationManager
。这可能看起来像这样:
struct ContentView: View {
@State var cityName:String = ""
@State var temp:Double = 0
@State var condition:String = ""
var body: some View {
VStack{
UserLocatonButtonView(cityName: $cityName)
WeatherInfo(cityName: $cityName, temp: $temp, condition: $condition)
}
}
}
struct WeatherInfo: View {
@Binding var cityName: String
@Binding var temp: Double
@Binding var condition: String
var body: some View {
HStack {
Text(cityName.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", temp))
.padding()
.font(.title)
Text(condition)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}
struct UserLocatonButtonView: View {
@StateObject private var locationManager : UserLocationManager
init(cityName: Binding<String>) {
_locationManager = StateObject(wrappedValue: UserLocationManager(cityName: cityName))
}
var body: some View {
VStack {
if let location = locationManager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
LocationButton(.currentLocation) {
locationManager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}
final class UserLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
var cityName : Binding<String>
@Published var location = CLLocation()
let locationManager = CLLocationManager()
init(cityName: Binding<String>) {
self.cityName = cityName
super.init()
locationManager.delegate = self
}
// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()
self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
self.processResponse(withPlacemarks: placemarks, error: error)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}
func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
cityName.wrappedValue = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
}
话虽这么说,我认为一个更简洁的解决方案是将共享状态移动到父视图模型和 @Published
属性 所拥有。此外,也许这只是您的最小示例的结果,但您只需要在需要 two-way 通信时使用 Binding
s。因此,就目前而言,您的 WeatherInfo
根本不需要 Binding
。一旦这样的重构可能看起来像这样:
struct Model {
var cityName : String
var temp: Double
var condition: String
}
struct ContentView: View {
@StateObject private var manager = UserLocationWeatherManager()
var body: some View {
VStack{
UserLocatonButtonView(manager: manager)
WeatherInfo(cityName: manager.model.cityName,
temp: manager.model.temp,
condition: manager.model.condition)
}
}
}
struct WeatherInfo: View {
var cityName: String
var temp: Double
var condition: String
var body: some View {
HStack {
Text(cityName.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", temp))
.padding()
.font(.title)
Text(condition)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}
struct UserLocatonButtonView: View {
@ObservedObject var manager : UserLocationWeatherManager
var body: some View {
VStack {
if let location = manager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
LocationButton(.currentLocation) {
manager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}
final class UserLocationWeatherManager: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var model = Model(cityName: "", temp: 0, condition: "")
@Published var location = CLLocation()
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()
self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
self.processResponse(withPlacemarks: placemarks, error: error)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}
func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
self.model.cityName = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
}
我在下面的函数中更改 'cityName' 时遇到问题
private func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
@Binding var cityName:String?
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
cityName = placemark.locality
} else {
print("No matching address found")
}
} }
它在 cityName = placemark.locality
上抛出的错误是 Variable '_cityName' used by function definition before being initialized
。我在另一个名为 ContentView.swift 的文件中用 @State var cityName:String = ""
我是 Swift 的新手,如有任何帮助,我们将不胜感激。
编辑: 这是上下文的更多代码
ContentView.swift
import SwiftUI
struct ContentView: View {
@State var cityName:String = ""
@State var temp:Double = 0
@State var condition:String = ""
var body: some View {
VStack{
UserLocatonButtonView()
//Text view
weatherInfo(cityName: $cityName, temp: $temp, condition: $condition)
//Text("Temperature: \(temp), Condition: \(condition)")
}
}
}
struct weatherInfo: View {
@Binding var cityName: String
@Binding var temp: Double
@Binding var condition: String
var body: some View {
HStack {
Text(self.$cityName.wrappedValue.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", $temp.wrappedValue))
.padding()
.font(.title)
Text($condition.wrappedValue)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}
UserLocationManager.swift(processResponse 位于该文件的底部)
import CoreLocation
import CoreLocationUI
import SwiftUI
struct UserLocatonButtonView: View {
@StateObject private var locationManager = UserLocationManager()
var body: some View {
VStack {
if let location = locationManager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
LocationButton(.currentLocation) {
locationManager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}
final class UserLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var location = CLLocation()
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()
self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
processResponse(withPlacemarks: placemarks, error: error)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}
}
/************
*
* Helper function for coverting coordinates to city location
*
************/
private func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
@Binding var cityName: String
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
cityName = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
如果不进行大的重构,最快的更改可能是通过视图将 Binding
显式传递给 cityName
并传递给 UserLocationManager
。这可能看起来像这样:
struct ContentView: View {
@State var cityName:String = ""
@State var temp:Double = 0
@State var condition:String = ""
var body: some View {
VStack{
UserLocatonButtonView(cityName: $cityName)
WeatherInfo(cityName: $cityName, temp: $temp, condition: $condition)
}
}
}
struct WeatherInfo: View {
@Binding var cityName: String
@Binding var temp: Double
@Binding var condition: String
var body: some View {
HStack {
Text(cityName.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", temp))
.padding()
.font(.title)
Text(condition)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}
struct UserLocatonButtonView: View {
@StateObject private var locationManager : UserLocationManager
init(cityName: Binding<String>) {
_locationManager = StateObject(wrappedValue: UserLocationManager(cityName: cityName))
}
var body: some View {
VStack {
if let location = locationManager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
LocationButton(.currentLocation) {
locationManager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}
final class UserLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
var cityName : Binding<String>
@Published var location = CLLocation()
let locationManager = CLLocationManager()
init(cityName: Binding<String>) {
self.cityName = cityName
super.init()
locationManager.delegate = self
}
// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()
self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
self.processResponse(withPlacemarks: placemarks, error: error)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}
func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
cityName.wrappedValue = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
}
话虽这么说,我认为一个更简洁的解决方案是将共享状态移动到父视图模型和 @Published
属性 所拥有。此外,也许这只是您的最小示例的结果,但您只需要在需要 two-way 通信时使用 Binding
s。因此,就目前而言,您的 WeatherInfo
根本不需要 Binding
。一旦这样的重构可能看起来像这样:
struct Model {
var cityName : String
var temp: Double
var condition: String
}
struct ContentView: View {
@StateObject private var manager = UserLocationWeatherManager()
var body: some View {
VStack{
UserLocatonButtonView(manager: manager)
WeatherInfo(cityName: manager.model.cityName,
temp: manager.model.temp,
condition: manager.model.condition)
}
}
}
struct WeatherInfo: View {
var cityName: String
var temp: Double
var condition: String
var body: some View {
HStack {
Text(cityName.capitalized)
.padding()
.font(.title)
Text(String(format: "%.1f *C", temp))
.padding()
.font(.title)
Text(condition)
}
.frame(maxWidth: 400)
.background(Color.black.opacity(0.1))
}
}
struct UserLocatonButtonView: View {
@ObservedObject var manager : UserLocationWeatherManager
var body: some View {
VStack {
if let location = manager.location {
Text("Your location: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
LocationButton(.currentLocation) {
manager.requestAllowOnceLocationPermission()
}
.foregroundColor(.white)
.cornerRadius(8)
.labelStyle(.titleAndIcon)
.symbolVariant(.fill)
.tint(Color.black.opacity(0.3))
}
}
}
final class UserLocationWeatherManager: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var model = Model(cityName: "", temp: 0, condition: "")
@Published var location = CLLocation()
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
// Request location once
func requestAllowOnceLocationPermission() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// if there are not locations
guard let latestLocation = locations.last else {
// show an error
return
}
// If location is updated
DispatchQueue.main.async {
let geocoder = CLGeocoder()
self.location = CLLocation(latitude: latestLocation.coordinate.latitude, longitude: latestLocation.coordinate.longitude)
//self.location = latestLocation.coordinate
print("\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
geocoder.reverseGeocodeLocation(self.location) { (placemarks, error) in
self.processResponse(withPlacemarks: placemarks, error: error)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(self.location.coordinate.latitude), \(self.location.coordinate.longitude)")
print(error.localizedDescription)
}
func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Reverse Geocode Location (\(error))")
//locationLabel.text = "Unable to Find Address for Location"
} else {
if let placemarks = placemarks, let placemark = placemarks.first {
//locationLabel.text = placemark.compactAddress
self.model.cityName = placemark.locality!
} else {
print("No matching address found")
//locationLabel.text = "No Matching Addresses Found"
}
}
}
}