0

I'm having the following issue.

Suppose I have my own image view class (to make it easy I've trimmed all the code and just included only the code that is causing the issue). That class inherits from UIView and overrides drawRect. Here is how my drawRect looks (the code is in Xamarin, but it's easy to understand)

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);
        }
    }

This code simply draws the image set to Image property in a way which simulates UIImageView's aspect fill option by calculating the rect which will display the image in aspect fill mode using this line of code getAspectFillRect(rect, Image.Size);

I also have UICollectionView with custom layout and custom cells. Each UICollectionViewCell UI is defined in an xib file, there I have all the necessary constraints set and I there I also have my custom view for displaying images. For that view in interface builder I've set content mode to "Aspect Fill". The custom UICollectionViewLayout is made in a way that cell expands (you'll see the example shortly). So the image inside the cell should also scale, and as I've set "Aspect Fill" option, it shouldn't call drawRect of my custom view, rather it should just scale already drawn content. But that is not the case!

LOOK HERE to see the video which demonstrates what happens. You may see that the image inside is not growing together with the cell. The problem is that before ever the cell expanding animation begins drawRect of my custom UIView is called with the rect which will eventually be established after the animation. So I get a jerky animation. In my custom layout code I just call LayoutIfNeeded after updating the constraints of the cell. I don't call setNeedsDisplay, so I don't get why my drawRect is called.

For experiment I replaced my custom image view with UIImageView, I set it's content mode to "Aspect Fill", and LOOK HERE what happened. YEAH! It worked. So I suppose the issue is not in the custom UICollectionView layout, rather in my custom image drawing class.

What I should take into account?? How I should handle this case? Any Ideas? Thanks!

kyurkchyan
  • 2,260
  • 2
  • 23
  • 37

1 Answers1

0

DrawRect will be called whenever the system "feels" it should call it. Regardless if you call SetNeedsDisplay or not.

Now, setting ContentMode to ScaleAspectFill does not mean that DrawRect will not be called. In fact, from Apple docs here:

Instead of redrawing the contents of the view every time, you can use this property to specify that you want to scale the contents (either with or without distortion) or pin them to a particular spot on the view.

This means that, if you don't want to go through the hassle of drawing the contents yourself, just set the ContentMode property. In your example, you are using DrawRect to draw.

Now, the fact that DrawRect is called before the animation starts, but with the target value for Bounds (or Frame?) means that the target value of these properties is set before the change finishes. During an animation, these properties do not change. Note during. A Bounds' or Frame's value will not change through all the values of an ongoing transition. It only knows "start" and "end".

What would happen in your app if DrawRect was not called before the animation start, but was called after animation end? Well, the cell would resize along with its subview, but your image would remain small throughout the animation and snap to the large size after the animation finished. So it's one way or the other.

Solutions:

  • Use the UIImageView.
  • If you do need to use a custom view, use a UIImageView on that to display your image.
  • Draw your image on a custom CALayer and add that layer on your custom view's Layer.
Dimitris Tavlikos
  • 8,170
  • 1
  • 27
  • 31
  • Hello Dimitris. Thanks for the answer. I also was thinking to draw everything and set that as image of UIImageView. But for me it wasn't the best solution. Using UIImageView is not a solution in my case. The third solution I think is the same as mine, isn't it? I think it will again work as in my case with jerky animation. I thought that if UIImageView is working, then there should be a way to build a class that will work like UIImageView in this current case. what you think? – kyurkchyan Sep 26 '14 at 08:36
  • No, option #3 will work. Doing it on a layer is practically what UIImageView is doing. No need to create your own image view. However, it will need some work to adjust it accordingly with UICollectionView's animations. Start with a simple example: draw an image on a CALayer and resize it with CABasicAnimation and see what happens. Remember, you have to think with "start" and "end" values in mind. – Dimitris Tavlikos Sep 27 '14 at 14:31