I have 4 categories in segmented control. When i press on one of them collection view should show products in particular category. But instead of reusing cells and show only new items, collection view shows old items and in the end adds new cells with new items.
Where is proper place to use reloadData(). Or maybe i'm missing something?
Here is my code
private lazy var segmentedControl: UISegmentedControl = {
var control = UISegmentedControl(items: ["All", "Men", "Women", "Jewelery", "Electro"])
control.selectedSegmentTintColor = .black
control.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
control.tintColor = .white
control.selectedSegmentIndex = 0
control.addTarget(self, action: #selector(segmentChanged(_ :)), for: .valueChanged)
return control
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
networkManager.delegate = self
setupMainStackView()
setupCollectionView()
networkManager.loadProducts(category: .all)
performSearch()
}
func performSearch() {
if let category = NetworkManager.Category(rawValue: segmentedControl.selectedSegmentIndex) {
networkManager.loadProducts(category: category)
collectionView.reloadData()
}
// collectionView.reloadData()
}
@objc func segmentChanged(_ sender: UISegmentedControl) {
performSearch()
}
// MARK: - Data Source extension MainViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return productResults.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MainViewCollectionViewCell.identifier, for: indexPath) as! MainViewCollectionViewCell
let listOfProduct = productResults[indexPath.row]
cell.configure(for: listOfProduct)
return cell
}
}
And here is how i search category
enum Category: Int {
case all = 0
case menSClothing = 1
case womenSClothing = 2
case electronics = 3
case jewelery = 4
var type: String {
switch self {
case .all: return ""
case .menSClothing: return "category/men's clothing"
case .womenSClothing: return "category/women's clothing"
case .electronics: return "category/electronics"
case .jewelery: return "category/jewelery"
}
}
}
private func fakeStoreURL(category: Category) -> URL {
let kind = category.type
let url = URL(string: "https://fakestoreapi.com/products/" "\(kind)")
return url!
}
func loadProducts(category: Category) {
let url = fakeStoreURL(category: category)
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print(error)
return
}
guard let data = data else { return }
do {
var products = try JSONDecoder().decode([Product].self, from: data)
products = self.parse(data: data)
print(products)
DispatchQueue.main.async {
self.delegate?.didSendProductData(self, with: products)
}
} catch {
print(error)
}
}.resume()
}
CodePudding user response:
Problem is here
networkManager.loadProducts(category: category)
collectionView.reloadData()
loadProducts
is an asynchronous method , and reload of the collection occurs before the network data returns , so you need a completion
func loadProducts(category: Category,completion:@escaping([Product]? -> ())) {
let url = fakeStoreURL(category: category)
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print(error)
completion(nil)
return
}
guard let data = data else { return }
do {
var products = try JSONDecoder().decode([Product].self, from: data)
products = self.parse(data: data)
print(products)
DispatchQueue.main.async {
completion(products)
}
} catch {
print(error)
completion(nil)
}
}.resume()
}
Then
networkManager.loadProducts(category: category) { [weak self] products in
guard let products = products else { return }
self?.productResults = products
self?.collectionView.reloadData()
}