即使使用 UIViewContentModeScaleAspectFill 也会调用自定义 UIView drawRect

Posted

技术标签:

【中文标题】即使使用 UIViewContentModeScaleAspectFill 也会调用自定义 UIView drawRect【英文标题】:Custom UIView drawRect is called even with UIViewContentModeScaleAspectFill 【发布时间】:2014-09-25 13:15:54 【问题描述】:

我遇到了以下问题。

假设我有自己的图像视图类(为方便起见,我修剪了所有代码,只包含导致问题的代码)。该类继承自UIView 并覆盖drawRect。这是我的drawRect 的外观(代码在 Xamarin 中,但很容易理解)

public override void Draw(RectangleF rect)
    
        base.Draw(rect);
        CGContext context = UIGraphics.GetCurrentContext();
        if (Image != null)
        
            var imageRect = getAspectFillRect(rect, Image.Size);
            context.DrawImage(imageRect, Image.CGImage);
        
        else
        
            this.BackgroundColor.SetFill();
            context.FillRect(this.Bounds);
        
    

这段代码简单地绘制设置为Image属性的图像,通过计算矩形来模拟UIImageView的方面填充选项,该矩形将使用这行代码getAspectFillRect(rect, Image.Size);以方面填充模式显示图像

我也有 UICollectionView 自定义布局和自定义单元格。每个UICollectionViewCell UI 都在一个xib 文件中定义,在那里我设置了所有必要的约束,并且我还拥有用于显示图像的自定义视图。对于界面生成器中的该视图,我已将内容模式设置为“Aspect Fill”。自定义UICollectionViewLayout 以单元格扩展的方式制作(您将很快看到该示例)。所以单元格内的图像也应该缩放,并且因为我设置了“Aspect Fill”选项,它不应该调用我的自定义视图的drawRect,而应该只是缩放已经绘制的内容。但事实并非如此!

LOOK HERE 观看演示所发生情况的视频。 您可能会看到里面的图像没有与细胞一起生长。问题是,在我的自定义UIView 的单元格扩展动画开始drawRect 之前,使用最终将在动画之后建立的矩形进行调用。所以我得到了一个生涩的动画。在我的自定义布局代码中,我只是在更新单元格的约束后调用LayoutIfNeeded。我不打电话给setNeedsDisplay,所以我不明白为什么我的drawRect 会被调用。

为了实验,我用UIImageView 替换了我的自定义图像视图,我将它的内容模式设置为“Aspect Fill”,LOOK HERE 发生了什么。是的!有效。所以我想问题不在于自定义UICollectionView 布局,而在于我的自定义图像绘制类。

我应该考虑什么?我应该如何处理这种情况?有任何想法吗? 谢谢!

【问题讨论】:

【参考方案1】:

DrawRect 将在系统“感觉”它应该调用它时被调用。不管你是否打电话给SetNeedsDisplay

现在,将ContentMode 设置为ScaleAspectFill 并不意味着DrawRect不会 被调用。事实上,来自 Apple 文档here:

不用每次都重绘视图的内容,你可以使用 这个属性来指定你想要缩放的内容(或者 有或没有失真)或将它们固定在 查看。

这意味着,如果您不想自己绘制内容的麻烦,只需设置ContentMode 属性。在您的示例中,您正在使用DrawRect 进行绘图。

现在,DrawRect 在动画开始之前被调用,但目标值为Bounds(或Frame?)意味着这些属性的目标值为在更改完成之前设置。在动画期间,这些属性不会改变。注意期间Bounds' 或 Frame 的值不会随着正在进行的转换的所有值而改变。它只知道“开始”和“结束”。

如果DrawRect 在动画开始前没有被调用,但在动画结束后被调用,你的应用会发生什么?好吧,单元格将随其子视图一起调整大小,但您的图像将在整个动画过程中保持较小,并在动画完成后捕捉到大尺寸。所以这是一种或另一种方式。

解决方案:

使用 UIImageView。 如果您确实需要使用自定义视图,请在其上使用 UIImageView 来显示您的图像。 在自定义 CALayer 上绘制图像并将该图层添加到自定义视图的图层上。

【讨论】:

你好,迪米特里斯。感谢你的回答。我也在考虑绘制所有内容并将其设置为 UIImageView 的图像。但对我来说,这不是最好的解决方案。在我的情况下,使用 UIImageView 不是解决方案。我认为第三个解决方案与我的相同,不是吗?我认为它会再次像我的生涩动画一样工作。我认为如果 UIImageView 正在工作,那么应该有一种方法可以构建一个在当前情况下可以像 UIImageView 一样工作的类。你怎么看? 不,选项 #3 将起作用。在层上做这件事实际上是 UIImageView 正在做的事情。无需创建自己的图像视图。但是,它需要一些工作来根据 UICollectionView 的动画进行相应的调整。从一个简单的例子开始:在 CALayer 上绘制一个图像并使用 CABasicAnimation 调整它的大小,看看会发生什么。请记住,您必须考虑“开始”和“结束”值。

以上是关于即使使用 UIViewContentModeScaleAspectFill 也会调用自定义 UIView drawRect的主要内容,如果未能解决你的问题,请参考以下文章

为啥即使使用提交后 sql plus 也不会保存新行?

即使我在 laravel 中使用 updateorcreate 方法,为啥还要复制数据

为啥即使使用 QThreadPool,GUI 也会冻结?

为啥 isConfiguration:compatibleWithStoreMetadata 会返回 yes,即使使用了映射模型?

为啥即使使用 Colab Pro 帐户也无法连接到 GPU 后端?

即使使用示例代码,mmenu 也无法正常工作