1

I'm working on an iOS (6) app, and I have a view, whose size I change (animated), and within that view is a UILabel, which I want to grow with the view, but not proportionally.

i.e. I need the view to take up, say 90% of the view if the view is a certain size, but when I make it bigger (to another certain size), I only want it to take up, say 80%. This is to accommodate other subviews around the UILabel that will become visible when the super view grows.

I have tried using NSLayoutContraints with this, and setting the leading space to what it needs to be, as well as setting the width of the UILabel to get it to grow appropriately, and I have also tried with settings the leading and trailing spaces (as the width would then automatically grow).

Oddly, I have other subviews whose NSLayoutContraints I am playing with, in the same animation code block (using the UIView animateWithDuration:...) and they all work. All of these, however are spacing constraints, and not size constraints, and none involve UILabels so I dont know if this has anything to do with it...

I then tried setting the UILabel's translatesAutoresizingMaskIntoConstraints = YES, and just using setFrame: on the label, and although this sets the size and position of the label exactly where I want it, the change is not animated, so it looks horrible...

Has anybody else had experience with anything like this or any ideas to help me out?

Thanks

Following is some code I have been trying with, where "left" is the leading space constraint of the label and "width" is the width constraint of the label.

The setFrame method is called within an animate code block from the parent view controller.

- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];

    float gap = ((frame.size.height - 36) - 9 * [self.account.accountData numberOfBars]) / ([self.account.accountData numberOfBars] + 1);
    for (NSLayoutConstraint *constraint in constraints) {
        constraint.constant = gap;
    }

    self.left.constant = frame.size.width * 34/160 + (2 - 34 * 140 / 160);
    self.width.constant = frame.size.width * 0.575 + 55.5;

    [self layoutIfNeeded];

}
Nick Duffell
  • 340
  • 1
  • 6
  • 17

1 Answers1

1

You should be able to do this in code using constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:. Using the multiplier and constant together should work. Set the width of your label to be equal to the width of its superview, but with a multiplier of say 0.7 or 0.8 and then a constant that gives you the right width at the smaller size of the view. As the view grows, that constant value will make up a smaller portion of the overall width. For instance, with a multiplier of .7 and a constant of 20, if the superview were 100 points wide, the label would be 90 points (100*.7 +20). If the view grows to 200, the label would be 160 wide (or 80% of the superview).

After Edit:

Actually, I think the method I mention above won't work. In the WWDC 2012 talk, "Best Practices For Mastering Auto Layout", they talk about using core animation or animating the constraints directly. I think this is a case where you have to do the latter. If you use core animation, the label jumps to its new size while the view animates, even if both are within an animation block (at least it did for me -- Rob, if you know better, please post). To animate the constraints directly (in IOS) you have to use a timer, and increment the constraint constant. You could use an NSTimer, but I found that to be a bit jerky because of the limitations of its resolution (50-100 mSec). A smoother way is to use a CADisplayLink which is tied to the display refresh rate (I think that's 16.7 mSec). So here's the code I used. My setup in IB is a view that's 190 wide with a 170 wide label inside. I have width constraints on both (you need to add the QuartzCore framework for this).

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()
@property (strong,nonatomic) CADisplayLink *displayLink;
@end

@implementation ViewController {

    IBOutlet NSLayoutConstraint *labelWidthCon;
    IBOutlet NSLayoutConstraint *viewWidthCon;
}


-(IBAction)startViewTimer:(id)sender {
    [self startDisplayLink];
}

-(void)startDisplayLink {
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(expandView:)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)stopDisplayLink {
    [self.displayLink invalidate];
    self.displayLink = nil;
}


-(void)expandView:(CADisplayLink *) link {
    labelWidthCon.constant += 5;
    viewWidthCon.constant +=10;
    if (viewWidthCon.constant > 370) [self stopDisplayLink];
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • OK, I tried this, but I get the same thing as I was getting before. The width of the label immediately changes to what it should be at the end of the animation, and does not animate... – Nick Duffell Feb 10 '13 at 02:29
  • @NickDuffell If you're changing the constraints and are doing this within an `animateWithDuration`, you need to put not only whatever will result in the new layout (e.g. the adding of constraints, changing of them, etc.) within the `animations` block, but also include the `[view layoutIfNeeded]` in the animation block, too. But I agree with rdelmar, that you need to update your question with the code that will result in the changing views, and we can help you more on the animation process. – Rob Feb 10 '13 at 02:56
  • @NickDuffell If you want to animate that, wrap it in a `animateWithDuration`. See http://stackoverflow.com/questions/14189362/animating-an-image-view-to-slide-upwards/14190042#14190042 for an example (slightly different application, but hopefully it gives you a sense of how to animate the change; just look at how I use `animateWithDuration`). – Rob Feb 10 '13 at 03:26
  • "using the UIView animateWithDuration:..." As stated, I am using that, but the animation is called one level up the call stack – Nick Duffell Feb 10 '13 at 03:37
  • @Rob, I think the way I originally posted won't work, and you have to animate the constraints directly. See my edit. – rdelmar Feb 10 '13 at 05:26