0

I have this TatodexController that handles my Collection View code. It gets the data from an API call and save it on a pokemon var. This var is decoded with a Pokemon struct, that holds all the attributes of every pokemon (name, height, abilities, etc).

On my CollectionView, I can show the images, names and more info poke-related, but I added a feature that works this way:

  • When I longpress any pokemon cell, it shows up an Info View, that should show a chunk of pokemon info, but it's not getting the updated data from my TatodexController.

This is how the relevant code of my TatodexController looks like:

class TatodexController: UICollectionViewController, InfoViewDelegate {

var pokemon: Pokemon?
var pokemons = [Pokemon]()
let service = Service()
    . . .

let infoView: InfoView = {
    let view = InfoView()
    view.layer.cornerRadius = 5
    return view
}()

override func viewDidLoad() {
    super.viewDidLoad()
    fetchPokemons()
}

. . .

@objc func handleDismissal() {
    dismissInfoView(pokemon: nil)
}
}

The extension where I handle the parsing and some InfoView configuration is this one:

extension TatodexController {

. . .

func dismissInfoView(pokemon: Pokemon?) {
    UIView.animate(withDuration: 0.5, animations: {
        self.visualEffectView.alpha = 0
        self.infoView.alpha = 0
        self.infoView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
    }) { (_) in
        self.infoView.removeFromSuperview()
        self.navigationItem.rightBarButtonItem?.isEnabled = true
        guard let pokemon = pokemon else { return }
        self.showInfoController(withPoke: pokemon)
    }
}

func fetchPokemons() {
    service.fetchPokes { (result) in
        DispatchQueue.main.async {
            
            switch result {
            case .success(let poke):
                self.pokemon = poke
                self.pokemons.append(poke)
                self.collectionView.reloadData()
                
                self.pokemons.sort { (poke1, poke2) -> Bool in
                    return poke1.name! < poke2.name!
                    
                }
            case .failure(let error):
                
                let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
                 alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
                 self.show(alert, sender: nil)
            }
            
        }
    }
}
}

And the extension that configures my InfoViewDelegate is:

    extension TatodexController: TatodexCellDelegate {
    
    func presentInfoView(withPokemon pokemon: Pokemon) {

        configureSearchBar(showSearch: false)
        navigationItem.rightBarButtonItem?.isEnabled = false

        view.addSubview(infoView)
        infoView.configureViewComponents()
        infoView.delegate = self
        infoView.pokemon = self.pokemon
        configureInfoView()
    }
    
    func configureInfoView() {
        
        infoView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: view.frame.width - 64, height: 480)
        infoView.layer.cornerRadius = view.frame.width / 6
        infoView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        infoView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -40).isActive = true
        
        infoView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
        infoView.alpha = 0
        
        UIView.animate(withDuration: 0.3) {
            self.visualEffectView.alpha = 1
            self.infoView.alpha = 1
            self.infoView.transform = .identity
        }
        
    }
}

The relevant code of my InfoView class is this:

    protocol InfoViewDelegate {
    func dismissInfoView(pokemon: Pokemon?)
}

class InfoView: UIView {
    
    var delegate: InfoViewDelegate?
    var pokemon: Pokemon? {
        didSet {
                guard let pokemon   = self.pokemon,
                      let type      = pokemon.types,
                      let typeName  = type[0].type?.name,
                      let defense   = pokemon.defense,
                      let attack    = pokemon.attack,
                      let id        = pokemon.id,
                      let height    = pokemon.height,
                      let weight    = pokemon.weight,
                      let imageUrl  = pokemon.sprites?.front else { return }
                    
                if id == pokemon.id {
                    imageView.kf.setImage(with: URL(string: imageUrl))
                }

                nameLabel.text = pokemon.name?.capitalized
                
                configureLabel(label: typeLabel,      title: "Type",        details: "\(typeName)")
                configureLabel(label: defenseLabel,   title: "Defense",     details: "\(defense)")
                configureLabel(label: attackLabel,    title: "Base Attack", details: "\(attack)")
                configureLabel(label: heightLabel,    title: "Height",      details: "\(height)")
                configureLabel(label: weightLabel,    title: "Weight",      details: "\(weight)")
                configureLabel(label: pokedexIdLabel, title: "Pokedex Id",  details: "\(id)")
        }
    }
    
    let skillLabel: UILabel = {
        let label = UILabel()
        return label
    }()
    
    let imageView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFill
        return iv
    }()
    
    lazy var nameContainerView: UIView = {
        let view = UIView()
        view.backgroundColor = Colors.softRed
        view.addSubview(nameLabel)
        view.layer.cornerRadius = 5
        nameLabel.center(inView: view)
        return view
    }()
    
    let nameLabel: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.font = UIFont.systemFont(ofSize: 24, weight: .thin)
        label.text = "Lucario"
        return label
    }()
    
    . . .

    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc func handleViewMoreInfo() {
        guard let pokemon = self.pokemon else { return }
        delegate?.dismissInfoView(pokemon: pokemon)
    }

    . . .
}

And of course, in this last chunk of code is where I'm not getting data. I think it's because I'm not connecting the pokemon info of my TatodexController with my InfoView, but idk how to do it. For example, when I longpress any pokemon cell and the InfoView shows up, it says "Lucario" (that's my default value), and not the actual pokemon name.

I would realle appreciate any help or advice. If anyone needs another chunk of code to look at, I'd be happy to update this post.

EDIT 1: handleLongPress func at TatodexCell

    @objc func handleLongPress(sender: UILongPressGestureRecognizer) {
    if sender.state == .began {
        guard let poke = self.pokemon else { return }
        delegate?.presentInfoView(withPokemon: poke)
    }
}
3rnestocs
  • 23
  • 7
  • From your code it's unclear who is calling "presentInfoView", can it's parameter "pokemon" be nil? – Mikhail Vasilev Dec 06 '20 at 19:47
  • I'm calling it on my handleLongPress func, at my TatodexCell (where I configure every cell of my CollectionView). I edited the post with that func at the end – 3rnestocs Dec 06 '20 at 20:41
  • You're sending pokemon to presentInfoView, but inside this method you're using TatodexController's pokemon. Try changing "infoView.pokemon = self.pokemon" to "infoView.pokemon = pokemon" – Mikhail Vasilev Dec 06 '20 at 20:47
  • Still not working – 3rnestocs Dec 06 '20 at 21:09
  • Any changes that some of the pokemon's field is empty? Because you have very big guard InfoView.pokemon.didSet. Anyway, the easiest way is to set breakpoints and debug whole flow – Mikhail Vasilev Dec 06 '20 at 21:12
  • See, my CollectionView has the correct information, but somehow I'm not sending that same info to my infoView. It doesn't shows anything because my pokemon var is nil, but can't figure out how to change it. – 3rnestocs Dec 06 '20 at 21:19
  • And to answer your question, noup, every cell has it's corresponding pokemon, with image and name. The problem is just in the infoView. – 3rnestocs Dec 06 '20 at 21:20
  • Start debugging by putting breakpoint in pokemon didSet method, before any guards. If will be nil, navigate back in calls to see in which place you're losing you pokemon. If it's not nil before guards, check what kind of field is missing in a pokemon. (for example - is pokemon.sprites?.front not nil?) – Mikhail Vasilev Dec 06 '20 at 21:29
  • I'm quite bad at debugging. I've got nil at my didSet method, how can I navigate back on my calls? – 3rnestocs Dec 06 '20 at 21:46
  • What I managed to do was making print statements on my `handleLongPress ` and `presentInfoView ` functions, and both had data. – 3rnestocs Dec 06 '20 at 21:52
  • Just to clarify: 1) you have pokemon = nil in didSet method right *before* "guard let" statements? 2) you have pokemon != nil in presentInfoView method? 3) in presentInfoView you're sending pokemon, *not self.pokemon* to infoView, right? – Mikhail Vasilev Dec 06 '20 at 22:17
  • About debugging, after breakpoint fired, you can see stacktrace on the left (in navigator). And you can select every step in stacktrace and print in console all you need – Mikhail Vasilev Dec 06 '20 at 22:18
  • If you feel yourself weak in debugging - it's obligatorily to read or see some manuals about it, it's hard to imaging development without it – Mikhail Vasilev Dec 06 '20 at 22:19
  • 1) yes 2) yes 3) also yes – 3rnestocs Dec 06 '20 at 22:36
  • Nevermind. I did what u told me and figured attack and defense attributes where nil, I fixed it. Thanks four your help, Mikhail! – 3rnestocs Dec 06 '20 at 22:46
  • That's good, you're welcome! – Mikhail Vasilev Dec 06 '20 at 22:48

0 Answers0