some_id
some_id

Reputation: 29896

AutoLayout Visual language for mixed V+H

How can view layouts such as this, where the subviews are in vertical and horizontal relationships, be described using the Visual language?

enter image description here

Upvotes: 0

Views: 158

Answers (1)

foundry
foundry

Reputation: 31745

Here is one way to do it. Assumptions: views 1 and 2 have a fixed pixel width, view 3 fills remaining width, fixed margin all round and between views. Views 1 and 2 equal height.

If those are wrong assumptions its pretty straightforward to extend this example.

Swift:

    let views = ["view1": view1, "view2": view2, "view3": view3]
    let metrics = ["m":12,"w":100]

    let format0 = "H:|-(m)-[view1(w)]-(m)-[view3]-(m)-|"
    let format1 = "H:|-(m)-[view2(w)]-(m)-[view3]-(m)-|"

    let format2 = "V:|-(m)-[view1]-(m)-[view2(==view1)]-(m)-|"
    let format3 = "V:|-(m)-[view3]-(m)-|"

    for  string in [format0,format1,format2,format3] as [String] {
        self.view.addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat(
                string,
                options:nil,
                metrics:metrics,
                views:views))
    }

Objective-C:

    NSDictionary* views = NSDictionaryOfVariableBindings(view1,view2,view3);
    NSDictionary* metrics = @{@"m":@(12),@"w":@(100)};

    NSString* format0 = @"H:|-m-[view1(w)]-m-[view3]-m-|";
    NSString* format1 = @"H:|-m-[view2(w)]-m-[view3]-m-|";

    NSString* format2 = @"V:|-m-[view1]-m-[view2(==view1)]-m-|";
    NSString* format3 = @"V:|-m-[view3]-m-|";

    for (NSString* string in @[format0,format1,format2,format3]) {
        [self.view addConstraints:
        [NSLayoutConstraint constraintsWithVisualFormat:string 
                                                options:0 
                                                metrics:metrics
                                                  views:views]];
    }

The views under autolayout control need to have their translatesAutoresizingMaskIntoConstraints property set to NO (it defaults to YES).

In one of your comments you say that the views 'already know their frames'. This sounds a little confused: when using autolayout, views don't set frames, frames are the result of the autolayout equations (the autolayout mechanism sets them).

In any case whether or not you use autolayout, views shouldn't set their own frames, that should be the job of the view's superview context. The superview, or its viewController would make frame decisions, as a frame positions a view with respect to the superview.

It sounds like you may mean that the views already know their sizes, based on their content (in the same way that buttons and labels know their sizes). In this case they can return a size value by overriding -(CGSize) intrinsicContentSize in a UIView subclass. Then you can then omit size metrics from the format strings, simplifying them to:

Swift:

let format0 = "H:|-m-[view1]-m-[view3]-m-|"
let format1 = "H:|-m-[view2]-m-[view3]-m-|"
let format2 = "V:|-m-[view1]-m-[view2]-m-|"
let format3 = "V:|-m-[view3]-m-|"

Objective-C:

NSString* format0 = @"H:|-m-[view1]-m-[view3]-m-|";
NSString* format1 = @"H:|-m-[view2]-m-[view3]-m-|";
NSString* format2 = @"V:|-m-[view1]-m-[view2]-m-|";
NSString* format3 = @"V:|-m-[view3]-m-|";

However if the sizes don't all add up (eg 3*m + view1.height + view2.height != superview.height) something's going to break, and you are losing the advantage of using autolayout to flexibly arrange your views to fill the available space.

Upvotes: 1

Related Questions