首页 > 解决方案 > 我可以打印数据但不能将其分配给 Swift 中的标签

问题描述

我将我的 API 调用中的数据发送到我的 InfoController viewDidLoad。在那里,我能够将它安全地存储在一个技能名称常量中,并打印它,通过控制台接收所有信息。

当我尝试将此变量分配给我的技能标签时,问题就来了。

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 时,控制台会显示我需要的所有数据。这是数据的样子:数据示例

我想使用这些数据的计算属性是:

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 类级别拥有的字符串变量。

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

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

EDIT1:我从我的服务类中获取口袋妖怪数据的函数是这个:

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

标签: swiftstringviewdidloadcomputed-properties

解决方案


跳转到 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 设置都非常重要。

编辑:

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

编辑2:

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

1.fetchPokemons实施

每个controller.service.fetchPokespokemon都会调用处理程序,因此我们需要检查获取的是否是当前的(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, title: "Skills", 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)
    }
}

推荐阅读