查看不显示另一个ChildViewController的childViewController

时间:2022-02-15 12:39:41

I'm having an issue with a multi-level hierarchy of childViewControllers in iOS/Swift. There are three layers to the current setup from lowest-to-highest:

我在iOS / Swift中遇到了childViewControllers的多级层次结构问题。从最低到最高的当前设置有三个层:

  1. InfoViewController
  2. InfoViewController
  3. SelectionViewController
  4. SelectionViewController
  5. MainViewController
  6. MainViewController

The InfoViewController has a view that is loaded from XIB.

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

SelectionViewController contains a UIStackView with an InfoViewController and a UIButton.

SelectionViewController包含一个带有InfoViewController和UIButton的UIStackView。

MainViewController is the top-level VC which is usually embedded in a UINavigationController.

MainViewController是*VC,通常嵌入在UINavigationController中。

The Problem

问题

When I add the InfoViewController and it's view directly to the MainViewController everything works great.

当我将InfoViewController及其视图直接添加到MainViewController时,一切都很有效。

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

However, if I add the SelectionViewController to the MainViewController using the same method, the embedded InfoViewController doesn't update it's UI - it always looks like the untouched XIB file that it is controlling. When it is NOT embedded in this SelectionViewController is behaves as expected.

但是,如果我使用相同的方法将SelectionViewController添加到MainViewController,嵌入的InfoViewController不会更新它的UI - 它总是看起来像它正在控制的未触摸的XIB文件。当它未嵌入此SelectionViewController时,其行为与预期一致。

As shown below, the UI is visible, but any changes made to it via asking it's ViewController do not show up - it looks exactly like the XIB file it was created from.

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

查看不显示另一个ChildViewController的childViewController

Below is the class setup starting with the BasicInfoView followed by the three viewControllers listed above.

下面是从BasicInfoView开始的类设置,后面是上面列出的三个viewControllers。

BasicInfoView

BasicInfoView

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

}

BasicInfoViewController

BasicInfoViewController

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


}

SelectionViewController

SelectionViewController

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

}

Some non-relevant code has been removed

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

Notes

笔记

Please note that in practice, both BasicInfoViewController and SelectionViewController are subclassed. For example, I have a ContactInfoViewController which can be passed a Contact object and display full name, company name and photo (as explained above, this works fine). There is also a subclass of SelectionViewController to complement this: ContactSelectionViewController. ContactSelectionViewController also has a Contact object property which can be assigned and is then passed to the embedded ContactInfoViewController - this is the point at which the data is not displayed. I have included these subclasses below for additional reference.

请注意,在实践中,BasicInfoViewController和SelectionViewController都是子类。例如,我有一个ContactInfoViewController,它可以传递一个Contact对象并显示全名,公司名称和照片(如上所述,这样可以正常工作)。还有一个SelectionViewController的子类来补充它:ContactSelectionViewController。 ContactSelectionViewController还有一个Contact对象属性,可以将其赋值,然后传递给嵌入式ContactInfoViewController - 这是不显示数据的点。我在下面列出了这些子类以供进一步参考。

ContactInfoViewController

ContactInfoViewController

Again, this works perfectly when placed directly into the MainViewController.

同样,当直接放入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

ContactSelectionViewController

This VC functions properly, but the embedded ContactInfoViewController does not display data. For some reason, the view from the ContactInfoViewController is not being updated with data when it is embedded inside this controller.

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

}

1 个解决方案

#1


1  

Try

尝试

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

or

要么

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

It might be because you are initiating PLBasicInfoViewController every time when you try to access.

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

#1


1  

Try

尝试

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

or

要么

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

It might be because you are initiating PLBasicInfoViewController every time when you try to access.

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