使用 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
)。
这是对我之前发布的一个问题的跟进:
我正在使用描述的方法将我想要旋转的视图放入另一个 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
)。