使用 layoutSubviews 时 iOS 应用程序中的布局约束消息

Layout Constraint Messages in iOS app when using layoutSubviews

这是对我之前发布的一个问题的跟进:

我正在使用描述的方法将我想要旋转的视图放入另一个 UIView,并在 layoutSubviews 方法中进行仿射变换以进行旋转。

虽然这在只有一个要旋转的 UISlider 的应用程序中有效,但我现在希望在整个 UIView 上使用这种旋转方法,每个 UIView 由几个子视图组成,它们也有子视图,所有子视图都使用自动布局约束来定位元素。

@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

这实际上为我生成了正确的视觉结果,但出于某种原因,它给了我约束错误消息,例如:

 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]>

它列出的第一个约束对我来说很有趣,因为它似乎认为我希望 MMXChannelStripView 为 0 像素,而我没有。

请注意,即使我不旋转视图,但仍然使用 layoutSubviews 将其作为子视图包括在内,也会出现此错误消息。为什么我在 layoutSubviews 中所做的与我在更多嵌入式视图中所做的自动布局不兼容?

我附上了一张屏幕截图来展示我的目的。灰色视图是旋转后的视图,蓝色视图是未旋转且未包含在另一个视图中的视图,因此不会给出错误消息。

改用这个:

- (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;
}

以编程方式实例化视图时,请务必将其 translatesAutoresizingMaskIntoConstraints 属性 设置为 NO。默认情况下,系统会根据视图的框架及其自动调整大小自动创建一组约束。当您添加自己的约束时,它们不可避免地会与自动生成的约束发生冲突。这会创建一个无法令人满意的布局,因此您会在控制台中收到这些消息。

我认为这里的问题是您设置 MMXChannelStripView 的中心和边界太晚了。到-[MMXRotateChannel layoutSubviews]为运行时,自动布局已经解决了约束系统,并在解决时产生错误信息。

无论如何,试试这个来初始化条带:

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],
]];

而且您在 layoutSubviews 中不需要任何东西。这在我的测试中有效(UISlider)。