我牛仔为趣味性编写了一个小天气应用程序。据我所知,UI看起来很不完美,但我正在重做以符合具有可伸缩性的行业标准代码。无论如何,在OG代码中,我的ViewController的UIImageView扩展中具有下载功能,实际上,它实际上是在线程中下载图像,然后基于此以编程方式更改UIImageView。
现在,相反,我具有该下载功能,就像在Service类中的常规功能一样,我在ViewModel类中调用它来执行相同的操作,然后将URL存储在我的模型类和viewmodel类之一中。
一切正常。我在更改图像之前和更改之后打印出图像,我可以说它已经更改,而且也不为零,所以我知道它可以工作。我现在已经使用相同的下载功能了很久了,唯一的区别是我现在将其作为常规功能而不是扩展功能来使用。
反正下面是代码:
import UIKit
import CoreLocation
class ViewController: UIViewController {
//UI variables
@IBOutlet var weatherImage: UIImageView!
@IBOutlet var test: UITextField!
//Location Object
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
// locationManager.requestLocation()
if (CLLocationManager.locationServicesEnabled()){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
}
func populateView(currentLocationVM : LocationWeatherVM){
//this prints the default image prior to messing around with it
print(weatherImage.image)
print(weatherImage.image)
//this is to test to see if other UI components are able to be dynamically changed
self.test.text = currentLocationVM.city
//self.weatherImage = currentLocationVM.weatherImage
//this was to test if I changed these properties if it could help
let test = currentLocationVM.weatherImage
test.clipsToBounds = true
test.autoresizesSubviews = true
self.weatherImage = test
print("")
print("")
print("")
//these match as the new image
print(weatherImage.image)
print(weatherImage.image)
print("")
print("")
print("")
let temp = UIImageView()
print(temp.image) //this is empty
}
}
/**
CLLocationManagerDelegate extension for the Vew Controller class
- important: didFailWithError and didUpdateLocations are for testing purposes. didChangeAuthorizationStatus is used for requesting process. didUpdateLocations is ALSO used for starting the functionality's chain of events once the user location has been found
In later updates, this can return a message to the user if there isn't enough network power to get a search (say...within a few seconds?)
*/
extension ViewController : CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if locations.first != nil {
print("location: (location)")
let currentLongitude = manager.location!.coordinate.longitude
let currentLatitude = manager.location!.coordinate.latitude
//service to get current location info - weather info and store it as viewmodel to then displays upon app launch
//essentially do the fetchcourses thing but one for fetchLocation then one for fetchWeather
//map that to a viewModel then call the populateView func
let service = Service()
let currentLocationVM = service.fetchCurrentViewModelInfo(currentLongitude: currentLongitude, currentLatitude: currentLatitude)
print(currentLocationVM)
populateView(currentLocationVM: currentLocationVM)
//load old UI Searches
manager.delegate = nil
}
}
}
我的服务班级:
import Foundation
import CoreLocation
import GooglePlaces
import GoogleMaps
import UIKit
class Service: NSObject {
static let shared = Service()
let googleAPIKey = "i'mnottellingumyapikeyhaha"
let commaSpace = ","
func fetchCurrentViewModelInfo(currentLongitude: CLLocationDegrees, currentLatitude: CLLocationDegrees) -> LocationWeatherVM{
let locationInfo = getLocationInfoFromPlaceID(placeID: getCurrentPlaceID(currentLongitude: currentLongitude, currentLatitude: currentLatitude))
print("or here")
let weatherInfo = fetchWeather(locationInfo: locationInfo)
return LocationWeatherVM(locationInfo: locationInfo, weatherInfo: weatherInfo)
}
func getCurrentPlaceID(currentLongitude: CLLocationDegrees, currentLatitude: CLLocationDegrees) -> String {
let currLat = String(currentLatitude)
let currLong = String(currentLongitude)
var placeID: String!
let semaphore = DispatchSemaphore(value: 0)
print("https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(currLat),\(currLong)&radius=1&key=\(googleAPIKey)")
let urlRequest = URLRequest(url: URL(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(currLat),\(currLong)&radius=1&key=\(googleAPIKey)")!)
let task = URLSession.shared.dataTask(with: urlRequest){(data, response, err) in
if err == nil{
guard let data = data else { return }
do{
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String: AnyObject]
guard let results = json["results"] as? NSArray else { return }
guard let firstResult = results[0] as? NSDictionary else { return }
guard let place_id = firstResult["place_id"] as? String else { return }
placeID = place_id
semaphore.signal()
}catch let jsonError{
print(jsonError.localizedDescription)
}
}
}
task.resume()
semaphore.wait()
return placeID
}
func getLocationInfoFromPlaceID(placeID: String) -> LocationInfo{
var cityName: String!
var stateName: String!
var countryName: String!
let semaphore = DispatchSemaphore(value: 0)
print("https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeID)&fields=address_components&key=\(googleAPIKey)")
let urlRequest = URLRequest(url: URL(string: "https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeID)&fields=address_components&key=\(googleAPIKey)")!)
let task = URLSession.shared.dataTask(with: urlRequest){(data, response, err) in
if err == nil{
guard let data = data else { return }
do{
print("hold on")
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String: AnyObject]
guard let result = json["result"] as? NSDictionary else { return }
guard let address_components = result["address_components"] as? NSArray else { return }
for n in 0...address_components.count-1 {
let temp = address_components[n] as? NSDictionary
let temp1 = temp?["types"] as? NSArray
let temp2 = temp1?[0] as? NSString
if temp2 == "locality"{
cityName = temp!["short_name"] as? String
}
if temp2 == "administrative_area_level_1"{
stateName = temp!["short_name"] as? String
}
if temp2 == "country"{
countryName = temp!["short_name"] as? String
}
}
semaphore.signal()
} catch let jsonError{
print(jsonError.localizedDescription)
}
}
}
task.resume()
semaphore.wait()
return LocationInfo(city: cityName, state: stateName, country: countryName)
}
func fetchWeather(locationInfo : LocationInfo) -> WeatherInfo{
var temperature: Double!
var weatherImageURL: String!
var precipitation: Double!
var time: Array<Any>!
let cityTemp = locationInfo.city.replacingOccurrences(of: " ", with: "%20")
let stateTemp = locationInfo.state.replacingOccurrences(of: " ", with: "")
let countryTemp = locationInfo.country.replacingOccurrences(of: " ", with: "")
let semaphore = DispatchSemaphore(value: 0)
print("http://api.apixu.com/v1/current.json?key=bd662599761d4279a0b30426192405&q=\(cityTemp)\(commaSpace)\(stateTemp)\(commaSpace)\(countryTemp)")
let urlRequest = URLRequest(url: URL(string: "http://api.apixu.com/v1/current.json?key=bd662599761d4279a0b30426192405&q=\(cityTemp)\(commaSpace)\(stateTemp)\(commaSpace)\(countryTemp)")!)
let task = URLSession.shared.dataTask(with: urlRequest){(data, response, error) in
if error == nil{
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
guard let current = json["current"] as? [String: AnyObject] else { return }
guard let temp = current["temp_f"] as? Double else { return }
temperature = temp
guard let condition = current["condition"] as? [String: AnyObject] else { return }
let icon = condition["icon"] as! String
weatherImageURL = "http:\(icon)"
guard let precipIn = current["precip_in"] as? Double else { return }
precipitation = precipIn
guard let location = json["location"] as? [String: AnyObject] else { return }
let timeStamp = location["localtime"]
let sepTimeStamp = timeStamp?.components(separatedBy: " ")
let tempTime = sepTimeStamp?[1]
time = Array(tempTime!)
semaphore.signal()
} catch let jsonError{
print(jsonError.localizedDescription)
}
}
}
task.resume()
semaphore.wait()
let weatherImage = UIImageView(image: downloadImage(weatherImageURL : weatherImageURL))
return WeatherInfo(precipitation: precipitation, temperature: temperature, weatherImage: weatherImage, time: time)
}
func downloadImage(weatherImageURL : String) -> UIImage{
var weatherImage = UIImage()
let semaphore = DispatchSemaphore(value: 0)
let url = URL(string: weatherImageURL)!
let task = URLSession.shared.dataTask(with: url){(data, response, err) in
if err == nil{
//guard let data = data else { return }
print(data)
print(weatherImageURL)
weatherImage = UIImage(data: data!)!
semaphore.signal()
}
}
task.resume()
semaphore.wait()
return weatherImage
}
}
我的LocationWeatherVM类:
import Foundation
import UIKit
struct LocationWeatherVM{
//Location Variables
let city: String
let state: String
let country: String
//Weather Variables
let isRainy: Bool
let temperature: Double
let weatherImage: UIImageView
//let greeting: String
//let greetingMsg: String
init (locationInfo: LocationInfo, weatherInfo: WeatherInfo){
self.city = locationInfo.city
self.state = locationInfo.state
self.country = locationInfo.country
self.temperature = weatherInfo.temperature
self.weatherImage = weatherInfo.weatherImage
if weatherInfo.precipitation > 0{
self.isRainy = true
}else{
self.isRainy = false
}
}
}
当然,现在我还没有包括所有内容,只是现在才开始重新构建所有内容。我要使用MVVM,因此如果您认为我的结构不正确,也可以对此发表评论。
现在,我只是想显示当前的用户位置和天气信息,这是OG应用在启动时正在执行的操作,而不是加载旧的搜索位置(如果有的话),但这完全是另外一回事了。谢谢大家:D