簡體   English   中英

斯威夫特:代表為零

[英]Swift : Delegate is nil

我試圖建立一個委托設計模式。 我在WeatherManager有一個簡單的delegate ,但它總是nil 我試圖在 WeatherViewController 的override func viewDidLoad()中添加WeatherViewController weatherManager.delegate = self 但是,我有另一個協議和委托,它工作正常。

我正在使用 API 從城市名稱中獲取地理坐標,並創建並執行獲取有關天氣信息的 API 的 URL。

天氣管理器

import Foundation

// delegate design parttern

protocol WeatherManagerDelegate:AnyObject {
    func didUpdateWeather(inputWeatherModel: WeatherModel)
}

class WeatherManager {
    
    // delegate
    weak var delegate: WeatherManagerDelegate?
    
    // fetchCoordinate
    func fetchWeather(urlString: String) {
        performRequest(inputURLString: urlString)
    }
    
    // performRequest
    func performRequest(inputURLString: String) {
        // 1. Create URL
        if let url = URL(string: inputURLString) {
            // 2. Create URLSession
            let session = URLSession(configuration: .default)
            // 3. Give the URLSession a task
            let task = session.dataTask(with: url) { (data, response, error) in
                if error != nil {
                    print(error!)
                    return
                }
                if let safeData = data {
                    if let weather = self.parseJSON(JSONobject: safeData) {
                        // ---------------- problem here  -------------------
                        self.delegate?.didUpdateWeather(inputWeatherModel: weather)
                    }
                }
            }
            // 4. Start the task
            task.resume()
        }
    }
    
    // parse JSON object
    func parseJSON(JSONobject: Data) -> WeatherModel? {
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode(WeatherData.self, from: JSONobject)
            
            let id = decodedData.weather[0].id
            let temp = decodedData.main.temp
            let name = decodedData.name
            
            let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
            return weather
            
        } catch {
            print(error)
            return nil
        }
    }

}

天氣模型

import Foundation

struct WeatherModel {
    let conditionId: Int
    let cityName: String
    let temperature: Double
    var temperatureString: String {
        return String(format: "%.1f", temperature)
    }
    
    var conditionName: String {
        switch conditionId {
        case 200...232:
            return "cloud.bolt"
        case 300...321:
            return "cloud.drizzle"
        case 500...531:
            return "cloud.rain"
        case 600...622:
            return "cloud.snow"
        case 701...781:
            return "cloud.fog"
        case 800:
            return "sun.max"
        case 801...804:
            return "cloud.bolt"
        default:
            return "cloud"
        }
    }
}

天氣視圖控制器

import UIKit

class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
    
    @IBOutlet weak var conditionImageView: UIImageView!
    @IBOutlet weak var temperatureLabel: UILabel!
    @IBOutlet weak var searchTextField: UITextField!
    @IBOutlet weak var cityLabel: UILabel!
    
    let weatherManager = WeatherManager()
    var coordinateManager = CoordinateManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        searchTextField.delegate = self
        searchTextField.keyboardType = .asciiCapable
        weatherManager.delegate = self
    }

    @IBAction func searchPressed(_ sender: UIButton) {
        searchTextField.endEditing(true)
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        searchTextField.endEditing(true)
        return true
    }
    
    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        if textField.text != "" {
            return true
        } else {
            textField.placeholder = "Type Something"
            return false
        }
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        
        if let city = searchTextField.text {
            coordinateManager.fetchCoordinate(cityName: city)
        }
        searchTextField.text = ""
    }
    
    func didUpdateWeather(inputWeatherModel: WeatherModel) {
        print(inputWeatherModel.temperature)
    }
}

坐標管理器

import Foundation

struct CoordinateManager {

    // WeatherManager
    let weathetManager = WeatherManager()
    // geographical coordinates(lat, lon) 地理座標
    let coordinateURL = "https://api.openweathermap.org/geo/1.0/direct?limit=1&appid=c8e60c6317c653a1789294c00f54ae19"
    
    // fetchCoordinate
    func fetchCoordinate(cityName: String) {
        let urlString = "\(coordinateURL)&q=\(cityName)"
        performRequest(inputURLString: urlString)
    }
    
    // transformURLString
    func transformURLString(_ string: String) -> URLComponents? {
        guard let urlPath = string.components(separatedBy: "?").first else {
            return nil
        }
        var components = URLComponents(string: urlPath)
        if let queryString = string.components(separatedBy: "?").last {
            components?.queryItems = []
            let queryItems = queryString.components(separatedBy: "&")
            for queryItem in queryItems {
                guard let itemName = queryItem.components(separatedBy: "=").first,
                      let itemValue = queryItem.components(separatedBy: "=").last else {
                        continue
                }
                components?.queryItems?.append(URLQueryItem(name: itemName, value: itemValue))
            }
        }
        return components!
    }
    
    // performRequest
    func performRequest(inputURLString: String) {
        // 1. Create URL
        let components = transformURLString(inputURLString)
        if let url = components?.url {
            // 2. Create URLSession
            let session = URLSession(configuration: .default)
            // 3. Give the URLSession a task
            let task = session.dataTask(with: url) {(data, response, error) in
                if error != nil {
                    print(error!)
                    return
                }
                if let safeData = data {
                    self.parseJSON(JSONobject: safeData)
                }
            }
            // 4. Start the task
            task.resume()
        }
    }

    // parseJSON
    func parseJSON(JSONobject: Data) {
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode([CoordinateData].self, from: JSONobject)
            let name = decodedData[0].name
            let lat = decodedData[0].lat
            let lon = decodedData[0].lon
            
            let coordinateModel = CoordinateModel(name: name, lat: lat, lon: lon)
            let weatherURL = coordinateModel.urlString

           // ------- WeatherManager fetchWeather ---------
           weathetManager.fetchWeather(urlString: weatherURL)
        } catch {
            print(error)
        }
    }
}

坐標模型

import Foundation

struct CoordinateModel {
    let name: String
    let lat: Double
    let lon: Double
    
    // wheather 地理座標を元に検索した天気の情報
    let weatherstr = "https://api.openweathermap.org/data/2.5/weather?"
    let str = "units=metric&appid=c8e60c6317c653a1789294c00f54ae19#"

    // computedproperty
    var urlString: String {
        return "\(weatherstr)&lat=\(lat)&lon=\(lon)&\(str)"
    }
}

您在 WeatherViewController 中創建一個 weatherManager,並在 viewDidLoad 方法中將 WeatherViewController 設置為您在此處創建的 weatherManager 實例的委托。 在 CoordinateManager 類中,您創建了一個新的 weatherManager,但不為其設置委托。

2個解決方案,用你最方便的:

  1. 為 CoordinateManager 創建一個初始化程序,將您創建的並且正在收聽的 WeatherManager 傳遞給它
// CoordinateManager class

let weatherManager: WeatherManager

init(weatherManager: WeatherManager) {
   self.weatherManager = weatherManager
}
// WeatherViewController class

let weatherManager = WeatherManager()
var coordinateManager = CoordinateManager(weatherManager: weatherManager)
  1. 您可以直接將 WeatherViewController 設置為所需 weatherManager 實例的委托。
// WeatherViewController class

override func viewDidLoad() {
   super.viewDidLoad()
   searchTextField.delegate = self
   searchTextField.keyboardType = .asciiCapable
   // Instead weatherManager.delegate = self
   coordinateManager.weatherManager.delegate = self
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM