我将以下
CAShapeLayers
和 UIViews
设置附加到 UIView
:
[self.layer addSublayer:self.shapeLayer1];
if (someCase)
{
[self.layer addSublayer:self.shapeLayerOptional];
}
[self.layer addSublayer:self.shapeLayer2];
[self addSubview: self.view1];
我希望有一个功能允许用户在 shapeLayer2 之前插入另一个
UIView
,我们称之为 view0
,但我不知道如何实现这一点。父级 UIView
subviews
属性仅包含 view1
,而其 layer.sublayers
属性包含 shapelayers 和 subview1 的图层,但我不想将 subview0.layer
插入到 layers.sublayers
中,因为 subview0
有一个UITouchGestureRecognizer
。表面上这似乎是一个简单的设计问题,但由于某种原因它确实让我感到困惑。有人有任何推荐的解决方案吗?如果可以的话,我想尝试避免将 CAShapeLayers
包装成 UIViews
。
有几点需要注意...
图层 - 无论是
CALayer
、CAShapeLayer
、CAGradientLayer
等 - 都属于视图。您可以通过.zPosition
操纵图层的外观。
此外,图层本身并不真正存在...它必须作为子图层添加到视图的图层中。
视图有自己的图层...因此,在尝试操纵图层顺序时,必须考虑到这一点。
因此,您的目标是拥有一个具有多个图层的视图,另一个具有一个或多个图层的视图,以及在第一个视图的图层之间插入第二个视图(及其图层)。
如果我们从两个视图开始——黄色,有两个
CAShapeLayer
子层;带有一个 CAShapeLayer
子层的青色:
看起来像这样(分层):
我们想要在yellowView的矩形和椭圆形层之间插入cyanView(及其三角形子层):
让我们添加cyanView作为yellowView的子视图,偏移40,40和
yellowView.clipsToBounds = true
,这样我们就可以看到它是一个子视图:
这给了我们我们所期望的......但不是我们想要的,因为矩形和椭圆形层属于yellowView。
要将椭圆形层移动到顶部,我们将更改
.zPosition
属性:
// we don't want to mess with the .zPosition of yellow view's layer,
// because it's the "base" of the hierarchy
rectShapeLayer.zPosition = 1;
cyanView.layer.zPosition = 2;
triShapeLayer.zPosition = 3;
ovalShapeLayer.zPosition = 4;
我们得到这个结果:
cyanView 及其三角形层现在位于黄色视图的矩形和椭圆形层之间。
目标达成了吗?也许...
现在让我们看看调试视图层次结构:
请注意,图层并未与其视图分离。
在上面的第一个层次结构图像中,我使用单独的视图只是为了展示我们正在尝试做的事情。
现在,管理
.zPosition
值可能对你有用...但我认为仅实现图层或仅实现视图会更容易(我们可以更改视图的图层类以消除添加的需要)每个视图的子层)。
这里是一些生成上述内容的示例代码:
ViewsAndLayersViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ViewsAndLayersViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
ViewsAndLayersViewController.m
#import "ViewsAndLayersViewController.h"
@implementation ViewsAndLayersViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
info = [UILabel new];
info.numberOfLines = 0;
info.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:info];
UILayoutGuide *g = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[info.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:20.0],
[info.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-20.0],
[info.topAnchor constraintEqualToAnchor:g.topAnchor constant:20.0],
]];
[self reset];
}
- (void)reset {
[cyanView removeFromSuperview];
[yellowView removeFromSuperview];
cyanView = nil;
yellowView = nil;
yellowView = [UIView new];
cyanView = [UIView new];
rectShapeLayer = [CAShapeLayer new];
ovalShapeLayer = [CAShapeLayer new];
triShapeLayer = [CAShapeLayer new];
yellowView.clipsToBounds = YES;
[self.view addSubview:yellowView];
[self.view addSubview:cyanView];
CGRect r = CGRectMake(40.0, 160.0, 240.0, 200.0);
yellowView.frame = r;
cyanView.frame = CGRectOffset(r, 0.0, r.size.height + 20.0);
yellowView.backgroundColor = UIColor.yellowColor;
cyanView.backgroundColor = UIColor.cyanColor;
for (CAShapeLayer *s in @[rectShapeLayer, ovalShapeLayer, triShapeLayer]) {
s.fillColor = UIColor.clearColor.CGColor;
s.lineWidth = 16;
s.lineJoin = kCALineJoinRound;
}
rectShapeLayer.strokeColor = UIColor.systemRedColor.CGColor;
ovalShapeLayer.strokeColor = UIColor.systemGreenColor.CGColor;
triShapeLayer.strokeColor = UIColor.systemBlueColor.CGColor;
[yellowView.layer addSublayer:rectShapeLayer];
[yellowView.layer addSublayer:ovalShapeLayer];
[cyanView.layer addSublayer:triShapeLayer];
UIBezierPath *pth;
CGRect pRect = CGRectInset(yellowView.bounds, 20.0, 20.0);
CGPoint p;
// rectangle path
pth = [UIBezierPath bezierPathWithRect:pRect];
rectShapeLayer.path = pth.CGPath;
// oval path
pth = [UIBezierPath bezierPathWithOvalInRect:pRect];
ovalShapeLayer.path = pth.CGPath;
// triangle path
pth = [UIBezierPath new];
p = CGPointMake(CGRectGetMidX(pRect), CGRectGetMinY(pRect));
[pth moveToPoint:p];
p = CGPointMake(CGRectGetMaxX(pRect), CGRectGetMaxY(pRect));
[pth addLineToPoint:p];
p = CGPointMake(CGRectGetMinX(pRect), CGRectGetMaxY(pRect));
[pth addLineToPoint:p];
[pth closePath];
triShapeLayer.path = pth.CGPath;
info.text = @"Tap to add cyanView as a subview of yellowView, offset by 40,40";
iStep = 0;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
++iStep;
switch (iStep) {
case 1:
[self step1];
break;
case 2:
[self step2];
break;
case 3:
[self step3];
break;
default:
break;
}
}
- (void)step1 {
cyanView.frame = CGRectOffset(yellowView.bounds, 40.0, 40.0);
[yellowView addSubview:cyanView];
info.text = @"Tap to arrange layers via .zPosition";
}
- (void)step2 {
// we don't want to mess with the .zPosition of yellow view's layer,
// because it's the "base" of the hierarchy
rectShapeLayer.zPosition = 1;
cyanView.layer.zPosition = 2;
triShapeLayer.zPosition = 3;
ovalShapeLayer.zPosition = 4;
info.text = @"Tap to reset...";
}
- (void)step3 {
[self reset];
}
@end