视图不显示另一个 ChildViewController 的 childViewController

View not displaying for childViewController of another ChildViewController

我在 iOS/Swift 中的子 ViewController 的多级层次结构中遇到问题。当前设置从最低到最高分为三层:

  1. InfoViewController
  2. SelectionViewController
  3. MainViewController

InfoViewController 有一个从 XIB 加载的视图。

SelectionViewController 包含一个 UIStackView,一个 InfoViewController 和一个 UIButton

MainViewController 是顶级 VC,通常嵌入 UINavigationController.

问题

当我添加 InfoViewController 并直接查看 MainViewController 时,一切正常。

func setupInfoViewControllerDirectlyOnMainVC () {
    addChildViewController(infoViewController)
    infoViewController.view.embedInside(otherView: infoContainerView)
    infoViewController.didMove(toParentViewController: self)
}

但是,如果我使用相同的方法将 SelectionViewController 添加到 MainViewController,嵌入的 InfoViewController 不会更新它的 UI - 它看起来总是它控制的未修改的 XIB 文件。当 NOT 嵌入此 SelectionViewController 时,其行为符合预期。

如下所示,UI 是可见的,但是通过询问 ViewController 对其所做的任何更改都不会显示 - 它看起来与创建它的 XIB 文件完全一样。

下面是 class 设置,从 BasicInfoView 开始,然后是上面列出的三个 viewController。

基本信息视图

class PLBasicInfoView: PLDesignableViewFromXib {

    //
    // MARK: Outlets
    //
    //

    @IBOutlet weak var photoView: PFRoundImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subtitleLabel: UILabel!


    var imageFile:PFFile? {
        didSet {
            photoView.file = imageFile
            photoView.loadInBackground()
        }
    }

    //
    // MARK: Initialization
    //
    //

    override var nameOfXib: String {
        return "PLBasicInfoView"
    }

    override var intrinsicContentSize: CGSize {
        return CGSize(width: super.intrinsicContentSize.width, height: 56)
    }

}

基本信息ViewController

class PLBasicInfoViewController: UIViewController {

    /**
     The BasicInfoView which will be managed by this controller.
     */
    var basicInfoView = PLBasicInfoView()

    /**
     This is the master stack view which contains all subviews.
     */
    var stackView = UIStackView()


    /**
     PFFile representing the image to be displayed in the imageView. Setting a valid imageFile object automatically laods the image from the server. If set to nil, the defaultImage is displayed instead.
     */
    var imageFile: PFFile? {
        didSet {
            if imageFile != nil {
                basicInfoView.imageFile = imageFile
            } else {
                basicInfoView.photoView.image = defaultImage
            }
        }
    }

    /**
     Default UIImage to be displayed in the imageView if there is no imageFile assigned.
     */
    var defaultImage: UIImage! {
        return #imageLiteral(resourceName: "ios7-camera-outline")
    }


    /**
     Main text of the infoView
     */
    var titleText:String? {
        didSet {
            basicInfoView.titleLabel.isHidden = (titleText == nil)
            basicInfoView.titleLabel.text = titleText
        }
    }

    /**
     Secondary text of the infoView. Displays under titleText.
     */
    var subtitleText:String? {
        didSet {
            basicInfoView.subtitleLabel.isHidden = (subtitleText == nil)
            basicInfoView.subtitleLabel.text = subtitleText
        }
    }

    /**
     Embed our stackView into main view. The custom embedInsider(otherView:UIView) method (UIView extension) will take care of the subview additional as well as all layout constraints.
     */
    func setupStackView () {
        stackView.embedInside(otherView: view)
        stackView.axis = .vertical
        stackView.addArrangedSubview(basicInfoView)
    }

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


}

选择ViewController

class PLSelectableInfoViewController: UIViewController {


    /**
     If true, the info view will be shown and the selection button will be hidden.
     */
    var isAssigned = false {
        didSet {
            selectionButton.isHidden = isAssigned
            infoView.isHidden = !isAssigned
        }
    }


    /**
     The View controller dispaying the object in question.
     */
    var infoViewController: PLBasicInfoViewController! {
        return PLBasicInfoViewController()
    }

    private
    var infoView: UIView!

    /**
     Button on bottom of stack. Intended to allow user to assign a new value to the contact property.
     */
    var selectionButton = PLButton()

    /**
     Stack view containing all subviews.
     */
    var stackView = UIStackView()


    //
    // MARK: UIViewController Overrides
    //
    //

    override
    func viewDidLoad() {
        super.viewDidLoad()
        setupStackView()
        addInfoView()
    }


    private
    func setupStackView () {
        stackView.embedInside(otherView: view)
        stackView.axis = .vertical
    }

    private
    func addInfoView () {
        addChildViewController(infoViewController)
        infoView = infoViewController.view
        stackView.addArrangedSubview(infoView)
        infoViewController.didMove(toParentViewController: self)
    }

}

一些不相关的代码已被删除

备注

请注意,实际上,BasicInfoViewController 和 SelectionViewController 都是子class。例如,我有一个 ContactInfoViewController,它可以传递一个 Contact 对象并显示全名、公司名称和照片(如上所述,这很好用)。还有一个 SelectionViewController 的子class 来补充这个:ContactSelectionViewController。 ContactSelectionViewController 也有一个 Contact 对象 属性 可以分配,然后传递给嵌入的 ContactInfoViewController - 这是数据不显示的点。我在下面包含了这些子 classes 以供额外参考。

联系信息ViewController

同样,将其直接放入 MainViewController 时效果很好。

class PLContactInfoViewController: PLBasicInfoViewController {

    /**
     Contact object managed by this controller.
     */
    var contact: PLContact? {
        didSet {
            if contact == nil {
                titleText = "Not Set"
                subtitleText = nil
                return
            }
            contact?.fetchIfNeededInBackground(block: { (object, error) in
                if let _ = object as? PLContact {
                    self.updateWithContact()
                }
            })
        }
    }

    override
    var defaultImage: UIImage! {
        return #imageLiteral(resourceName: "ios7-contact-outline")
    }


    private
    func updateWithContact () {
        if let c = contact {
            titleText = c.fullName
            imageFile = c.photo
            c.company?.fetchIfNeededInBackground(block: { (object, error) in
                if let comp = object as? PLCompany {
                    self.subtitleText = comp.name
                } else {
                    self.subtitleText = nil
                }
            })
        }
    }

}

ContactSelectionViewController

此VC功能正常,但嵌入的ContactInfoViewController不显示数据。出于某种原因,来自 ContactInfoViewController 的视图在嵌入此控制器时未使用数据更新。

class PLContactAssignmentViewController: PLSelectableInfoViewController {


    /**
     The contact currently selected by this contorller
     */
    var selectedContact: PLContact? {
        didSet {
            isAssigned = !(selectedContact == nil)
            contactInfoViewController.contact = selectedContact
        }
    }


    override
    var infoViewController: PLBasicInfoViewController! {
        return PLContactInfoViewController()
    }


    private
    var contactInfoViewController: PLContactInfoViewController {
        return infoViewController as! PLContactInfoViewController
    }

}

尝试

    var _infoViewController: PLBasicInfoViewController?
    var infoViewController: PLBasicInfoViewController! {
        if let vc = _infoViewController {
            return vc
        }
        _infoViewController = PLBasicInfoViewController()
        return _infoViewController!
    }

lazy var infoViewController: PLBasicInfoViewController = {
        return PLBasicInfoViewController()
    }()

可能是每次访问时都在启动PLBasicInfoViewController。