Miroslav Kuťák
Miroslav Kuťák

Reputation: 1915

drawRect weird behaviour on faster devices

Ok, I've an array of dummy objects containing property cgpath. This is called darkPathArray property in my UIView.

When I draw them one after another I found an interesting thing to happen. I got EXC_BAD_ACCESS on CGContextAddPath(context,ppath.path); (ppath.path throws that) when running on iOS 5 sim/device. But When I put a NSLog call in front of CGContextAddPath, it works correctly. And this is, because it's significantly slown down.

So the problem is in the frequency of -drawRect callings. That's ok, I could fake it 'till I'd make it, but that's not the way professional works.

Therefore I'm asking why is this happening?

-(void)drawRect:(CGRect)r
{
    // Get the graphics context and clear it
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, self.bounds);

    [[UIColor whiteColor] set];
    UIRectFill ([self bounds]);

    // Draw every single section as a path
    for (ShopSection *section in self.shopSectionsArray) {
        // Draw the walls
        [section.darkColor setFill];

        for (MMPath *ppath in section.darkPathArray) {
            CGContextAddPath(context,ppath.path);
            CGContextDrawPath(context, kCGPathFill);
        }
    }
}

Edit: ARC enabled

MMPaths are not changing

EXC_BAD_ACCESS is responding to bad section object / dunno why (they're stored safely in the array)

Superview: UIScrollView Layer: CATiledLayer

The problem with bad access occurs when the content is rapidly redrawn - on fast device/simulator or when wildly scrolling and zooming.

Edit2: I think I got it! The problem is that CATiledLayer draws on backround threads, so it somehow catches it's tail. Have anyone an idea how to fix that? What is the best approach for that? What am I doing wrong?

Upvotes: 0

Views: 400

Answers (2)

Miroslav Kuťák
Miroslav Kuťák

Reputation: 1915

I finally changed my mind and used the CAShapeLayer instead of my MMPath (NSObject with CGPath property). Then using CGRectIntersectsRect, I've managed to get rid of that multithreading bug. It also mean an optimalization as only the appropriate shapes are drawn in each rect.

So that's how multiple shapes in CATiledLayer shall be drawn:

-(void)drawRect:(CGRect)rect
{
// Get the graphics context and clear it
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, self.bounds);

[[UIColor whiteColor] set];
UIRectFill ([self bounds]);

// Draw every single section as a path
for (ShopSection *section in self.shopSectionsArray) {

    CAShapeLayer *s = section.wallsShapeLayer;
    // Draw the walls
    if ( CGRectIntersectsRect(rect, s.frame ) )
    {
        // Rasterising before rendering enhances the performance a bit
        s.shouldRasterize = YES;
        [s renderInContext:context];
    }
}
}

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299275

This loop is incorrect:

for (MMPath *ppath in section.darkPathArray) {
    CGContextAddPath(context,ppath.path);
    CGContextDrawPath(context, kCGPathFill);
}

CGContextAddPath() adds to "the current path." CGContextDrawPath draws "the current path." So you keep extending the path, then redrawing everything. You mean this:

for (ShopSection *section in self.shopSectionsArray) {

    // Draw the walls
    [section.darkColor setFill];

    for (MMPath *ppath in section.darkPathArray) {
        CGContextAddPath(context,ppath.path);
    }
}
CGContextDrawPath(context, kCGPathFill);

Upvotes: 1

Related Questions