返回

iOS: API数据传递至新视图控制器Label不显示?问题排查和解决方案

IOS

API 数据传递至新视图控制器的问题

在使用 API 获取数据后,将数据传递到新的视图控制器(view controller)是常见操作,但有时可能会出现数据显示不出来的问题,尤其是在 UILabel 等控件上。以下是一些导致该问题的原因,并提供对应的解决方案。

常见原因与对应方案

1. 委托代理(delegate)未正确设置

问题: WeatherManager 通过委托 (delegate) 将数据传递给视图控制器。如果视图控制器的委托代理没有正确设置,则收不到数据更新。
分析: 当一个实例方法回调另一个类的实例方法时,需要有delegate的配合。委托代理是建立双向沟通的一种常用机制,使用delegate的时候需要主要以下几个部分。 首先需要建立delegate,接着使用代理接受数据的controller(或其他的class),必须是实现了协议定义的类。
解决方案:

  • ReportViewController 中, 确保 WeatherManagerdelegate 已被正确赋值为当前控制器实例。通常,这在 viewDidLoad 中完成。
// ReportViewController.swift
import UIKit

class ReportViewController: UIViewController, WeatherManagerDelegate {
    var weatherManager = WeatherManager()
    
    //... other properties

    override func viewDidLoad() {
        super.viewDidLoad()
        weatherManager.delegate = self // 正确设置 delegate
        
    }
//... Other Methods
}

2. 数据回调的线程问题

问题: API 调用通常在后台线程完成。如果 UI 更新尝试在后台线程执行,会导致 UI 不更新。
分析: UI更新必须发生在主线程(main thread)。异步任务完成以后默认线程不是在主线程中,所以UILabel并不能立即被更新。
解决方案:

  • 使用 DispatchQueue.main.async 将 UI 更新操作放回主线程。这确保 UILabel 的更新操作在主线程中执行,保证了UI刷新。
   func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
        
        DispatchQueue.main.async {
            self.flightRulesValueLabel.text = weather.flightConditions
            print(weather.flightConditions)
             //其他 Label 更新也放在这里。
         }
 }

3. 数据传递时机错误

问题描述:WxViewController 中发起了 API 请求,但在 ReportViewController 中尝试接收更新。这导致数据未能在 ReportViewController 显示。
分析: ReportViewController是需要显示数据的一方,应该ReportViewController一方发起request。
解决方案:

  1. 直接在 ReportViewController 中调用 fetchWeather:
  • ReportViewController 中,在需要获取天气数据的时机调用 weatherManager.fetchWeather()。可以将 ReportViewController 设置为 UITextFieldDelegate,并在用户在输入框完成搜索后调用 fetchWeather() 方法。或者可以考虑将搜索栏(UITextField)与按钮放到ReportViewController里面。以下代码展示如何实现以上要求。
// ReportViewController.swift
import UIKit

class ReportViewController: UIViewController, WeatherManagerDelegate, UITextFieldDelegate{
    
    var weatherManager = WeatherManager()
    @IBOutlet weak var stationSearch: UITextField!
    
    //... other outlets

    override func viewDidLoad() {
        super.viewDidLoad()
        weatherManager.delegate = self
        stationSearch.delegate = self // 设置 ReportViewController为 UITextField 的委托
       
        
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print(stationSearch.text!)
        stationSearch.endEditing(true)
        return true
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        if let station = stationSearch.text {
             weatherManager.fetchWeather(stationICAO: station) // 从这里请求数据
        }

        stationSearch.text = ""
    }
    
     func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
         
         DispatchQueue.main.async {
           self.flightRulesValueLabel.text = weather.flightConditions
         print(weather.flightConditions)
         //Other data Update labels...
            }
            
            
     }
}
  • 修改 WxViewController : WxViewController只作为展示页。可以删除WeatherManagerDelegate的实现。

  • 注意: 使用新的实现,记得把ReportViewController初始化的地方改成从 WxViewController pushViewController时创建。例如

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "toReportVC"{
      let reportVC = segue.destination as! ReportViewController
      //reportVC 的参数可以在这里添加传递

  }
}
  1. 共享 WeatherManager 对象: 如果需要保持 WxViewControllerReportViewController 使用同一个 WeatherManager 实例,请使用如下方法。 确保在它们初始化时使用同一个对象实例。 可以考虑在整个应用程序中使用一个单例 WeatherManager 或者利用属性传递在不同ViewController共享实例,而不是分别创建它们。这样它们将访问相同的 API 数据。此方法并非本题的首选方法,但是作为提供一种新的思路来分析API数据。
// App level scope. 在整个app里面都是singleton

class AppState {
     static let shared = AppState()
     let sharedWeatherManager = WeatherManager()

      private init(){}
 }


 // Usage in ViewController.
 //使用`AppState`实例的方法去访问`WeatherManager`

 class WxViewController :UIViewController {
  let weatherManager =  AppState.shared.sharedWeatherManager

//  ... code using weatherManager


 }
class ReportViewController : UIViewController {
     let weatherManager = AppState.shared.sharedWeatherManager

     override func viewDidLoad() {
         super.viewDidLoad()
          weatherManager.delegate = self // 必须是相同的实例!
     }
 }

安全提示

  • 错误处理: 务必实现 didFailWithError 方法处理 API 请求失败的情况,并在用户界面上显示友好的错误消息。
  • 数据验证:parseJSON 函数中进行必要的数据验证,防止 JSON 解析错误导致崩溃。

以上方案针对 API 数据未成功传递到新视图控制器的常见原因,请按照实际情况选择使用。如果仍有问题,可以继续提供更多的细节进行排查。