我可以打印数据,但不能在Swift中将其分配给标签 1 fetchPokemons实施 2更新viewDidLoad() 3重构var pokemon: Pokemon? didSet观察者

问题描述

我将数据从API调用发送到了InfoController viewDidLoad。在那里,我能够安全地将其存储在skillName常量中,并打印出来,并通过控制台接收所有信息。

当我尝试将此变量分配给SkillLabel时出现问题。

override func viewDidLoad() {
    super.viewDidLoad()
    configureViewComponents()
    fetchPokemons { (names) in
        guard var skillName = names as? String else { return }
        self.pokemon?.skillName = skillName
        
        self.allNames = skillName
        print(self.allNames)
    }
}

在那里,当我打印allNames时,控制台将显示我需要的所有数据。数据如下所示:Data Example

我想使用此数据的外观的计算属性为:

var pokemon: Pokemon? {
    didSet {
        guard let id        = pokemon?.id else { return }
        guard let data      = pokemon?.image else { return }

        
        navigationItem.title = pokemon?.name?.capitalized
        infoLabel.text = pokemon?.description
        infoView.pokemon = pokemon
        
        if id == pokemon?.id {
            imageView.image = UIImage(data: data)
            infoView.configureLabel(label: infoView.skillLabel,title: "Skills",details: "\(allNames)")
        }
    }
}

PD: allNames是我在InfoController类级别具有的String变量。

这是我的应用在运行时的外观: PokeApp

我的目标是获取详细信息参数以显示skillName数据,但它返回nil,idk为什么。有什么建议吗?

EDIT1 :从我的服务类中获取Pokemon数据的函数是这个函数

func fetchPokemons(handler: @escaping (String) -> Void) {
    controller.service.fetchPokes { (poke) in
        dispatchQueue.main.async {
            self.pokemon? = poke
            
            guard let skills = poke.abilities else { return }
            
            for skill in skills {
                
                guard let ability = skill.ability else { return }
                
                guard var names = ability.name!.capitalized as? String else { return }
                
                self.pokemon?.skillName = names
                handler(names)
            }
        }
    }
}

EDIT2: InfoView类如下:

class InfoView: UIView {

// MARK: - Properties
var delegate: InfoViewDelegate?

//  This whole block assigns the attributes that will be shown at the InfoView pop-up
//  It makes the positioning of every element possible
var pokemon: Pokemon? {
    didSet {
        guard let pokemon   = self.pokemon else { return }
        guard let type      = pokemon.type else { return }
        guard let defense   = pokemon.defense else { return }
        guard let attack    = pokemon.attack else { return }
        guard let id        = pokemon.id else { return }
        guard let height    = pokemon.height else { return }
        guard let weight    = pokemon.weight else { return }
        guard let data      = pokemon.image else { return }
        
        if id == pokemon.id {
            imageView.image = UIImage(data: data)
        }
        nameLabel.text = pokemon.name?.capitalized
        
        configureLabel(label: typeLabel,title: "Type",details: type)
        configureLabel(label: pokedexIdLabel,title: "Pokedex Id",details: "\(id)")
        configureLabel(label: heightLabel,title: "Height",details: "\(height)")
        configureLabel(label: defenseLabel,title: "Defense",details: "\(defense)")
        configureLabel(label: weightLabel,title: "Weight",details: "\(weight)")
        configureLabel(label: attackLabel,title: "Base Attack",details: "\(attack)")
    }
}

let skillLabel: UILabel = {
    let label = UILabel()
    return label
}()

let imageView: UIImageView = {
    let iv = UIImageView()
    iv.contentMode = .scaleAspectFill
    return iv
}()
. . .
}

infoView.configureLabel是这样的:

func configureLabel(label: UILabel,title: String,details: String) {
    let attributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: "\(title):  ",attributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16),NSAttributedString.Key.foregroundColor: Colors.softRed!]))
    attributedText.append(NSAttributedString(string: "\(details)",attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16),NSAttributedString.Key.foregroundColor: UIColor.gray]))
    label.attributedText = attributedText
}

编辑3:结构设计

struct Pokemon: Codable {
    var results: [Species]?
    var abilities: [Ability]?
    var id,attack,defense: Int?
    var name,type: String?
...
}

struct Ability: Codable {
    let ability: Species?
}

struct Species: Codable {
    let name: String?
    let url: String?
}

解决方法

跳到Edit2段落以获得最终答案!

初始答案:

在控制器获取所有数据后,您的UI似乎没有更新。

由于您的所有UI配置代码都位于var pokemon / didSet内部,因此最好将其提取到单独的方法中。

private func updateView(with pokemon: Pokemon?,details: String?) {
    guard let id = pokemon?.id,let data = pokemon?.image else { return }

    navigationItem.title = pokemon?.name?.capitalized
    infoLabel.text = pokemon?.description
    infoView.pokemon = pokemon

    if id == pokemon?.id {
        imageView.image = UIImage(data: data)
        infoView.configureLabel(label: infoView.skillLabel,title: "Skills",details: details ?? "")
    }
}

现在您可以轻松拨打didSet

var pokemon: Pokemon? {
    didSet { updateView(with: pokemon,details: allNames) }
}

fetchPokemons完成

override func viewDidLoad() {
    super.viewDidLoad()
    configureViewComponents()
    fetchPokemons { (names) in
        guard var skillName = names as? String else { return }
        self.pokemon?.skillName = skillName

        self.allNames = skillName
        print(self.allNames)
        DispatchQueue.main.async {
            self.updateView(with: self.pokemon,details: self.allNames)
        }
    }
}

在主队列上进行任何UI设置都是非常重要的。

编辑:

获取功能可能会导致问题!您多次调用处理程序:

func fetchPokemons(handler: @escaping (String) -> Void) {
    controller.service.fetchPokes { (poke) in
        DispatchQueue.main.async {
            self.pokemon? = poke
            guard let skills = poke.abilities else { return }
            let names = skills.compactMap { $0.ability?.name?.capitalized }.joined(separator: ",")
            handler(names)
        }
    }
}

Edit2:

查看代码库后,您需要更改以下几项内容:

1。 fetchPokemons实施

controller.service.fetchPokes的处理程序被每个宠物小精灵调用,因此我们需要检查所获取的是当前(self.pokemon),然后使用以下命令调用handler格式正确的技能。

func fetchPokemons(handler: @escaping (String) -> Void) {
    controller.service.fetchPokes { (poke) in
        guard poke.id == self.pokemon?.id else { return }
        self.pokemon? = poke
        let names = poke.abilities?.compactMap { $0.ability?.name?.capitalized }.joined(separator: ",")
        handler(names ?? "-")
    }
}

2。更新viewDidLoad()

现在只需将names值传递给标签即可。

override func viewDidLoad() {
    super.viewDidLoad()
    configureViewComponents()
    fetchPokemons { (names) in
        self.pokemon?.skillName = names
        self.infoView.configureLabel(label: self.infoView.skillLabel,details: names)
    }
}

3。重构var pokemon: Pokemon? didSet观察者

var pokemon: Pokemon? {
    didSet {
        guard let pokemon = pokemon,let data = pokemon.image else { return }
        navigationItem.title = pokemon.name?.capitalized
        infoLabel.text = pokemon.description!
        infoView.pokemon = pokemon
        imageView.image = UIImage(data: data)
    }
}