我可以打印数据,但无法将其分配给 Swift 中的标签

I can print data but can't assign it to a label in Swift

我将 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 class 级别的字符串变量。

这是我的应用在 运行 时的样子: PokeApp

我的目标是获取详细信息参数以显示 skillName 数据,但它 returns 没有,我不知道为什么。有什么建议吗?

编辑 1: 我从我的服务 class 获取口袋妖怪数据的功能是这个:

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 看起来像:

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?
}

Jump to the Edit2 paragraph for the final answer!

初始答案:

我看起来像你 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 设置都非常重要。

编辑:

可能是 fetch 函数导致了问题!您多次调用处理程序:

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 { [=13=].ability?.name?.capitalized }.joined(separator: ", ")
            handler(names)
        }
    }
}

编辑2:

查看您的代码库后,您需要更改几处:

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 { [=14=].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, title: "Skills", details: names)
    }
}

3。重构 var pokemon: Pokemon? didSet observer

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)
    }
}