UIImage drawInRect: bug in IOS 13 beta 4?

Hi,


I have an App showing maps using the RouteMe Library from 2013.


With the iOS Beta 13 something went wrong and from a certain zoom level onward nothing is drawn.


I debug the issue and I find that, after having created/loaded in some way an UIImage containing a pice of map it is loaded in the

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context in a view which layer is a CATiledLayer


Inside this method the image is load using the following code:


CGRect rect = CGContextGetClipBoundingBox(context);

UIGraphicsPushContext(context);

[tileImage drawInRect:rect];


This piece of code always worked in previous IOS version and it is working until the rect.size.width and .height >= 0.5, as soon it becomes 0.25 or less it does nothing (completely gray screen).


I found that if I use the following code everything works fine (except image is upside-down but this can be easily solved):


CGContextDrawImage(context, rect, tileImage.CGImage);


Does anyone knows if this is a bug in the IOS 13 Beta and so it will be solved or do I have to change my code using the CGContextDrawImage ?

I have exactly the same issue with MapBox libraries, which are probably based off RouteMe. Did you get a solution?

Andrew

Here's how I solved the problem:


Replace:


[tileImage drawInRect: rect];


With this:


UIImage *image = [self horizontalFlipRotate: tileImage];

CGContextDrawImage(context, rect, image.CGImage);



- (UIImage*) horizontalFlipRotate: (UIImage *) image {

UIGraphicsBeginImageContext(image.size);

CGContextRef current_context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(current_context, image.size.width, 0);

CGContextScaleCTM(current_context, -1.0, 1.0);


CGContextTranslateCTM( current_context, 0.5f * image.size.width, 0.5f * image.size.height ) ;

CGContextRotateCTM( current_context, DegreesToRadians( 180 ) ) ;

CGContextTranslateCTM( current_context, -0.5f * image.size.width, -0.5f * image.size.height ) ;


[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];


UIImage *flipped_img = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();


return flipped_img;

}


static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}

I've solved this by swizzling in a fix with a category on UIImage, which I apply only if we are running iOS 13.


#import <objc/runtime.h>


/**
 * Fix UIImage -drawRect: bug in iOS 13 that impacts RouteMe library.
 *
 * SEE: https://forums.developer.apple.com/thread/120526
 */
@implementation UIImage (FixIOS13Bug)

+ (void)load
{
    // https://nshipster.com/method-swizzling/
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        if (@available(iOS 13.0, *)) {
            NSLog(@"Applying iOS 13 fix for UIImage -drawRect:");
            Class clazz = [self class];
            
            SEL originalSelector = @selector(drawInRect:);
            SEL swizzledSelector = @selector(s6_drawInRect:);
            
            Method originalMethod = class_getInstanceMethod(clazz, originalSelector);
            Method swizzledMethod = class_getInstanceMethod(clazz, swizzledSelector);
            
            BOOL methodAdded = class_addMethod(clazz,
                                               originalSelector,
                                               method_getImplementation(swizzledMethod),
                                               method_getTypeEncoding(swizzledMethod));
            
            if (methodAdded) {
                class_replaceMethod(clazz,
                                    swizzledSelector,
                                    method_getImplementation(originalMethod),
                                    method_getTypeEncoding(originalMethod));
            }
            else {
                method_exchangeImplementations(originalMethod, swizzledMethod);
            }
        }
    });
}


- (void)s6_drawInRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
    UIImage* flipped = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGContextDrawImage(ctx, rect, flipped.CGImage);
}

@end

I want to add some details to this issue.


First, `[UIView drawLayer:inContext:]` before iOS 13 was running in `Queue: com.apple.root.default-qos (concurrent)` and on iOS 13 is running in `Queue: CA DispatchGroup (serial)`.


Second, advice from Apple regarding UI drawing and CG drawing inside `[UIView drawLayer:inContext:]`.

-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
 // Do all your drawing here. Do not use UIGraphics to do any drawing, use Core Graphics instead. 
}

https://developer.apple.com/library/content/qa/qa1637/_index.html

UIImage drawInRect: bug in IOS 13 beta 4?
 
 
Q