函数在初始化之前使用的变量

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 通信时使用 Bindings。因此,就目前而言,您的 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"
            }
        }
    }
}