UIView
。它的子视图位置是通过自动布局来管理的。然而,我想要明确地定位容器本身。该容器存在于我称之为
VideoPreviewView
的 UIView
子类中。该容器的名称为 contentOverlayView
。以下是我的代码,用于向容器添加一个明显的红色子视图,该子视图填充容器,除了边缘的 1 像素外。
UIView *const redView = [UIView new];
[redView setTranslatesAutoresizingMaskIntoConstraints: NO];
[redView setBackgroundColor: [UIColor redColor]];
[[videoView contentOverlayView] addSubview: redView];
[[videoView contentOverlayView] addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:@"H:|-(1@900)-[redView]-(1@900)-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(redView)]];
[[videoView contentOverlayView] addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-(1@900)-[redView]-(1@900)-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(redView)]];
在layoutSubviews
中设置框架
我最初保持容器视图位置的方法是简单地覆盖VideoPreviewView
的layoutSubviews
并在那里明确设置容器的框架,如下所示:
-(void) layoutSubviews
{
// Other stuff snipped out.
const CGRect videoBounds = [self videoBounds];
[_contentOverlayView setFrame: videoBounds];
[super layoutSubviews];
}
但是这样并不起作用。如果我在videoView
上使用recursiveDescription
,我会得到:
<VideoPreviewView: 0x146a54f0; frame = (0 0; 320 460); layer = <CALayer: 0x146a5640>>
| <UIView: 0x145b6610; frame = (0 0; 2 2); layer = <CALayer: 0x145b6680>>
| | <UIView: 0x145bff60; frame = (1 1; 0 0); layer = <CALayer: 0x145bffd0>>
这个框架的宽度刚好符合我要求的边距,但位置完全不对,而且框架也不够大。
我在容器视图上使用了_autoLayoutTrace
来尝试解决这个问题,但是被告知布局是有歧义的:
UIWindow:0x145a3a50
| UIView:0x145a6e60
| | HPBezelView:0x1468d580
| | | UIImageView:0x145aab30
| | *VideoPreviewView:0x146a54f0
| | | *UIView:0x145b6610- AMBIGUOUS LAYOUT for UIView:0x145b6610.minX{id: 5}, UIView:0x145b6610.minY{id: 13}, UIView:0x145b6610.Width{id: 8}, UIView:0x145b6610.Height{id: 16}
| | | | *UIView:0x145bff60- AMBIGUOUS LAYOUT for UIView:0x145bff60.minX{id: 4}, UIView:0x145bff60.minY{id: 12}, UIView:0x145bff60.Width{id: 9}, UIView:0x145bff60.Height{id: 17}
这启示我,如果我正在使用自动布局,那么框架将被忽略,因此我还需要使用自动布局设置容器的框架。
添加大小约束
其他一些帖子、博客等也建议采取这种方法。因此,我添加了一些约束来控制容器的宽度和高度:
_contentOverlayWidthConstraint =
[NSLayoutConstraint constraintWithItem: contentOverlayView
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: 1];
_contentOverlayHeightConstraint =
[NSLayoutConstraint constraintWithItem: contentOverlayView
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: 1];
[contentOverlayView addConstraints: @[_contentOverlayWidthConstraint, _contentOverlayHeightConstraint]];
我更新了我的
layoutSubviews
方法来调整这些约束的“常量”:-(void) layoutSubviews
{
const CGRect videoBounds = [self videoBounds];
[_contentOverlayView setFrame: videoBounds];
[_contentOverlayWidthConstraint setConstant: CGRectGetWidth(videoBounds)];
[_contentOverlayHeightConstraint setConstant: CGRectGetHeight(videoBounds)];
[super layoutSubviews];
}
这样做效果稍微好一些了——红色视图现在是可见的并且具有正确的尺寸,但它仍然处于错误的位置。我仍然从跟踪中得到歧义:
UIWindow:0x17d76a00
| UIView:0x17d7dfd0
| | HPBezelView:0x17d7d7c0
| | | UIImageView:0x17d82240
| | *VideoPreviewView:0x17d925e0
| | | UIView:0x17d96790
| | | *UIView:0x17d96830- AMBIGUOUS LAYOUT for UIView:0x17d96830.minX{id: 9}, UIView:0x17d96830.minY{id: 16}
| | | | *UIView:0x17d9ae90- AMBIGUOUS LAYOUT for UIView:0x17d9ae90.minX{id: 8}, UIView:0x17d9ae90.minY{id: 15}
...所以,宽度和高度的歧义已经消除了,但位置的歧义仍然存在。
添加位置约束
因此,我添加了另外两个布局约束来设置容器的位置:
_contentOverlayLeftConstraint =
[NSLayoutConstraint constraintWithItem: contentOverlayView
attribute: NSLayoutAttributeLeft
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: 0];
_contentOverlayTopConstraint =
[NSLayoutConstraint constraintWithItem: contentOverlayView
attribute: NSLayoutAttributeTop
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: 0];
[contentOverlayView addConstraints: @[_contentOverlayLeftConstraint, _contentOverlayTopConstraint]];
更新了layoutSubviews
:
-(void) layoutSubviews
{
[[self contentView] setFrame: [self bounds]];
[[self videoPreviewLayer] setFrame: [self bounds]];
const CGRect videoBounds = [self videoBounds];
[_contentOverlayWidthConstraint setConstant: CGRectGetWidth(videoBounds)];
[_contentOverlayHeightConstraint setConstant: CGRectGetHeight(videoBounds)];
[_contentOverlayLeftConstraint setConstant: CGRectGetMinX(videoBounds)];
[_contentOverlayTopConstraint setConstant: CGRectGetMinY(videoBounds)];
[super layoutSubviews];
}
崩溃
但是当我尝试添加新的约束时,我遇到了一个异常,错误信息如下:
* 由于未捕获到的异常 'NSInvalidArgumentException' 而终止应用程序,原因是:*+ [NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]:无法设置将位置设置为常量的约束。位置属性必须成对指定。
我完全不理解这个错误。我看不出我该如何纠正它。
你能帮忙吗?
layoutSubviews
中更改约束是完全合法的;有一个WWDC视频演示了这种技术。 - mattvideoView -> contentOverlayView -> redView
。videoView
是UIView
子类VideoPreviewView
的一个实例。它在其init
过程中创建了contentOverlayView
。它有约束条件(如讨论所述),将contentOverlayView
约束到它呈现的视频端口的顶部。redView
是我的调试工具,用于测试约束条件。它与contentOverlayView
一起添加到其他地方,并在其中添加了它与contentOverlayView
之间的约束条件,就像问题中所述。 - Benjohn