Child controllers and size of subview

MihaValencicMihaValencic SIMember, University

Hi!

I'm adding a child controller to a parent controller (which is RootController):

public override void LoadView ()
{
base.LoadView ();
var linearChild = new LinearChildController ();
AddChildViewController (linearChild);
linearChild.View.Frame = new RectangleF (10, 300, 300, linearChild.View.Frame.Height);
Add(linearChild.View)
}

What I want to do with this code is to "position" the child view inside a parent view hiearchy. The linearChild.View.Frame.Height returns 80, and this is what parent controller also sets for the frame. But the result is 100 (20 pixels more). And that is what is reported inside ViewDidLoad method of the child controller as well. If I change the Child Controller's view height to be 20px, the result will be 40 (again 20 more).

I don't know why this is happening. I can post the complete project if that helps.

Posts

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I don't know exactly what is going on, but in general you shouldn't do layout in LoadView or ViewDidLoad. You should do layout in ViewDidLayoutSubviews.

    I also think it's strange how you're using LoadView instead of ViewDidLoad. LoadView is normally overridden when you want to change how the top-level view for that view controller is created. In this case you aren't doing that. Why not move the code to create and add the child view controller into ViewDidLoad?

    This is what LoadView would typically look like:

    public override void LoadView ()
    {
        View = new UIView(); // Or something more complicated
    }
    

    Even then you aren't adding or configuring any subviews of View. The job of LoadView is to create and set the View property. ViewDidLoad is for doing the things you need to do once you have a View to work with (i.e., creating and configuring subviews).

  • MihaValencicMihaValencic SIMember, University

    @adamkemp,

    I made a copy/paste mistake, sorry about that. I am adding a child controller in ViewDidLoad. The LoadView() inside a child controller loads the views and adds buttons to it. I've simplified the code now so that the child controller actually does nothing to its (sub)views. The result is the same.

    From the documentation:

    If you cannot define your views in a storyboard or a nib file, override the loadView method to manually instantiate a view hierarchy and assign it to the view property.
    

    That is why I created child controller's views inside LoadView.

    I guess I'm doing something wrong with subviews then. The source code to reproduce this behaviour is:

    // ----- this is ParentController ----
    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
    
        View.BackgroundColor = UIColor.FromRGB (0xFA, 0xFA, 0x0C);
    
        Console.WriteLine ("Parent view bounds: {0}", View.Bounds);
        Console.WriteLine ("Parent view frame: {0}", View.Frame);
    
        var childRect = new RectangleF (10, 100, 300, 200);
    
        // add a child controller
        var child = new ChildController ();
        AddChildViewController (child);
        child.View.Frame = childRect;
        Add (child.View);
        child.DidMoveToParentViewController (this);
    }
    
    // ---- ChildController ----
    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        View.BackgroundColor = UIColor.FromRGB (0x00, 0xff, 0x00);
    }
    
    public override void ViewWillAppear (bool animated)
    {
        base.ViewWillAppear (animated);
        Console.WriteLine ("Child view bounds: {0}", View.Bounds);
        Console.WriteLine ("Child view frame: {0}", View.Frame);
    }
    

    And the log output (and also measured in a simulator) is:

    Child view bounds: {X=0,Y=0,Width=300,Height=220}
    Child view frame: {X=10,Y=100,Width=300,Height=220}
    

    So this is the part I don't understand. Why is the height 220, if the parent controller set the height to be 200? You can see the complete project here.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    The code you posted doesn't match the code on git.

    I think the problem might be this:

    // ChildViewController
    public override void LoadView ()
    {
        var view = new UIView (Rectangle.Empty);
        view.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
        View = view;
    }
    

    I think the auto-resizing is causing your view to change size. You can test this by commenting out that line.

    In order for auto-resizing to work properly you need for the parent view and the child view to have the correct relative sizes already when the child view is first added to the parent view. Then any time the parent view changes size it will change the child view's size accordingly.

    The problem in this case is that in ViewDidLoad (in your parent view controller) you don't have a size for View yet (it's going to be zero). It has just been created, and notice that LoadView doesn't set a Frame. That's not wrong. It's not LoadView's job to set the Frame. In fact, it's not up to this view controller to set its own size. That's the responsibility of whatever code is creating the view controller and adding it to the screen (usually either the app delegate or another parent view controller).

    Still, you have to have a meaningful size to start with in order for auto-resizing to work so you have two options:

    1. In the parent's LoadView assign a Frame with the expected size (the one that corresponds to the child view's size). This may be altered later, but that's ok because then auto-resizing will do the right thing. You're just setting it up for auto-resizing purposes only.
    2. Don't use auto-resizing. Implement ViewDidLayoutSubviews and just do that math.
  • MihaValencicMihaValencic SIMember, University

    Adam,

    sorry. I see that I have not pressed Post reply and the following text was left in the editor:

    I found the problem. One is supposed to add a view (UIView) as a container, then set child.View.Frame = container.Bounds and then add the child's view to this container view container.Add(child.View) and that fixes the additional 20px problem I was seeing.

    So, the code in Github actually works without problems - this is the final code I came up with while testing. I don't think auto-resizing ultimately changes anything, though.

    Thank you for your help in pointing me in the right direction. Later on, I played with XibFree layouts and calculating child layout size (height) upfront and then determining the size of the parent container based on that, so I achieved what I was looking for. Basically, same stuff as fragments in Android with wrap_content.

    Again, thank you.

  • DeepPrDeepPr INMember

    Hi
    I know its old post but i am too facing the same problem.But in my case i am using the storyboard to create scene.
    I have created a parent view controller and used a container in that.When i add child view controller to container its view resizes according to container size but subview moves 20 pixels up (it happens only in iPad .)Can you please help me in this.
    in my app child view controller is the common view controller added to 3 container.

  • I have the same problem as DeepPr.. Container view in the storyboard does not resize correctly in an iPad layout.

Sign In or Register to comment.