【问题标题】:Cannot transfer Data from VC1 to VC2 using protocols in Swift无法使用 Swift 中的协议将数据从 VC1 传输到 VC2
【发布时间】:2020-08-09 20:04:34
【问题描述】:

嗯,我使用了 2 个 VC,一个带有一个文本字段,用户在其中输入 CityName,另一个 VC 负责处理所有 UI 元素(如 temp、cityname 等),现在我还使用了一个NetWorkManager 负责处理所有网络和 JSON 内容。

问题是我试图将数据从 NetWorkManager 传输到 VC1,但由于某种原因委托无法正常工作 :( - 基本上道路应该是这样的:VC2 -> NetWorkManager -> VC1。

这是我的代码:

import Foundation

protocol NetworkManagerDelegate {
    func didUpdateWeather(weather: WeatherModel)
}


struct NetworkManager {
    
    let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=2da9980c9a43e21c2cdb1f28316d151d&units=metric"
    
    var delegate: NetworkManagerDelegate?
    
    func fetchWeather(cityName: String) {
     let urlString = "\(weatherURL)&q=\(cityName)"
     performRequest(urlString: urlString)
    }
    
    
    func performRequest(urlString: String) {
        if let url = URL(string: urlString) {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, respone, error) in
                if error != nil {
                    print(error!)
                }
                
                if let safeData = data {
                    if let weather = self.parseJSON(weatherData: safeData) {
                        print("Im not nil")
                        self.delegate?.didUpdateWeather(weather: weather)
                    }
                }
            }
            task.resume()
        }
        
    }
    
    
    
    func parseJSON(weatherData: Data) -> WeatherModel? {
        let decoder = JSONDecoder()
        do {
            let decodedData =  try decoder.decode(WeatherDataModel.self, from: weatherData)
            let id = decodedData.weather[0].id
            let cityName = decodedData.name
            let temp = decodedData.main.temp

            
            let weather = WeatherModel(conditionId: id, cityName: cityName ,temperatrue: temp)
            print("Temp is: \(weather.temperatrueString)")
            return weather
            
        } catch {
            print(error)
            return nil
        }
    }
      
    }

VC2:

import UIKit
import Foundation

class WeatherByCityController: UIViewController, UITextFieldDelegate {
    
//    func didUpdateWeather(weather: WeatherModel) {
//        print("Hi")
//    }
//

    
    @IBOutlet weak var cityTextField: UITextField!
    @IBOutlet weak var updateWeatherBtn: UIButton!
    
    var netWorkManager = NetworkManager()

    override func viewDidLoad() {
        super.viewDidLoad()
//        netWorkManager.delegate = self

        cityTextField.delegate = self

    }
    

    
     
    
    @IBAction func closeButtonTapped(_ sender: UIButton) {
        dismiss(animated: true, completion: nil)
    }
    
    
    
    
    @IBAction func updateWeatherByCityTapped(_ sender: UIButton) {
        //Calling delegate to update the City:
        
        
        
        //Dismiss the VC:
        dismiss(animated: true, completion: nil)

    }
    
    
    
    
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        cityTextField.endEditing(true)
        return true
        
    }
    
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        if let city = cityTextField.text {
            netWorkManager.fetchWeather(cityName: city)
        }
        cityTextField.text = ""
    }
    

}

VC1:

import UIKit
import Foundation
import CoreLocation


class WeatherScreen: UIViewController,NetworkManagerDelegate {
   
    
    
    
    
    //Objects outlets:
    @IBOutlet weak var conditionIcon: UIImageView!
    @IBOutlet weak var tempLabel: UILabel!
    @IBOutlet weak var cityLabel: UILabel!
    
    
    //TableView Outlet:
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var segeControl: UISegmentedControl!
    
    
    var models = [DailyWeatherEntry]()
    var hourlyModels = [HourlyWeatherEntry]()
    
    var netWorkManager = NetworkManager()
    
    override func viewDidLoad() {
        netWorkManager.delegate = self

        tableView.register(HourlyTableViewCell.nib(), forCellReuseIdentifier: HourlyTableViewCell.identifier)
        
        tableView.dataSource = self
    }
    
    
    override func viewWillAppear(_ animated: Bool) {
        // Load things once the view will appear
    }
    
    
    
    @IBAction func locationBtnTapped(_ sender: UIButton) {
        //Asking the user for a permission for using his location:
    }
    
    
    func didUpdateWeather(weather: WeatherModel) {
        print("Hi")
       }
       
    
    
    
    
}


extension WeatherScreen: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
            //creating the cell:
            let cell = tableView.dequeueReusableCell(withIdentifier: "weatherCell", for: indexPath) as! HourlyTableViewCell
            
            //Cell Configure:
        
        
            cell.textLabel!.font = UIFont.systemFont(ofSize: 10)
            
            
            return cell
        }
        
     
}

【问题讨论】:

    标签: ios swift delegates protocols


    【解决方案1】:

    VC1 的代码在哪里?如果没有 VC 1 的代码,很难给出答案。但是我会尝试回答,因为这可能是这种情况。

    所以基本上你要做的是从 VC2 进行网络调用,无论它应该在 VC1 中更新什么响应,它已经在其他地方处于活动状态。在这里,您只需将 NetworkManger 的委托设置为 VC1 实例。所以你必须在VC2中获取VC1的实例。

    var netWorkManager = NetworkManager()
    //Get this instance in your code
    var vc1: UIViewController!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Here you will be setting the delegate 
        // to VC1 where you will be having the delegate methods
        netWorkManager.delegate = vc1
    
        cityTextField.delegate = self
    
    }
    

    虽然这种方法有效,但我不建议以这种方式使用网络管理器。尝试使用完成处理程序而不是委托来获取数据,然后在视图控制器之间传递该值。

    编辑: 像这样在网络管理器 performRequest 函数中传递完成。

    func performRequest(urlString: String, completion: @escaping (Bool, String?, Error?) -> Void) {
    
         guard let url = URL(string: urlString) else {
             completion(false, nil, NSError(domain: "URLString is not a valid URL", code: 100, userInfo: nil))
             return
         }
    
         let session = URLSession(configuration: .default)
         let task = session.dataTask(with: url) { (data, respone, error) in
             guard let safeData = data, let weather =      self.parseJSON(weatherData: safeData) else {
                 print("Empty data or JSON parse error")
                 completion(false, nil, error)
             }
    
             print("Im not nil")
             completion(true, weather, nil)
         }
         task.resume()
    
     }
    

    并调用VC2中的api请求。

    @IBAction func updateWeatherByCityTapped(_ sender: UIButton) {
        //Calling the api request. Pass your url string here
        self. netWorkManager.performRequest(urlString: "") { (success, weather, error) in
              guard success else {
                   print(error as Any)
                   return
              }
              // Here you have got the weather data. 
              // Don't know what is weather model. so simply passing the weather string.
              self.delegate.didUpdateWeather(weather: weather)
              //Dismiss the VC:
              dismiss(animated: true, completion: nil)
        }
    
    }
    

    这里采用 NetworkManagerDelegate 到 VC1 并在呈现 VC2 之前将代理设置为 VC1。或者,如果您没有从 VC1 展示 VC2,则使用 UserNotifications 将天气数据发送到 VC1。

    【讨论】:

    • 顺便说一句,如果你能给我另一种方法来解决它,比如使用完成处理程序
    【解决方案2】:

    这行代码var netWorkManager = NetworkManager() 在每次调用时都会创建一个新的网络管理器实例。

    在您的情况下,当您在 VC2 中创建的网络管理器收到网络响应时,不会调用 VC1 中的网络管理器。它们是两个独立的实体。

    您可以考虑以下几点:

    1. 如果我正确理解您的场景,您的 VC2 用于从用户那里获取城市名称。 VC2真的需要进行网络调用吗?您可以将 VC2 限制为仅获取城市名称。

    2. 使 networkManager 成为单例。然后,您可以从代码中的多个位置调用它。 networkManger 可以具有“获取”的方法,并且可以接受 completionHandler(正如@Raja Vijaya kumar 所建议的那样。

    【讨论】:

    • 首先感谢您的回答 :),其次 - 是的,您的情况是正确的。 VC2 正在从用户那里获取 cityName,基本上不必这样做,我想换一种方式,只是将名称传递给 VC1,让 VC1 进行网络调用。并且关于将networkManager作为一个单身人士我很想看到一个例子,因为我对iOS开发有点初级:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多