【问题标题】:didSet property value only appears in print()didSet 属性值只出现在 print()
【发布时间】:2018-05-04 07:29:16
【问题描述】:

初始目标:

有一个视图,其中包含垂直放置的单元格列表,以显示一些信息。一旦用户单击单元格以显示包含更多信息的新视图。

THE ROAD SO FAR(诅咒我任性的儿子!):

  • 我创建了 2 个视图控制器:ViewController(继承 UICollectionViewController、UICollectionViewDelegateFlowLayout)和 DetailViewController(继承 UIViewController)。
  • 我创建了 ViewController 用来生成集合视图的 Cell 和 DetailViewController 使用的 DetailView
  • 我创建了一个名为 Detail 的结构作为自定义数据类型,它使用属性(例如姓名、姓氏、地址等)提供数据存储

结构:

struct Detail: Decodable {
    let name: String?
    let surname: String?
    let address: String?
    let description: String?
}

我使用以下数据进行测试(测试完成后,我将从 API 调用中获取这些数据)。我把它放在 ViewController 里面:

let details: [Detail] = [Detail(name: "Chris", surname: "Doe", address: "Neverland 31", description: "This is a description about Chris Doe"), Detail(name: "Tony", surname: "Cross", address: "Galaxy Road 1", description: "This is a description about Tony Cross")]

使用上述信息和方法创建单元格:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

还有:

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! Cell

由于该方法要求我们返回一个 UICollectionViewCell,我首先通过以下操作将相关信息发送给 Cell:

cell.details = details[indexPath.item]
return cell

在 Cell 中,我使用 didSet 创建了以下属性来帮助我检索信息:

var details: Detail? {
    didSet {
        guard let details = details else { return }
        guard let name = details.name else { return }
        ....
        ....
     }    

正如您所理解的,使用来自 ViewController 的信息我动态构建了每个单元格。

此时一切都很好。

然后我尝试在单击单元格时显示详细视图。为此,我在方法中遵循了相同的做法:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    let detailView = DetailView()
    detailView.details = details[indexPath.item]

    let detailViewController = DetailViewController()
    detailViewController.modalTransitionStyle = .coverVertical
    self.present(detailViewController, animated: true, completion: nil)
}

同样,在 DetailView 中,我使用相同的方法来获取与所选项目关联的数据。这样我就可以访问用户选择的单元格的数据,如下所示:

 import UIKit

 class DetailView: UIView {

     var dismissDetailViewAction: (() -> Void)?

     var details: Detail? {
        didSet {

            // get details
            guard let details = details else { return }
            guard let name = details.name else { return }
            guard let surname = details.surname else { return }
            guard let address = details.address else { return }
            guard let description = details.description else { return }

            // print description and it shows in the console but not in the view
            print(description)

            let attributedTextDescription = NSMutableAttributedString(string: description, attributes: [NSAttributedStringKey.font: UIFont.FontBook.AvertaRegular.of(size: 20), NSAttributedStringKey.foregroundColor: UIColor.white])
            briefDescription.attributedText = attributedTextDescription
            briefDescription.textAlignment = .center
            briefDescription.textColor = .white
            briefDescription.numberOfLines = 0
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        setupView()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not yet been implemented")
    }

    fileprivate func setupView() {
        setupDescriptionText()
        setupCloseButton()
    }

    let briefDescription: UITextView = {
        let text = UITextView()          
        text.textColor = .red
        return text
    }()

    let closeButton: UIButton = {
        let button = UIButton(title: "Close", font: UIFont.FontBook.AvertaRegular.of(size: 18), textColor: .white, cornerRadius: 5)
        button.backgroundColor = .black
        button.addTarget(self, action: #selector(closeDetailView), for: .touchUpInside)
        return button
    }()


    fileprivate func setupDescriptionText() {
        self.addSubview(briefDescription)
        briefDescription.translatesAutoresizingMaskIntoConstraints = false
        briefDescription.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5).isActive = true
        briefDescription.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -5).isActive = true
        briefDescription.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
        briefDescription.heightAnchor.constraint(equalToConstant: 300).isActive = true
    }

    fileprivate func setupCloseButton() {
        self.addSubview(closeButton)
        closeButton.translatesAutoresizingMaskIntoConstraints = false

        closeButton.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        closeButton.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
        closeButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 40).isActive = true
        closeButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -40).isActive = true
        closeButton.heightAnchor.constraint(equalToConstant: 60).isActive = true
    }

    @objc func closeDetailView() {
        dismissDetailViewAction?()
    }

}

所以,我实际上做的是在didSet外面设计视图的静态部分,而在didSet里面设计动态部分。这适用于 collectionView 的单元格。

我使用 DetailViewController 来显示 DetailView 并在用户单击“关闭”按钮时自行关闭。

import UIKit

class DetailViewController: UIViewController {

    // reference DetailView view
    var detailView: DetailView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // setup view elements
        setupView()
    }

    fileprivate func setupView() {
        let mainView = DetailView(frame: self.view.frame)
        self.detailView = mainView
        self.view.addSubview(detailView)

        self.homeDetailView.dismissDetailViewAction = dismissDetailView

        // pin view
        self.detailView.translatesAutoresizingMaskIntoConstraints = false
        self.detailView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        self.detailView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        self.detailView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        self.detailView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
    }

    fileprivate func dismissDetailView() {
        // dismiss current (DetailViewController) controller
        self.dismiss(animated: true, completion: nil)
    }
}

我这样做的原因是我喜欢让我的 ViewControllers 尽可能干净(Massive View Controller,不是我的事)。

问题

整个构建没有任何问题,但是当我单击一个单元格进入DetailView时,没有显示任何信息。

奇怪的部分

在 DetailView --> didSet 中,当我使用 print(name) 时,它工作得很好(你会在控制台中看到正确的名称)。但是当我尝试在视图中使用该值时,它不会显示出来。

而且我知道我的 DetailView 很好,因为如果我在其中使用硬编码值,它就可以工作(你会看到正确的结果)。

任何建议为什么这不能正常工作?

PS:我正在以编程方式构建整个东西。不涉及故事板。

在此先感谢,并为丢失的帖子感到抱歉。

【问题讨论】:

  • 你在didSet中创建了一堆局部变量,didSet退出后它们将不存在。您想在这里完成什么,已设置的详细信息实例已经包含名称、姓氏、...值?如何在视图控制器中访问它?
  • 您能否添加更多代码以尝试将这些详细信息绑定到您的视图以及如何将 detailView 传递给 detailViewController?现在问题太不清楚了。
  • 请检查已编辑的问题。希望它能提供更多信息。

标签: ios swift cells collectionview


【解决方案1】:

如前所述,detailViewController 内部未引用您的 detailView。相反,您在 DetailViewController 内创建了另一个 DetailView 实例,但这个实例中没有 Detail。

控制台消息是从您的detailView 内部调用的,但detailViewController 内部是另一个没有调用此消息的实例,因为它的Detail 默认设置为nil。

简而言之,要解决这个问题,您只需进行以下更改:

import UIKit

class DetailViewController: UIViewController {

var detail: Detail!

private lazy var detailView: DetailView = {
    let mainView = DetailView(frame: self.view.frame)
    mainView.details = detail
    return mainView
}

override func viewDidLoad() {
    super.viewDidLoad()

    setupView()
}

fileprivate func setupView() {
    self.view.addSubview(detailView)

    self.homeDetailView.dismissDetailViewAction = dismissDetailView

    // pin view
    self.detailView.translatesAutoresizingMaskIntoConstraints = false
    self.detailView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
    self.detailView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
    self.detailView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
    self.detailView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
}

fileprivate func dismissDetailView() {
    // dismiss current (DetailViewController) controller
    self.dismiss(animated: true, completion: nil)
}

}

在你的collectionView(...) func:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let detailViewController = DetailViewController()
    detailViewController.detail = details[indexPath.item]
    detailViewController.modalTransitionStyle = .coverVertical
    self.present(detailViewController, animated: true, completion: nil)
}

【讨论】:

  • 这样就可以了!但是有一个错误:关闭后缺少括号。谢谢斯蒂芬!
【解决方案2】:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    let detailView = DetailView()
    detailView.details = details[indexPath.item]

    let detailViewController = DetailViewController()
    detailViewController.modalTransitionStyle = .coverVertical
    self.present(detailViewController, animated: true, completion: nil)
}

你在这里创建一个DetailView,将它传递给你的details...然后什么都不做。

通常DetailView 将是DetailViewController 的一个属性,您会将details 传递给视图控制器,它会显示它。

这里发生的情况是,您正在创建、配置和丢弃DetailView,而您可能应该使用DetailViewController 拥有或应该拥有的那个。

【讨论】:

    猜你喜欢
    • 2019-05-22
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 2023-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多