我正在尝试遍历字符串数组并触发 api 调用以重新加载集合视图

Posted

技术标签:

【中文标题】我正在尝试遍历字符串数组并触发 api 调用以重新加载集合视图【英文标题】:I am trying to loop over an array of strings and firing api calls to reload the collection view 【发布时间】:2018-01-09 18:29:37 【问题描述】:
import UIKit
import GooglePlaces
import Alamofire
import CoreData

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate 
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return listData?.count ?? 0


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CityCollectionViewCell

    let city = listData![indexPath.row] as? NSDictionary
    let name = city?.object(forKey: "name") as? String
    let main = city?.object(forKey: "main") as! NSDictionary
    let temp = main.object(forKey: "temp") as? Double
    let date1 = city?.object(forKey: "dt")

    let date = Date(timeIntervalSince1970: date1 as! TimeInterval)
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(abbreviation: "GMT") //Set timezone that you want
    dateFormatter.locale = NSLocale.current
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm" //Specify your format that you want
    let strDate = dateFormatter.string(from: date)

    cell.cityLabel.text = name!
    cell.lastUpdatedLabel.text = strDate
    cell.tempLabel.text = "\(temp!)"

    return cell


func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 

    cv.deselectItem(at: indexPath, animated: true)

    let row = indexPath.row;
    let selectedCity = list![row];

    userDefaults?.set(selectedCity, forKey: "citySelection");
    self.performSegue(withIdentifier: "selectCity", sender: self);



@IBOutlet weak var cv: UICollectionView!

var userDefaults:UserDefaults?;

var list:NSMutableArray?
var listData:NSMutableArray?
let group = DispatchGroup()

override func viewDidLoad() 
    super.viewDidLoad()

    userDefaults = UserDefaults.standard;




@IBAction func addCity(_ sender: Any) 
    let autocompleteController = GMSAutocompleteViewController()
    autocompleteController.delegate = self
    let addressFilter = GMSAutocompleteFilter()
    addressFilter.type = .city
    autocompleteController.autocompleteFilter = addressFilter
    present(autocompleteController, animated: true, completion: nil)



override func viewDidAppear(_ animated: Bool) 
    super.viewDidAppear(animated)
    updateValues()


func updateValues()  
    let list = getSearchHistory()
    print(list)
        let count = list.count
    if count > 0
     
        for item in list 
            group.enter()
                getData(name: item as! String)
        
        group.notify(queue: .main, execute: 
            self.cv.reloadData()
        )
    


func getData(name: String)  
    let modified = name.replacingOccurrences(of: " ", with: "+")
    let url = "http://api.openweathermap.org/data/2.5/weather?q=\(modified)&APPID=-------"

    Alamofire.request(url, method: HTTPMethod.get).responseJSON(completionHandler: 
        (response) -> Void in

        let city = response.result.value as! NSDictionary;
        self.listData?.add(city)
        print(self.listData)
        self.group.leave()
    )


func addToSearchHistory(locationName:String) 

    let delegate = UIApplication.shared.delegate as! AppDelegate
    let managedContext = delegate.persistentContainer.viewContext;
    let entity = NSEntityDescription.insertNewObject(forEntityName: "SavedPlaces", into: managedContext)
    entity.setValue(locationName, forKey: "name")

    do 
        try managedContext.save();
    
    catch 
        print("Core data error");
    


func getSearchHistory() -> NSMutableArray 
    let returnData = NSMutableArray()
    let delegate = UIApplication.shared.delegate as! AppDelegate
    let managedContext = delegate.persistentContainer.viewContext;

    do 
        let req = NSFetchRequest<NSFetchRequestResult>(entityName: "SavedPlaces");

        let data = try managedContext.fetch(req) as! [NSManagedObject];

        for item in data 
            let name = item.value(forKey: "name") as? String;
            returnData.add(name!);
        
    
    catch 
        print("Core data error");
    

    return returnData;




 extension ViewController: GMSAutocompleteViewControllerDelegate 

// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) 
    self.addToSearchHistory(locationName: place.name)
    dismiss(animated: true, completion: nil)


func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) 
    // TODO: handle the error.
    print("Error: ", error.localizedDescription)


// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) 
    dismiss(animated: true, completion: nil)


// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) 
    UIApplication.shared.isNetworkActivityIndicatorVisible = true


func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) 
    UIApplication.shared.isNetworkActivityIndicatorVisible = false


 

我正在尝试使用核心数据从存储的数据中加载值,然后循环遍历字符串并调用 api,然后将其添加到我从中填充集合视图的新数组中。

问题:我正确填充了所有值(城市名称)列表,但在调用 api 并调用“UpdateUI”函数后,我只得到一个单元格。 gif file

【问题讨论】:

【参考方案1】:

请求尚未完成这是一个异步任务等待我将为您处理代码,这样每当有新响应出现时,tableView 将重新加载以反映这一点

   func updateValues()  
    let list = getSearchHistory()

    if !list.isEmpty
     
        for item in list 
                getData(name: item as! String)
        

    


func getData(name: String)  
    let modified = name.replacingOccurrences(of: " ", with: "+")
    let rr = NSMutableArray()
    let url = "http://api.openweathermap.org/data/2.5/weather?q=\(modified)&APPID=------------------"

    Alamofire.request(url, method: HTTPMethod.get).responseJSON(completionHandler: 
        (response) -> Void in

        let city = response.result.value as! NSDictionary;

        rr.add(city)
       self.listData.append(rr)

       DispatchQueue.main.async
        
               self.cv.reloadData()
                    



    )

还要注意一个非常重要的步骤,以响应您覆盖当前数组而不是附加到它

  self.listData = rr

应该是

 self.listData.append(rr)

无论是否发生负载,都会导致一个项目的永久显示

另外别忘了在 viewDidLoad 中初始化 listData

  listData = NSMutableArray()

尝试像这样解析api

-(void)getDataforCity:(NSString*)cityName


  NSURL*url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?APPID=%@&q=%@",openWeatherMapBaseURL,openWeatherMapAPIKey,cityName]];

 [NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) 

    if(error == nil)
    

        NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

       NSDictionary*main = json[@"main"];

        NSString*humidity = main[@"humidity"];

        NSString*pressure = main[@"pressure"];

        NSString*temp = main[@"temp"];

        NSString*tempMax = main[@"temp_max"];

        NSString*tempMin = main[@"temp_min"];

        NSArray*weatherArr = json[@"weather"];

        NSDictionary*weather = weatherArr[0];

        NSString*description = weather[@"description"];

        NSDictionary*wind = json[@"wind"];

        NSString*deg = wind[@"deg"];

        NSString*speed = wind[@"speed"];

        NSLog(@"humidity %@ : ",humidity);
        NSLog(@"pressure %@ : ",pressure);
        NSLog(@"temp %@ : ",temp);
        NSLog(@"tempMax %@ : ",tempMax);
        NSLog(@"tempMin %@ : ",tempMin);
        NSLog(@"description %@ : ",description);
        NSLog(@"deg %@ : ",deg);
        NSLog(@"speed %@ : ",speed);

       NSLog(@"dasdasddasdataioioio : %@",json);
    
    else
    
        NSLog(@"dasdasddasdata : %@",error);
    


].resume;


【讨论】:

是的。谢谢?? let count = list.count if count &gt; 0?!就做if !list.isEmpty 而且没关系,如果列表为空,for 循环不会做任何事情 但我最初将它添加到数组中。前面的那一行。 rr.add(city) 不,rr 是一个局部变量 let rr = NSMutableArray() 每次调用它都是空的【参考方案2】:

您可以在每个请求完成后通过在网络回调中调用reloadData 重新加载collectionView,尽管这有点低效,除非您真的需要在每个项目之后重新加载。如果您想等到所有数据都加载完毕后再重新加载collectionView,您可以考虑使用DispatchGroup。可以这样工作:

let dispatchGroup = DispatchGroup()

func updateValues()  
let list = getSearchHistory()
let count = list.count
    if count > 0 
        for item in list 
            dispatchGroup.enter() //Indicate that a new process is beginning in the dispatch group
            getData(name: item as! String)
        
        group.notify(queue: .main)  //When all processes in the group finish this code will execute
            self.cv.reloadData()
        
    


func getData(name: String)  
    let modified = name.replacingOccurrences(of: " ", with: "+")
    let url = "http://api.openweathermap.org/data/2.5/weather?q=\(modified)&APPID=------------------"
    Alamofire.request(url, method: HTTPMethod.get).responseJSON(completionHandler:  (response) -> Void in
        let city = response.result.value as! NSDictionary
        self.listData.append(rr)
        dispatchGroup.leave() //Indicate that a process is ending in the dispatch group
    )

【讨论】:

这不是一个好的用户体验,因为用户应该等待所有响应完成,特别是在这种情况下,数组大小可能是 50 或更多,因为他使用 openweathermap apis @Sh_Khan 用户可以决定他们想怎么做。有些人更喜欢加载屏幕,而不是奇怪的层叠集合视图加载,其中新项目不断弹出。 大多数用户喜欢这种级联负载,因为它让他们觉得应用程序是响应式的,这是互联网加载的问题,而不是等待所有响应,这让他们觉得应用程序很慢或无响应,好日子!! @Sh_Khan 感谢您的讨论!我只是觉得有多种选择总是好的。 @xxxxx 什么不起作用?你确定在你的 AlamoFire 回调中添加了.leave() 调用吗?另外 - 这将等到所有调用完成后再重新加载 collectionView。

以上是关于我正在尝试遍历字符串数组并触发 api 调用以重新加载集合视图的主要内容,如果未能解决你的问题,请参考以下文章

如何构造循环API调用以使JSON结果可用?

Gracenote API 调用以获取节目/节目

需要帮助使用 Alamofire 进行 POST 调用以检索 XML

Paypal API 调用以获取订单详情

刷新 OAuth2 访问令牌的正确范例

javascript函数调用以获取数据并在页面加载时显示到屏幕