0
votes

Here is the situation, I build most of my interface in interface builder with auto layout, but there is a subview that is so complicated, that i would rather to layout it with custom code, I find in the documentation that I can override layoutSubviews() to implement my custom code

"Subclasses can override this method as needed to perform more precise layout of their subviews. You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly."

but I find that when I set the frame of the subviews, it just don't have any effects, I think there is something wrong with the interaction of auto layout system.But can anyone tell me where is wrong?

1

1 Answers

0
votes

When you enable autolayout, any code you write to set frames are ignored by the iOS system.

This is how I normally build my UI these days:

-(void)viewDidLoad
{
    [self initViews];
    [self initConstraints];
}

-(void)initViews
{
    // --------------------------------------------
    // The default init method calls initWithFrame: 
    // method and passes it a CGRectZero
    // --------------------------------------------
    self.textLabel = [[UILabel alloc] init]; 
    self.textLabel.text = @"Hello World!";

    [self.view addSubview:self.textLabel];
}

-(void)initConstraints
{
    // this line will cause any setFrame: calls to be ignored
    self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;

    id views = @{
                   @"textLabel": self.textLabel
               };

    // horizontal constraint
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[textLabel]|" options:0 metrics:nil views:views]];

    // vertical constraint
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[textLabel]|" options:0 metrics:nil views:views]];
}

If you need to change the position of the textLabel for example, you would need to instead declare a NSLayoutConstraint property in your class:

@property (nonatomic, strong) NSLayoutConstraint *textLabelTopConstraint;

Then modify your constraint code to something like this:

-(void)initConstraints
{
    self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;

    id views = @{
                   @"textLabel": self.textLabel
               };

    // horizontal constraint
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[textLabel]|" options:0 metrics:nil views:views]];

    // vertical constraint

    // --------------------------------------------
    // Note the constant value, you'll update this
    // later and tell the UI to relayout.
    // --------------------------------------------
    self.textLabelTopConstraint = [NSLayoutConstraint constraintWithItem:self.textLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];

    // note theres no 's' after addConstraint
    [self.view addConstraint:self.textLabelTopConstraint];


}

When you tap a button and want to change the textLabel's top position:

-(void)buttonTappedMethod
{
    // tell textLabel it should be offset 50 pixels from the top
    self.textLabelTopConstraint.constant = 50;

    // --------------------------------------------------------------------
    // wrap the layout command inside a UIView animation block to
    // see the textLabel animate down rather than instantly appear there
    // --------------------------------------------------------------------
    [UIView animateWithDuration:0.5 animations:^{

        [self.view layoutIfNeeded];

    }];
}