我正在尝试遍历字符串数组并触发 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 > 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 调用以重新加载集合视图的主要内容,如果未能解决你的问题,请参考以下文章