1

I am trying to make an Amazon clone. I am working on the products details page. basically you search, you click the tableviewcell for the item you want and you land on the details page. everything loads except for the collectionview which contains the images. when I remove the stackview everything loads fine but when I embed the view's contents into the stackview the collectionviewcell disappears. the cellForItemAt method doesn't even get called. I don't know why and I could really use some help.

edit: it seems the imageView in the collection cell css unwrapping as nil. I'm not sure why. I connected it to the cell from the storyboard. not sure why it's not working. I turned it into an optional cell.imagioView?.kf.setImage(with: imageUrl) but the collection view loads without the images inside. I finally get the horizontal scrollable action but the images don't load. I'm printing the url for the image when it comes into focus but it cannot be added to the cell. it seems the collection cell is loading but the image can't be added for some reason.

here's the view controller that is supposed to display it

class ProductViewController: UIViewController, UICollectionViewDelegate {

@IBOutlet var stackedView: UIStackView!
@IBOutlet weak var detailedView: UIView!
@IBOutlet var detailPageView: UIScrollView!
@IBOutlet var imagesCarouselCollection: UICollectionView!

@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var desc: UILabel!
@IBOutlet weak var features: UILabel!
@IBOutlet weak var price: UILabel!
@IBOutlet weak var starsLabel: UILabel!
@IBOutlet weak var noOfReviews: UILabel!
@IBOutlet weak var addToCartBtn: UIButton!
@IBOutlet weak var buyNowBtn: UIButton!

var searchedText: String = ""
var asinForSearch: String = ""
var resultsManager = ResultsManager()
var productDeets: Products?
var imagesArray = Array<String>()

override func viewDidLoad() {
    super.viewDidLoad()

    resultsManager.detailsDelegate = self
    search()
    self.setupUI()
    loadingIndicator.isAnimating = true
    imagesCarouselCollection.delegate = self
    imagesCarouselCollection.dataSource = self

imagesCarouselCollection.register(ImageCarouselViewCell.self, forCellWithReuseIdentifier: "ImageCarouselCollectionCell")
        imagesCarouselCollection.frame = imagesCarouselCollection.bounds

 DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.populatePage()
}
}

@IBAction func buyNow(_ sender: Any) {

}
@IBAction func addToCart(_ sender: Any) {

}

func search(){
    resultsManager.getDetails(asinForSearch)
}

func createFeaturesLabels(featuresArray: [String]) -> String {
    var newFeatureString = ""
    for feature in featuresArray {
        newFeatureString.append(feature + "\n")
    }
    return newFeatureString
}

func populatePage(){
    if let productResults = self.productDeets {
        self.titleLabel.text = productResults.title
        self.desc.text = productResults.productDescription
        self.imagesArray = productDeets!.images
        self.features.text = createFeaturesLabels(featuresArray: productResults.featureBullets)
        self.price.text = "$" + String(productResults.price.currentPrice)
        self.starsLabel.text = productResults.reviews.rating
        self.noOfReviews.text = String(productResults.reviews.totalReviews)

        self.loadingIndicator.isAnimating = false
        self.stackedView.isHidden = false
    }
}

// MARK: - UI Setup for loading icon
private func setupUI() {
    if #available(iOS 13.0, *) {
        overrideUserInterfaceStyle = .light
    }

    self.stackedView.isHidden = true
    self.detailPageView.backgroundColor = .white

    self.detailPageView.addSubview(loadingIndicator)

    NSLayoutConstraint.activate([
        loadingIndicator.centerXAnchor
            .constraint(equalTo: self.view.centerXAnchor),
        loadingIndicator.centerYAnchor
            .constraint(equalTo: self.view.centerYAnchor),
        loadingIndicator.widthAnchor
            .constraint(equalToConstant: 50),
        loadingIndicator.heightAnchor
            .constraint(equalTo: self.loadingIndicator.widthAnchor)
    ])
}

// MARK: - Properties
let loadingIndicator: ProgressView = {
    let progress = ProgressView(colors: [.red, .systemGreen, .systemBlue], lineWidth: 5)
    progress.translatesAutoresizingMaskIntoConstraints = false
    return progress
}()
}

extension ProductViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width: CGFloat = imagesCarouselCollection.bounds.size.width
        let height: CGFloat = imagesCarouselCollection.bounds.size.height
        return CGSize(width: width, height: height)
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCarouselCollectionCell", for: indexPath) as! ImageCarouselViewCell
        let imageUrl = URL(string: imagesArray[indexPath.item])
        cell.imagioView.kf.setImage(with: imageUrl)
        return cell
    }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    print(imagesArray.count)
    return imagesArray.count
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}
}

extension ProductViewController: ResultsDetailDelegate {
    func updateDetails(_ resultsManager: ResultsManager, products: Products) {
        self.productDeets = products
    }
}

class ImageCarouselViewCell: UICollectionViewCell {

    @IBOutlet weak var imagioView: UIImageView!

}

thestoryboard the controller loading without the collectionview

zamanada
  • 158
  • 13
  • when you embed to stackview then what is your array count? – Yogesh Patel Sep 14 '21 at 04:57
  • Are you using xib then please register nib like this let nib = UINib(nibName: "ImageCarouselCollectionCell", bundle: nil) imagesCarouselCollection.register(nib, forCellReuseIdentifier: "ImageCarouselCollectionCell") – Yogesh Patel Sep 14 '21 at 04:57
  • @YogeshPatel I am not using a xib – zamanada Sep 14 '21 at 05:00
  • ok so what's the array count? and also add background colour to UICollectionview and check. Do one thing add constant height to your collection view like 100 and see the output comes or not. – Yogesh Patel Sep 14 '21 at 05:01
  • @YogeshPatel 0. but if I move the imagesCarouselCollection.delegate/datasource too the async method I get can error in cellForItemAt saying there is nil value when unwrapping the imageURL in the cell.imagioView.kf.setImage line – zamanada Sep 14 '21 at 05:03
  • when the url is nil provide placeholder image like this if let imageUrl = URL(string: imagesArray[indexPath.item]){ cell.imagioView.kf.setImage(with: imageUrl) }else{ cell.imagioView.image = UIImage(named: "placeholderImage") } – Yogesh Patel Sep 14 '21 at 05:08
  • please try to add constant fix height once. – Yogesh Patel Sep 14 '21 at 05:09
  • @YogeshPatel add the constant fix height to what? the stackview? the collectionview? – zamanada Sep 14 '21 at 05:15
  • first give 100 height to collectionview – Yogesh Patel Sep 14 '21 at 05:19
  • @YogeshPatel sorry I'm still unclear about what you're saying. where are you suggesting I add a height constraint? – zamanada Sep 14 '21 at 05:21
  • Ok simply add height constrain to collectionview – Yogesh Patel Sep 14 '21 at 05:22
  • @YogeshPatel it doesn't matter. the if let for the imageURL causes it to fail. for whatever reason I cannot connect to the cell's image view. I've created an IBOutlet for the imageView in my collectionviewcell but it is unwrapping was nil. I'm able to print the url but it says there is no imageView. I've added the collection cell code – zamanada Sep 14 '21 at 05:32
  • @zamanada - if this line: `cell.imagioView.kf.setImage(with: imageUrl)` is crashing because `imagioView` is `nil`, disconnect and re-connect your `@IBOutlet` to that image view and see if that fixes it. – DonMag Sep 15 '21 at 14:24
  • @DonMag I tried that. it didn't work. I ended up deleting the imageview, creating a new nib with a xib that contained an imageView. then I followed a YouTube tutorial (this one in case you're interested or for future people who stumble on this: https://www.youtube.com/watch?v=eWGu3hcL3ww). I think my problem was I tried to treat it like a tableViewCell and then did 90% of a bunch of tutorials giving me all this sloppy, unreadable code. creating a UICollectionViewFlowLayout variable and then adding the appropriate CGSize values to it. – zamanada Sep 15 '21 at 17:36
  • 1
    @zamanada - if you found your problem (which was due to an incorrect approach) then you should either close or delete this question as it will not be helpful to other users. – DonMag Sep 15 '21 at 18:17

1 Answers1

1

I found the problem - it was with the layout. It was just as I suspected, half code from tutorials that a wasn't deleted. ended up trying to declare the collectionview and its contents in the storyboard and programmatically. it was fixed by following the layout advice in this tutorial https://www.youtube.com/watch?v=eWGu3hcL3ww

and for anyone else facing this problem here's the updated code.

class ProductViewController: UIViewController, UICollectionViewDelegate {

@IBOutlet var stackedView: UIStackView!
@IBOutlet weak var detailedView: UIView!
@IBOutlet var detailPageView: UIScrollView!
@IBOutlet var imagesCarouselCollection: UICollectionView!
@IBOutlet weak var imagesCarouselCollectionHeight: NSLayoutConstraint!

@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var desc: UILabel!
@IBOutlet weak var features: UILabel!
@IBOutlet weak var price: UILabel!
@IBOutlet weak var starsLabel: UILabel!
@IBOutlet weak var noOfReviews: UILabel!
@IBOutlet weak var addToCartBtn: UIButton!
@IBOutlet weak var buyNowBtn: UIButton!

var searchedText: String = ""
var asinForSearch: String = ""
var resultsManager = ResultsManager()
var productDeets: Products?
var imagesArray = Array<String>()

override func viewDidLoad() {
    super.viewDidLoad()

    resultsManager.detailsDelegate = self
    search()
    self.setupUI()
    loadingIndicator.isAnimating = true
    
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: 349, height: 410)
    imagesCarouselCollection.collectionViewLayout = layout
    layout.scrollDirection = .horizontal
    
    imagesCarouselCollection.register(ImageCollectionViewCell.nib(), forCellWithReuseIdentifier: ImageCollectionViewCell.identifier)
    imagesCarouselCollection.frame = imagesCarouselCollection.bounds

    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        self.imagesCarouselCollection.delegate = self
        self.imagesCarouselCollection.dataSource = self
        self.populatePage()
    }
}

@IBAction func buyNow(_ sender: Any) {
    
}

@IBAction func addToCart(_ sender: Any) {

}

func search(){
    resultsManager.getDetails(asinForSearch)
}

func createFeaturesLabels(featuresArray: [String]) -> String {
    var newFeatureString = ""
    for feature in featuresArray {
        newFeatureString.append(feature + "\n")
    }
    return newFeatureString
}

func populatePage(){
    if let productResults = self.productDeets {
        self.titleLabel.text = productResults.title
        self.desc.text = productResults.productDescription
        self.imagesArray = productDeets!.images
        self.features.text = createFeaturesLabels(featuresArray: productResults.featureBullets)
        self.price.text = "$" + String(productResults.price.currentPrice)
        self.starsLabel.text = productResults.reviews.rating + " out of 5 stars"
        self.noOfReviews.text = String(productResults.reviews.totalReviews) + " reviews"

        self.loadingIndicator.isAnimating = false
        self.stackedView.isHidden = false
    }
}

// MARK: - UI Setup for loading icon
private func setupUI() {
    if #available(iOS 13.0, *) {
        overrideUserInterfaceStyle = .light
    }

    self.stackedView.isHidden = true
    self.detailPageView.backgroundColor = .white

    self.detailPageView.addSubview(loadingIndicator)

    NSLayoutConstraint.activate([
        loadingIndicator.centerXAnchor
            .constraint(equalTo: self.view.centerXAnchor),
        loadingIndicator.centerYAnchor
            .constraint(equalTo: self.view.centerYAnchor),
        loadingIndicator.widthAnchor
            .constraint(equalToConstant: 50),
        loadingIndicator.heightAnchor
            .constraint(equalTo: self.loadingIndicator.widthAnchor)
    ])
}

// MARK: - Properties
let loadingIndicator: ProgressView = {
    let progress = ProgressView(colors: [.red, .systemGreen, .systemBlue], lineWidth: 5)
    progress.translatesAutoresizingMaskIntoConstraints = false
    return progress
}()
}

extension ProductViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCollectionViewCell.identifier, for: indexPath) as! ImageCollectionViewCell
        let imageUrl = URL(string: imagesArray[indexPath.item])
        cell.configure(with: imageUrl!)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return imagesArray.count
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
}

extension ProductViewController: ResultsDetailDelegate {
    func updateDetails(_ resultsManager: ResultsManager, products: Products) {
        self.productDeets = products
    }
}

I also moved away from just using a CocoaTouch class and created a new one with a xib/nib

class ImageCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var productImageView: UIImageView!
    static let identifier = "ImageCollectionViewCell"
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
    
    public func configure(with imageURL: URL) {
        productImageView.kf.setImage(with: imageURL)
    }
    
    static func nib() -> UINib {
        return UINib(nibName: "ImageCollectionViewCell", bundle: nil)
    }

}
zamanada
  • 158
  • 13