0
votes

This is somewhat of a followup to a previous question I posted:

Using NSLayoutConstraint on a subview in conjuction with an affine transform

I'm using the described method of placing a view I want to be rotated into another UIView, and doing the affine transform to rotate in the layoutSubviews method.

While this worked in an application having only a single UISlider to rotate, I now wish to use this method of rotation on entire UIViews, each composed of several subviews, which also have subviews, all of which use autolayout constraints to position the elements.

@implementation MMXRotateChannel

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.cstrip = [[MMXChannelStripView alloc] init];
        [self addSubview:self.cstrip];

        return self;
    }
    return nil;
}

// 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.cstrip.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);

    self.cstrip.center = [self convertPoint:self.center fromView:self.superview];

    // rotate it
    self.cstrip.transform = CGAffineTransformMakeRotation(M_PI * .5);

}

@end

This is actually producing the right visual result for me, but for some reason it's giving me constraint error messages, such as the following:

 Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x7ff932e8dae0 h=--& v=--& V:[MMXChannelStripView:0x7ff932e800d0(0)]>",
    "<NSLayoutConstraint:0x7ff932e16ce0 MMXFaderView:0x7ff932e7e4d0.bottom == MMXChannelStripView:0x7ff932e800d0.bottom>",
    "<NSLayoutConstraint:0x7ff932e6c150 V:|-(0)-[MMXChannelControlView:0x7ff932e1ac50]   (Names: '|':MMXChannelStripView:0x7ff932e800d0 )>",
    "<NSLayoutConstraint:0x7ff932e6d530 V:[MMXChannelControlView:0x7ff932e1ac50]-(0)-[MMXFaderView:0x7ff932e7e4d0]>",
    "<NSLayoutConstraint:0x7ff932e6d5d0 V:[MMXFaderView:0x7ff932e7e4d0(>=500)]>",
    "<NSLayoutConstraint:0x7ff932e07400 MMXChannelControlView:0x7ff932e1ac50.height >= 0.5*MMXFaderView:0x7ff932e7e4d0.height>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7ff932e6d530 V:[MMXChannelControlView:0x7ff932e1ac50]-(0)-[MMXFaderView:0x7ff932e7e4d0]>

The first constraint it lists is interesting to me, because it seems to think that I want the MMXChannelStripView to be 0 pixels, which I don't.

Note that this error message also occurs even when I don't rotate the view, but still include it as a subview using layoutSubviews. Is there some reason why what I'm doing in layoutSubviews should be incommpatible with the auto layout I'm doing at more embedded views?

I've included a screenshot to show what I'm going for. The grey view is the rotated one, and the blue one is the view which is not rotated and not enclosed in another view, and as such is not giving error messages.

Rotated and not rotated views

2
What is the value of translatesAutoresizingMaskIntoConstraints on the view?Tjalsma

2 Answers

1
votes

I think the problem here is that you're setting the center and bounds of the MMXChannelStripView too late. By the time -[MMXRotateChannel layoutSubviews] is running, auto layout has already solved the constraint system, and it generates the error messages while solving.

Anyway, try this instead to initialize the strip:

self.cstrip = [[MMXChannelStripView alloc] init];
self.cstrip.translatesAutoresizingMaskIntoConstraints = NO;
self.cstrip.transform = CGAffineTransformMakeRotation(M_PI_2);
[self addSubview:self.cstrip];
[NSLayoutConstraint activateConstraints:@[
    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.cstrip attribute:NSLayoutAttributeWidth multiplier:1 constant:0],
    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.cstrip attribute:NSLayoutAttributeCenterX multiplier:1 constant:0],
    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.cstrip attribute:NSLayoutAttributeCenterY multiplier:1 constant:0],
]];

And you shouldn't need anything in layoutSubviews. This worked in my testing (with UISlider).

0
votes

Use this instead:

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.cstrip = [[MMXChannelStripView alloc] init];
        self.cstrip.translatesAutoresizingMaskIntoConstraints = NO;
        [self addSubview:self.cstrip];

        return self;
    }
    return nil;
}

When programmatically instantiating views, be sure to set their translatesAutoresizingMaskIntoConstraints property to NO. By default, the system automatically creates a set of constraints based on the view’s frame and its autoresizing mask. When you add your own constraints, they inevitably conflict with the autogenerated constraints. This creates an unsatisfiable layout and hence you get those messages in the console.