Ios 另一个childViewController的childViewController的视图未显示

Ios 另一个childViewController的childViewController的视图未显示,ios,swift,viewcontroller,childviewcontroller,parentviewcontroller,Ios,Swift,Viewcontroller,Childviewcontroller,Parentviewcontroller,我对iOS/Swift中的ChildViewController的多级层次结构有问题。当前设置从最低到最高分为三层: InfoViewController SelectionViewController MainViewController InfoViewController有一个从XIB加载的视图 SelectionViewController包含一个带有InfoViewController和ui按钮的UIStackView MainViewController是顶级VC,通常嵌入到UINa

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

  • InfoViewController
  • SelectionViewController
  • MainViewController
  • InfoViewController
    有一个从XIB加载的视图

    SelectionViewController
    包含一个带有
    InfoViewController
    ui按钮的
    UIStackView

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

    问题

    当我添加
    InfoViewController
    并将其视图直接添加到
    MainViewController
    时,一切都很好

    func setupInfoViewControllerDirectlyOnMainVC () {
        addChildViewController(infoViewController)
        infoViewController.view.embedInside(otherView: infoContainerView)
        infoViewController.didMove(toParentViewController: self)
    }
    
    但是,如果我使用相同的方法将
    SelectionViewController
    添加到
    MainViewController
    ,则嵌入的
    InfoViewController
    不会更新其UI-它看起来总是像它控制的未触及的XIB文件。当它未嵌入此
    SelectionViewController
    中时,其行为与预期一致

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

    下面是从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)
        }
    
    }
    
    基本视图控制器

    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()
        }
    
    
    }
    
    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)
        }
    
    }
    
    选择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()
        }
    
    
    }
    
    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)
        }
    
    }
    
    一些不相关的代码已被删除

    注释

    请注意,实际上,BasicInfo ViewController和SelectionViewController都是子类。例如,我有一个ContactInfoViewController,它可以传递一个Contact对象并显示全名、公司名和照片(如上所述,这很好)。还有一个子类SelectionViewController来补充此功能:ContactSelectionViewController。ContactSelectionViewController还有一个Contact对象属性,可以分配该属性,然后将其传递给嵌入式ContactInfoViewController-这是数据不显示的点。我将这些子类包含在下面,以供其他参考

    ContactInfoViewController

    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()
        }
    
    
    }
    
    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)
        }
    
    }
    
    同样,当直接放置到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
                    }
                })
            }
        }
    
    }
    
    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
        }
    
    }
    
    联系人选择查看控制器

    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()
        }
    
    
    }
    
    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)
        }
    
    }
    
    此VC功能正常,但嵌入式ContactInfoViewController不显示数据。由于某些原因,当ContactInfoViewController中的视图嵌入此控制器时,该视图不会用数据更新

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


    这可能是因为您每次尝试访问时都会启动PLBasicInfoViewController。

    我不能从这个问题中看出什么,但我发现Xcode的视图调试功能对于此类问题非常有用,尤其是约束调查。(如果你还没试过。)@PhillipMills这似乎更像是一个参考问题,而不是布局问题。一切显示正确;但是,它的行为就像InfoView与附加到InfoViewController的InfoView不同。抱歉。我误解了“它看起来总是像未触及的XIB”。你真的能在viewcontroller中有一个viewcontroller吗?尝试让父级中的视图重画方法调用子级的视图重画方法?如果将
    var
    声明替换为
    let infoViewController=PLBasicInfoViewController()