Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Chirone

macrumors 6502
Original poster
Mar 2, 2009
279
0
NZ
Hi, I want to put an image view on the left of a table view in landscape and above it in portrait.

So I have to do this in code. now... for landscape I have this constraint added
Code:
[NSLayoutConstraint constraintsWithVisualFormat:@"|-[imageView]-(20)-[tableView(280)]-|"
							 options:NSLayoutFormatAlignAllTop
							 metrics:nil
							   views:views];
As you can see from the string format...
- the left edge and the image view is the default space of 20,
- the image view has a width that I don't care about,
- the image view should resize
- there should be 20 pixels between the image view and table view
- the table view should be 280 wide
- and there should be a default space of 20 between the table view and the right edge.

now... this doesn't work. The image view resizes fine, but it complains about conflicting constraints.
It seems to think that the image view HAS to be a particular size.
Even if I set the hugging and compression resistance of the image view to be less than the table view it still has this issue.

it complains and says it has to break the -(20)- between the image and the table view (which doesn't make sense because it resizes the image view)

I can change the spacing between views and edges to an inequality and it won't complain (but wont show up right)

I can give the image view an explicit size and let the table view not have an explicit size and it won't complain.

does anyone know what I might be doing wrong? Do I need to give more info for this to make sense?
 

KoolStar

macrumors demi-god
Oct 16, 2006
825
9
Kentucky
I would recommend on viewDidLoad to copy all the current constraints into an array. I would also create an array of constraints from your ascii art form. Thus you will have two arrays one for portrait constraints and one for landscape constraints. On rotation you can swap out the constraints. That is one possible solution. Without seeing the project I cant determine what constraints you should manually remove or add.

I can however give you a nice macro to debug autolayout that I have come up with.

Code:
    #define ALMasterLog(view) NSLog((@"%s [Line %d] \r\r %@ \r\r %@ \r %@"), __PRETTY_FUNCTION__, __LINE__, [view performSelector:@selector(recursiveDescription)], [[view constraints] description], [view performSelector:@selector(_autolayoutTrace)]);

That macro can be used by adding it to the class or pre compiled header to have access to it in any class.

You simply need to call ALMasterLog(self.view) or some view in it. This will print out some basic information first such as the function its called in and the line of the call. After that it will print a recursive description of the view hierarchy followed by all constraints currently on that view and will show any ambiguous layouts. A sample of the output is below.

Code:
2013-01-16 07:31:22.205 Constraints_Hybrid[21115:c07] -[ViewController applyConstraintsForInterfaceOrientation:] [Line 76] 

 <UIView: 0x7482c10; frame = (0 0; 768 1004); autoresize = W+H; gestureRecognizers = <NSArray: 0x74833b0>; layer = <CALayer: 0x7482c70>>
   | <UIView: 0x747f990; frame = (394 20; 354 964); autoresize = W+H; gestureRecognizers = <NSArray: 0x7481b20>; layer = <CALayer: 0x747ed30>>
   |    | <UILabel: 0x747fae0; frame = (138 472; 78 21); text = 'View Gold'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x747fec0>; layer = <CALayer: 0x747fba0>>
   | <UIView: 0x7481b80; frame = (20 20; 354 472); autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7482490>; layer = <CALayer: 0x7481be0>>
   |    | <UILabel: 0x7481c10; frame = (128 225; 99 21); text = 'View Orange'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x7481ea0>; layer = <CALayer: 0x7481ca0>>
   | <UIView: 0x74824f0; frame = (20 512; 354 472); autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7482bc0>; layer = <CALayer: 0x7482550>>
   |    | <UILabel: 0x7482580; frame = (138 226; 79 21); text = 'View Grey'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x7482760>; layer = <CALayer: 0x7482610>> 

 (
    "<NSLayoutConstraint:0x7482d40 V:|-(NSSpace(20))-[UIView:0x7481b80]   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482da0 H:|-(NSSpace(20))-[UIView:0x7481b80]   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482de0 H:[UIView:0x7481b80]-(394)-|   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482e20 V:[UIView:0x747f990]-(NSSpace(20))-|   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482e60 H:|-(394)-[UIView:0x747f990]   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482ea0 V:|-(NSSpace(20))-[UIView:0x747f990]   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482ee0 H:[UIView:0x747f990]-(NSSpace(20))-|   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482f20 H:|-(NSSpace(20))-[UIView:0x74824f0]   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482f60 V:[UIView:0x74824f0]-(NSSpace(20))-|   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482fa0 V:|-(512)-[UIView:0x74824f0]   (Names: '|':UIView:0x7482c10 )>",
    "<NSLayoutConstraint:0x7482fe0 UIView:0x74824f0.width == UIView:0x7481b80.width>",
    "<NSLayoutConstraint:0x7483020 UIView:0x74824f0.height == UIView:0x7481b80.height>"
) 
 
*<UIView:0x7482c10>
|   *<UIView:0x747f990>
|   |   *<UILabel:0x747fae0>
|   *<UIView:0x7481b80>
|   |   *<UILabel:0x7481c10>
|   *<UIView:0x74824f0>
|   |

I am also including a sample code file to show you what i was talking about with adding and removing all constraints on rotation.

Code:
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *orangeView, *greyView, *goldView;
@property (nonatomic, strong) NSArray *myLConstraints, *myPConstraints;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setupConstraints];
    [self applyConstraintsForInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}

- (void)setupConstraints
{
    _myPConstraints = [self.view constraints];
    
    if (_myLConstraints) return;
    NSMutableArray *constraints = [NSMutableArray array];
    
    NSDictionary *views = NSDictionaryOfVariableBindings(_orangeView, _greyView, _goldView);
    NSDictionary *metrics = NSDictionaryOfVariableBindings(@20);
    
    NSArray *hConstriants = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_orangeView(==_greyView)]-20-[_greyView(==_orangeView)]-|"
                                                                    options:NSLayoutFormatDirectionLeftToRight
                                                                    metrics:metrics
                                                                      views:views];
    
    NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_orangeView(300)]-20-[_goldView]-|"
                                                                    options:NSLayoutFormatDirectionLeadingToTrailing
                                                                    metrics:metrics
                                                                      views:views];
    
    NSArray *v1Constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_greyView]-20-[_goldView]-|"
                                                                    options:NSLayoutFormatDirectionLeadingToTrailing
                                                                    metrics:metrics
                                                                      views:views];
    NSArray *h1Constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_goldView]-|"
                                                                    options:NSLayoutFormatDirectionLeadingToTrailing
                                                                    metrics:metrics
                                                                      views:views];
    
    
    [self mergeArraysInto:&constraints, hConstriants, h1Constraints, vConstraints, v1Constraints, nil];
    _myLConstraints = (NSArray*)constraints;
    return;

}

- (void)applyConstraintsForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
    {
        [self.view removeConstraints:_myPConstraints];
        [self.view addConstraints:_myLConstraints];
    }
    else
    {
        [self.view removeConstraints:_myLConstraints];
        [self.view addConstraints:_myPConstraints];
    }
    //ALCLog(self.view);
    //ALAMBLog(self.view);
    ALMasterLog(self.view);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self applyConstraintsForInterfaceOrientation:toInterfaceOrientation];
}

- (void)mergeArraysInto:(NSMutableArray**)intoArray, ...
{
    NSArray *eachObject;
    va_list argumentList;
    if (intoArray) // The first argument isn't part of the varargs list,
    {
        va_start(argumentList, intoArray); // Start scanning for arguments after firstObject.
        while ((eachObject = va_arg(argumentList, NSArray*))) // As many times as we can get an argument of type "id"
        {
            [*intoArray addObjectsFromArray:eachObject]; // that isn't nil, add it to self's contents.
        }
        va_end(argumentList);
    }
}

@end
 

Chirone

macrumors 6502
Original poster
Mar 2, 2009
279
0
NZ
Hey thanks for that!

Pretty detailed there. I'll give it a whirl when I get a chance.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.