Basic positioning question Label/Image Top/Bottom

MartHughMartHugh USMember ✭✭✭

I have a ViewController class which is to be split 50:50 top and bottom. The top will hold a label/text, the bottom will hold an image.

The 50:50 top bottom divide needs to be maintained despite the screen size/shape of the device the App is running on. It also needs to be able to cope with device rotation.

What are my best options to set this divide for the given device, and, what is best practise to show the image at the biggest size it can for the device (i.e. fill the space it has) without compromising its aspect ratio.

Should I be using some kind of Layout manager rather than just dropping a Label and ImageView onto the ViewController.

Many thanks.

Tagged:

Posts

  • MartHughMartHugh USMember ✭✭✭
    edited May 2015

    OK, so I have tried to approach this by using the width and height properties of the View in the ViewController constructor and setting the Element widths to be the same as the device/view width; and proportionally scaling and positioning the label and image vertically to 50:50 based on the height of the view, in the ViewDidLoad event.

    This a little clunky, does the Framework provide a more intuitive way of doing this.

    Thanks

  • stvansolanostvansolano UMInsider, University ✭✭✭

    Hello Mart,

    Have you tried a GridLayout instead? It suits better for this kind of scenarios:

    http://developer.xamarin.com/guides/android/user_interface/gridlayout/

  • MartHughMartHugh USMember ✭✭✭

    @stvansolano thank you for your reply.

    There is no grid layout that I can see under IOS but there is a CollectionView which may be similar. Even so I cannot see how to common UI settings, such as telling an Element to (say) fill the whole view, or fill the width of a view, just by setting a property. If I used the Collection View I would want it to fill the entire view. I have seen the 'Scale To Fill' view setting but this does not seem to have any effect. Is there a way of doing from the Storyboard properties ?

    Thanks again.

  • nick5454nick5454 USUniversity ✭✭✭

    I would use constraints as they are recommended and the easiest way to have a screen look great in all sections. And when you start animating screens with constraints to can make beautiful apps. Here is a code example of doing what you are asking for. I have a bottom view and a top view taking of 50% of it's parent and each control is centered based on it's parent center position. You should read up on constraints, but this a good starting point. the wwdc videos on constraints are a must watch.

    I have a label and an asteroid image from the Game machine "SINISTAR!" for any old school gamers

    `using System;

    using UIKit;

    namespace TestSplit
    {
    public partial class ViewController : UIViewController
    {
    public ViewController (IntPtr handle) : base (handle)
    {
    }

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();
            // Perform any additional setup after loading the view, typically from a nib.
    
            BuildScreen ();
    
        }
    
        public override void DidReceiveMemoryWarning ()
        {
            base.DidReceiveMemoryWarning ();
            // Release any cached data, images, etc that aren't in use.
        }
    
        private void BuildScreen() {
            UIView topView = new UIView();
            UIView bottomView = new UIView ();
    
            UIImageView imageView = new UIImageView() { Image = UIImage.FromFile("asteroid.png") };
            UILabel myLabel = new UILabel () { Text = "See my asteroid from Sinistar!" };
    
            View.Add (topView);
            topView.TranslatesAutoresizingMaskIntoConstraints = false;
    
            myLabel.TranslatesAutoresizingMaskIntoConstraints = false;
            topView.Add (myLabel);
    
            View.Add (bottomView);
            bottomView.TranslatesAutoresizingMaskIntoConstraints = false;
            imageView.TranslatesAutoresizingMaskIntoConstraints = false;
            bottomView.Add (imageView);
    
            // place the views
            // top
            View.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(
                    topView,
                    NSLayoutAttribute.CenterX,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.CenterX,
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    topView,
                    NSLayoutAttribute.Width,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.Width,
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    topView,
                    NSLayoutAttribute.Top,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.Top, // hug to the top of the view
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    topView,
                    NSLayoutAttribute.Height,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.Height,
                    .5f, // half of the height of the view
                    0f)
            });
    
            // bottom
            View.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(
                    bottomView,
                    NSLayoutAttribute.CenterX,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.CenterX,
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    bottomView,
                    NSLayoutAttribute.Width,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.Width,
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    bottomView,
                    NSLayoutAttribute.Bottom,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.Bottom, // hug to the bottom of the view
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    bottomView,
                    NSLayoutAttribute.Height,
                    NSLayoutRelation.Equal,
                    View,
                    NSLayoutAttribute.Height,
                    .5f, // half of the height of the view
                    0f)
            });
    
            // label
            topView.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(
                    myLabel,
                    NSLayoutAttribute.CenterX,
                    NSLayoutRelation.Equal,
                    topView,
                    NSLayoutAttribute.CenterX,
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    myLabel,
                    NSLayoutAttribute.CenterY,
                    NSLayoutRelation.Equal,
                    topView,
                    NSLayoutAttribute.CenterY,
                    1f,
                    0f)
    
            });
    
            // image
            bottomView.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(
                    imageView,
                    NSLayoutAttribute.CenterX,
                    NSLayoutRelation.Equal,
                    bottomView,
                    NSLayoutAttribute.CenterX,
                    1f,
                    0f),
                NSLayoutConstraint.Create(
                    imageView,
                    NSLayoutAttribute.CenterY,
                    NSLayoutRelation.Equal,
                    bottomView,
                    NSLayoutAttribute.CenterY,
                    1f,
                    0f)
            });
    
        }
    }
    

    }

    `

  • MartHughMartHugh USMember ✭✭✭

    @Nick5454 thank you for your code. I pasted the BuildScreen routine into my ViewController, changed the name of the image to one I have in my project and it clean compiled first go. However when I ran it, the image renders over the entire ViewController's View. The label cannot be seen.

    I have checked the image and it is 3200x2000 pixels - which I accept is not something I should be using, but if this were constrained, shouldn't it have remained in the lower half anyway ?

    Any ideas?

  • nick5454nick5454 USUniversity ✭✭✭

    Sample project

    Change the ContentMode on your image to AspectFit

  • nick5454nick5454 USUniversity ✭✭✭

    @MartHugh See my reply above

  • nick5454nick5454 USUniversity ✭✭✭
    edited May 2015

    @MartHugh

    You need to scale it down. ImageVIews have several properties to maintain the image

    Here is a link to AutoLayout by example. Watch this, it's a good walkthrough of constrains, it's properties, and examples with animations.

    [https://developer.apple.com/videos/wwdc/2012/?include=232#232](AutoLayout by example)

  • MartHughMartHugh USMember ✭✭✭

    Thanks @Nick5454 !
    I have tried your sample and it works fine, but again, if I use a large image the whole view is rendered, despite setting the ContentMode

    UIImageView imageView = new UIImageView() { Image = UIImage.FromFile("Aerial05.jpg"),ContentMode=UIViewContentMode.ScaleAspectFit };

  • MartHughMartHugh USMember ✭✭✭

    I think those last two posts crossed - will have a look at that link. Thanks again.

  • nick5454nick5454 USUniversity ✭✭✭

    @MartHugh

    Change the image to this. The reason is because Constraints are using the intrinsic size, which I assume by default is the size of the image.

    bottomView.AddConstraints (new NSLayoutConstraint[] { NSLayoutConstraint.Create( imageView, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, bottomView, NSLayoutAttribute.CenterX, 1f, 0f), NSLayoutConstraint.Create( imageView, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, bottomView, NSLayoutAttribute.CenterY, 1f, 0f), NSLayoutConstraint.Create( imageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, bottomView, NSLayoutAttribute.Width, .5f, // half the width of the parent view 0f), NSLayoutConstraint.Create( imageView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, bottomView, NSLayoutAttribute.Height, .5f, // half the height of the parent view 0f) });

    let me know if that works

  • MartHughMartHugh USMember ✭✭✭

    Yes that has got it. So, you setting the width relative to the width of the parent, you are maintaining the aspect ratio ?.

  • nick5454nick5454 USUniversity ✭✭✭
    edited May 2015

    @MartHugh
    No, but setting the UIImageView mode to AspectFit it will set the UIimageView to aspect ratio of the image.

    See here for the description of each one https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/swift/enum/UIViewContentMode

  • MartHughMartHugh USMember ✭✭✭

    Yes that makes sense. Thanks @Nick5454

  • MartHughMartHugh USMember ✭✭✭

    I found the Apple video useful. Thanks. However on returning to hand coding constraints I find I have a problem.

    What I really need is a toolbar above the two views to allow me to scroll through text displayed in the label (a second Nav Bar).

    My problem is that the topView Top needs to be set to the View Top, offset by the intrinsic height of the sub Nav bar. But this has not been created at this time and so has a frame height of zero. Same question if I am putting a control like this in its own sub view and I need to size the view around it, and I don't want to do this proportionally (like I did above between topView/bottomView) but its size is unknown.

    So what I am after, within the view, is ....

    [Sub Toolbar set to Intrinsic height]
    [topView 50% of remaining View]
    [bottomView, 50% of remaining View]

    Thanks for any comments.

  • nick5454nick5454 USUniversity ✭✭✭
    edited May 2015

    @MartHugh Is the "Sub tool bar" the top tool bar or do you have a toolbar that expands into a drop down menu like thing?

    There is the toplayoutguide/bottomlayoutguide that figures the area in case there is a toolbar at top and a nav bar at the bottom. In that case the top view would have it's top equal to the toplayoutguide instead of the View

  • MartHughMartHugh USMember ✭✭✭

    The sub toolbar is just a UINavigationBar I have dragged a onto the view area in the storyboard, and have positioned its origin at the origin of the view. So this effectively steals from the 50:50 split I had originally.

  • MartHughMartHugh USMember ✭✭✭
    edited May 2015

    So modifying the position of the topView constraint, to take account of the new toolbar above it looks as follows

                NSLayoutConstraint.Create(
                    topView,NSLayoutAttribute.Top,
                    NSLayoutRelation.Equal,
                    View,NSLayoutAttribute.Top, // hug to the top of the view, but below the toolbar
                    1f, NavBarMess.IntrinsicContentSize.Height),
    

    But this still places the top of topView at the very top of View, even though the Offset (NavBarMess.IntrinsicContentSize.Height) is 44.

    The NavBarMess is rendered underneath topView in its expected position - but is obscurred by topView which should be beneath it.

    Any ideas guys. All I am trying to do is start a view immediately below a toolbar?
    Thanks.

  • MartHughMartHugh USMember ✭✭✭
                NSLayoutConstraint.Create(
                    topView,NSLayoutAttribute.Top,
                    NSLayoutRelation.Equal,
                    NavBarMess,NSLayoutAttribute.Bottom, // hug to the top of the view, but below the toolbar
                    1f, 0f),
    

    This seems to work OK.

    This is what I actually started with but I believe I had a compiler error complaining about the use of different NSLayoutAttributes.

  • nick5454nick5454 USUniversity ✭✭✭

    @MartHugh There are 2 possibilities.

    Is this a navigation sequence, if so I would use a UINavigationController instead. This way you don't need to change the view controller as it is a child view. You would call navigationBar.PushViewController(new ViewController());

    if you just want a navigation control ( UINavigationBar ), then you would add the bar, bind to the top layout guide and the top view to the navigation bar. Then the top view is slightly shorter. To fix this, add a parent view, and add topView and bottomView to its parent view.

    What this does is the parentView acts as the container and as so modify the parent view size, the constraints will readjust the topView and bottomView to always be 50% without you changing this, as AutoLayout is adpative.

    I have attached the sample project demonstrating this.

  • MartHughMartHugh USMember ✭✭✭
    edited May 2015

    Thanks so much for this. The parent view container makes a lot of sense and makes all the previous code reusable.

    However the problem I have is that I am trying to do this within a NavController, not a ViewController.

    I have a top level sequence of views which works OK from the NavController NavBar,. but on the very first page I need another NavBar/ToolBar inside the View (and above the parent view) which changes the display of the label (basically it scrolls through a number of messages) and I was planning to do this with a < and > buttons on a toolbar.

    The code you kindly provided me creates this bar, but in the top region of the View Controller where the top level nav bar would be.

    When I slot your BuildScreen routine into my NavController project, the effect I get is that my top-level NavBar is there as expected but then then the topView starts immediately beneath it. There is no second NavBar

    I have changed the navbar Top constraint to use View instead of TopLayout, and I note that the parentView Top is already set to be Equal to the navbar bottom. However this does not change the outcome. I still do not see the second (inner) bar.

    I notice if I don't show anything in the view except the navbar, is still does not appear - even adding a constraint to make its height equal to the view height has no effect.

    I'm sure I am missing something obvious, but can't see the wood for the trees at the moment.

    Thanks for your patience.

  • MartHughMartHugh USMember ✭✭✭

    OK. I had confused the remaining space after the NavController bar had been added with the View area. Whereas that includes the area where the main Nav Bar was, and so the second navbar was sitting under the first.

    I changed the first constraint to
    NSLayoutConstraint.Create(
    navBar,
    NSLayoutAttribute.Top,
    NSLayoutRelation.Equal,
    TopLayoutGuide,
    NSLayoutAttribute.Bottom,
    1f,
    0f),

    so that the navbar followed on from the bottom of the Main Navbar and it all hangs together nicely.

    Many thanks @nick5454.

Sign In or Register to comment.