Justin
Justin

Reputation: 119

Using NSLayoutConstraint on a subview in conjuction with an affine transform

I need to have vertical sliders. To do this, I have a UIView class that contains a UISlider, and inside initWithFrame:, I rotate the slider with an affine transform by 90 degrees.

I'm now adding Autolayout support to it.

I wish to allow the slider to be instantiated with different lengths, so that it can be used in a dynamic, Autolayout sense. This is the code I'm trying in order to get the rotated slider to conform to the bounds of the UIView, located in initWithFrame:

self = [super initWithFrame:frame];
if(self) {
    self.slider = [[UISlider alloc] init];
    [self addSubview:self.slider];
    self.slider.value = 0;

    self.slider.transform = CGAffineTransformMakeRotation(M_PI * 1.5);
    [self.slider setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self.slider attribute: NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self.slider attribute: NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self.slider attribute: NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f]];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self.slider attribute: NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]];

    self.backgroundColor = [UIColor colorWithWhite:.8 alpha:1.0];
    self.layer.borderWidth = 1;
    self.slider.backgroundColor = [UIColor colorWithRed:.21 green:.95 blue:1 alpha:1];
    self.slider.layer.borderWidth = 1;

    return self;
}

This is what the resulting view looks like. I've instantiated three different vertical sliders. The one on the left has a frame of 300 by 300, the one on the right has a frame of 50 by 300, and the one in the middle has a frame of 300 by 50. I've colored the background of the UIView frames to be grey, and colored the background of the UISliders to be blue.

Vertical Slider instances

The problem with the 300 by 300 one is that I don't think I can afford to have the frame be so big. There will be other views around, and I'm afraid that that one will get in the way.

The problem with the one on the left is that the slider is too small.

The problem with the one in the middle is that since the slider has been rotated out of the view's frame, it's impossible to grab the thumb and move it around.

I've also tried constraining the top of the slider to the right of the view, and the right of the slider to the bottom of the view, but this produces errors and unpredictable results.

What do I need to change to make these constraints work?

Upvotes: 4

Views: 358

Answers (1)

danh
danh

Reputation: 62676

Simpler than it seems. The UIView parent is all setup to comply with layout constraints. It can manage its slider subview much more simply Here's the whole class. This can be created in code with initWithFrame:, or drop a view into IB and set the class == TwistedSlider. Here's the whole thing...

@interface TwistedSlider ()
@property(weak,nonatomic) UISlider *slider;
@end

@implementation TwistedSlider

- (UISlider *)slider {
    if (!_slider) {
        UISlider *slider = [[UISlider alloc] init];
        [self addSubview:slider];
        _slider = slider;
    }
    return _slider;
}

// this works for any size of this view.  the slider will always be as tall as this view is wide
- (void)layoutSubviews {
    [super layoutSubviews];
    // size it to be as wide as this view's height, center it in this view
    self.slider.bounds = CGRectMake(0, 0, self.bounds.size.height, self.slider.bounds.size.height);
    self.slider.center = [self convertPoint:self.center fromView:self.superview];
    // rotate it
    self.slider.transform = CGAffineTransformMakeRotation(M_PI_2);
}

// that's it!
@end

Upvotes: 4

Related Questions