【问题标题】:renderInContext does not work correctly when changing layer Z position更改图层 Z 位置时,renderInContext 无法正常工作
【发布时间】:2016-03-01 08:12:24
【问题描述】:

我的应用程序允许用户前后切换UIImageView,然后用户可以捕获该屏幕。这是我将屏幕捕获到UIImage的代码

-(UIImage *)imageWithView:(UIView *)view
{
   UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 2.0f);

// I even tried view.layer.presentationLayer but still not working
[view.layer renderInContext:UIGraphicsGetCurrentContext()];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


return image;

}

我不使用[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES],因为它速度较慢,有时(当视图不可见时)会将屏幕截图绘制为黑色。

但 renderInContext 的问题是在UIImageView ( imageView.layer.zPosition = 0.01;etc ) 之间变化的 Z 位置。在我的 iPhone 屏幕上,当我将值分配给 zPosition 时,它可以正常工作,但在捕获屏幕中它结果是错误的。

无论如何我可以解决这个问题吗?提前致谢

编辑: 这是我在捕获屏幕截图之前尝试做的事情。我使用此代码使一个 ImageView 显示在另一个之前。

-(void)bringOneLevelUp:(UIImageView*)imageView
{
//_imgArray is sorted by Z position order (small to big)
    NSUInteger currentObjectIndex = [_imgArray indexOfObject:imageView];


if(currentObjectIndex+1< _imgArray.count){

UIImageView *upperImageView = [_imgArray objectAtIndex:currentObjectIndex+1];


    CGFloat currentZIndex = imageView.layer.zPosition;
    CGFloat upperZIndex= upperImageView.layer.zPosition;

    imageView.layer.zPosition = upperZIndex;
    upperImageView.layer.zPosition = currentZIndex;


// swap position in array 
[_imgArray exchangeObjectAtIndex:currentObjectIndex withObjectAtIndex:currentObjectIndex+1];

}

}

就像我之前解释的那样,手机屏幕上的这段代码的结果是正确的。 newImageView 在 lastImageView 前面。但是当我通过 renderInContext 捕获屏幕截图时。他们不是。

【问题讨论】:

  • “结果错误”是什么意思?您正在尝试什么 z 位置值?
  • 我交换了 UIImageView 的 z 位置。它在手机屏幕上看起来是正确的,但在我尝试捕获它时却不正确。
  • 我不太明白你的代码在做什么。为什么你没有在你的问题中显示它?
  • 我已经添加了您要求的代码。但这无关紧要。问题是 renderInContext 没有捕获视图层的 zPosition。我尝试使用 layer.presentationLayer。但它仍然无法正常工作

标签: ios objective-c iphone uiimageview calayer


【解决方案1】:

这是 Apple 的一个恼人的错误,但解决方案很简单,只需使用比较器根据 zPosition 递归地重新排序您正在快照的子视图(及其子视图(等等))的视图。

只需调用以下方法,在快照之前直接传递您要快照的视图。

///UIGraphicsGetCurrentContext doesn't obey layer zPositions so have to adjust view heirarchy (integer index) to match zpositions (float values) for all nested subviews.
-(void)recursivelyAdjustHeirarchyOfSubviewsBasedOnZPosition:(UIView *)parentView {

    NSArray *sortedSubviews = [[parentView subviews] sortedArrayUsingComparator:
                           ^NSComparisonResult(UIView *view1, UIView *view2)
                           {
                               float zpos1 = view1.layer.zPosition;
                               float zpos2 = view2.layer.zPosition;
                               return zpos1 > zpos2;
                           }];

    for (UIView *childView in sortedSubviews) {
        [parentView bringSubviewToFront:childView];
        [self recursivelyAdjustHeirarchyOfSubviewsBasedOnZPosition:childView];
    }

}

【讨论】:

    【解决方案2】:

    Swift 4.2

    @RameshVel 的回答帮我把它翻译成 Swift,也许有人需要这个:

     UIGraphicsBeginImageContext(drawingCanvas.frame.size)
     self.renderInContext(ct: UIGraphicsGetCurrentContext()!)
    
     guard let image = UIGraphicsGetImageFromCurrentImageContext() else {return}
     UIGraphicsEndImageContext()
    

    而在renderInContext函数中:

     func renderInContext(ct:CGContext){
    
        ct.beginTransparencyLayer(auxiliaryInfo: nil)
    
        // descendant ordered list of views based on its z-position
        let orderedLayers = self.view.subviews.sorted(by: {
            $0.layer.zPosition < $1.layer.zPosition
        })
    
        for l in orderedLayers {
            l.layer.render(in: ct)
        }
        ct.endTransparencyLayer()
    }
    

    如果您正在绘制和添加图层,CALayers 也可以这样做:

     UIGraphicsBeginImageContext(drawingCanvas.frame.size)
     //Canvas is a custom UIView for drawing
     self.renderInContext(ct: UIGraphicsGetCurrentContext()!, canvas: canvas)
    
     guard let image = UIGraphicsGetImageFromCurrentImageContext() else {return}
     UIGraphicsEndImageContext()
    

    renderInContext 函数:

    func renderInContext(ct:CGContext, canvas:CustomCanvas){
    
        ct.beginTransparencyLayer(auxiliaryInfo: nil)
        let layers:[CALayer] = canvas.layer.sublayers!
    
        let orderedLayers = layers.sorted(by: {
            $0.zPosition < $1.zPosition
        })
    
        for v in orderedLayers {
            v.render(in: ct)
        }
        ct.endTransparencyLayer()
    }
    

    【讨论】:

    • 这似乎没有将子层渲染到正确的位置或正确的旋转。是否必须添加一些东西来确保子层被渲染在正确的位置?
    【解决方案3】:

    我遇到了同样的问题,真是太可惜了。

    我知道这个问题是在大约 2 年前提出的,所以如果它对任何人有帮助的话,这就是答案

    因此,更改子视图zPosition 在应用程序中渲染得非常好,但实际上在用于创建屏幕截图时会影响renderByContext。我不知道为什么,我认为这是一个错误。

    因此,为了让屏幕截图在应用程序中正确呈现,我们需要按照 zPosition 指定的正确顺序在所有子视图上手动调用 renderByContext

        UIGraphicsBeginImageContext(self.view.frame.size);
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        [self renderInContext:ctx];
    
        UIImage *screenShotImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    

    -(void) renderInContext:(CGContextRef)ctx {
        CGContextBeginTransparencyLayer(ctx, NULL);
        //self.layers is a desc ordered list of views based on its zPosition
        for(UIView *view in self.layers){ 
            [view.layer renderInContext:ctx];
        }
        CGContextEndTransparencyLayer(ctx);
    }
    

    【讨论】:

      【解决方案4】:

      我也有这个问题。使用 renderInContext() 后,我希望文本在图像上绘制。我通过在将视图添加到窗口时定位视图而不是使用图层来设置 z 位置来解决此问题。

      我曾经有一个文本字段可以将其 z 位置向上设置:

      layer?.zPosition = 1
      

      我删除了这段代码,而是在将图像添加到窗口时使用:

      addSubview(image, positioned: .Below, relativeTo: nil)
      

      这解决了问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-10
        • 2022-01-23
        • 1970-01-01
        • 2012-01-08
        • 2017-04-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多