Subview's sublayers overlapping higher subview

2019-06-27 17:34发布

I have an issue: I'm creating a UIView that is returned from a method, and that part is fine, but what I've noticed is that when I add sublayers to one of the subviews, those layers overlap subviews that are higher up in the hierarchy (textView and imageView) the sublayers added to testViewCopy are appearing over top of these subviews, and they aren't supposed to. I have no idea what's going on here to cause this.

Code:

- (void)makeShareImages
{
    UIView *shareView = [self shareView];

    UIView *testViewCopy = [shareView viewWithTag:0];

    NSUInteger currentIndex = 1;

    for (NSDictionary *sub in self.array)
    {
        NSArray *lastArray = [sub objectForKey:@"LastArray"];

        for (NSDictionary *dict in lastArray)
        {
            @autoreleasepool
            {
                currentIndex ++;

                CircleLayer *layer = [[CircleLayer alloc]init];
                layer.portrait = [[dict objectForKey:@"Portrait"]boolValue];

                layer.frame = testViewCopy.bounds;

                [testViewCopy.layer addSublayer:layer];

                NSData *frameData = [self getSnapshotDataFromView:shareView];

                NSString *savePath = [NSString stringWithFormat:@"%@/%lu.png",somePath,(unsigned long)currentIndex];

                [frameData writeToFile:savePath options:0 error:nil];
            }
        }
    }
}

- (UIView *)shareView
{
    UIColor *bgColor = self.containerView.backgroundColor;

    CGSize size = self.containerView.bounds.size;

    UIView *viewToShare = [[UIView alloc]init];
    viewToShare.backgroundColor = bgColor;
    viewToShare.layer.cornerRadius = 6.0;
    viewToShare.layer.masksToBounds = YES;

    UIView *testViewCopy = [[UIView alloc]init];
    testViewCopy.backgroundColor = [UIColor clearColor];
    testViewCopy.contentMode = UIViewContentModeScaleAspectFit;
    testViewCopy.layer.masksToBounds = YES;
    testViewCopy.tag = 0;

    UITextView *textViewCopy = [[UITextView alloc]init];
    textViewCopy.backgroundColor = [UIColor clearColor];
    textViewCopy.tag = 1;
    textViewCopy.textContainerInset = self.textView.textContainerInset;

    UIImageView *profileImageViewCopy = [[UIImageView alloc]initWithFrame:CGRectMake(2, 2, 32, 32)];
    profileImageViewCopy.contentMode = UIViewContentModeScaleAspectFit;
    profileImageViewCopy.layer.masksToBounds = YES;
    profileImageViewCopy.image = [self profileImage];
    profileImageViewCopy.tag = 2;
    profileImageViewCopy.layer.cornerRadius = profileImageViewCopy.frame.size.width / 2.0;

    viewToShare.frame = CGRectMake(0, 0, size.width, size.height);
    testViewCopy.frame = CGRectMake(0, 0, size.width, size.width);
    textViewCopy.frame = CGRectMake(0, 0, size.width, size.height);

    NSAttributedString *attributedStringCopy = [[NSAttributedString alloc]initWithAttributedString:self.textView.attributedText];

    textViewCopy.attributedText = attributedStringCopy;

    [viewToShare addSubview:testViewCopy];
    [viewToShare addSubview:textViewCopy];
    [viewToShare addSubview:profileImageViewCopy];

    return viewToShare;
}

- (NSData *)getSnapshotDataFromView:(UIView *)view
{
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *snapShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return UIImagePNGRepresentation(snapShot);
}

1条回答
小情绪 Triste *
2楼-- · 2019-06-27 17:41

Your code is behaving correctly. All sibling views - subviews of a common superview - have a definite layering order, back to front. addSubview: adds the subview as the last among its siblings; thus, it is layered in front of all existing subviews of the same superview. If that isn't what you want, insert the subview at a further layer back from the front, or add it (at the front) and then move it further back in the layering order.

Moreover (and here I think we are getting closer to the particular phenomenon you have noticed), views are themselves just layers. Thus, the layering order of views is actually a subset of the layering order of layers. The same thing I just said about views is equally true of layers, because views are layers: if you add a sublayer to a superlayer, it is added in front of all other sublayers of that superlayer, including any subviews if that superlayer is actually a view.

As I write in the current edition of my book:

A view's subview's underlying layer is a sublayer of that view's underlaying layer, just like any other sublayers of that view's underlying layer. Therefore, it can be positioned anywhere among them in the drawing order. The fact that a view can be interspersed among the sublayers of its superview's underlying layer is surprising to beginners.

enter image description here

It sounds like you've just discovered this, and are appropriately surprised.

It may help to think of it in terms of the drawing of the render tree, in active terms. In other words, instead of thinking about how things look, think about what iOS does. A layer can have a superlayer, except for the ultimate superlayer, and a layer can have a previous sibling, except for the first sublayer of a superlayer. Conversely, a layer can have sublayers and it can have next siblings. We start by drawing the ultimate superlayer (the window). Then, for every layer we have just drawn, follow these two rules in order:

  • Draw its first sublayer, if any.

  • Draw its next sibling, if any.

Every time we draw a layer, it is in front of everything we have previously drawn. Thus all sublayers of a superlayer appear in front of the superlayer, and all later siblings (and their sublayers) appear in front of all previous siblings (and their sublayers). This results in what you see on the screen.

(However, note that you have even more flexibility, because among siblings you can manipulate the drawing order without rearranging the siblings, by setting the zPosition property of a layer.)

查看更多
登录 后发表回答