Auto layout on image view

Discussion in 'iOS Programming' started by Chirone, Jan 14, 2013.

  1. Chirone macrumors 6502

    Joined:
    Mar 2, 2009
    Location:
    NZ
    #1
    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?
     
  2. KoolStar macrumors demi-god

    KoolStar

    Joined:
    Oct 16, 2006
    Location:
    Kentucky
    #2
    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
    
    
     
  3. Chirone thread starter macrumors 6502

    Joined:
    Mar 2, 2009
    Location:
    NZ
    #3
    Hey thanks for that!

    Pretty detailed there. I'll give it a whirl when I get a chance.
     
  4. KoolStar macrumors demi-god

    KoolStar

    Joined:
    Oct 16, 2006
    Location:
    Kentucky
    #4
    Your quit welcome, if you need anymore help with auto layout or anything else don't hesitate to ask. :)
     

Share This Page