I have a mapView, which has 34 CAShapeLayer to render each province, and another 34 CATextLayer to render the name for each province. I will calculate its center for each province.
Now I add this mapView into a UIScrollView, and when I zoom the mapView, I want to re-draw the CATextLayer to use another font Size. So after zoom, I will manually remove all CATextLayer and redraw them, like below.
However, I found that, after the for loop finished, there is still some CATextLayer in the sublayers, to be exact, everytime I test it, there are 18 CATextLayer not being removed. I have never met this issue, do I miss something? Please help, thanks.
-(void)drawLabelsWithFontSize:(CGFloat)fontSize {
int i = 0;
NSUInteger count = [self.mapView.layer.sublayers count];
for (i = 0; i < count; i++) {
CALayer *layer = self.mapView.layer.sublayers[i];
if ([layer isKindOfClass:[CATextLayer class]]) {
[layer removeFromSuperlayer];
// NSLog(@"%@, %lu",[layer class],i);
} else {
// NSLog(@"%@",[layer class]);
}
}
// at here, some CATextLayer still in self.mapView.layer.sublayers
__block typeof(self) weakSelf = self;
NSDictionary *labelNameAndLocation = [self getLabelNameAndLocationInfo];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.alignment = NSTextAlignmentCenter;
[labelNameAndLocation enumerateKeysAndObjectsWithOptions:NSEnumerationReverse usingBlock:^(id key, id obj, BOOL *stop) {
if ([(NSString *)key length] > 0) {
NSDictionary *location = (NSDictionary *)obj;
CATextLayer *label = [CATextLayer layer];
CGPoint caculateCenter = CGPointMake([weakSelf longitudeToCoordinate:[location[@"lng"] doubleValue]],[weakSelf latitudeToCoordinate:[location[@"lat"] doubleValue]]);
NSMutableAttributedString *text = [[NSMutableAttributedString alloc]
initWithString:key
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize],
NSParagraphStyleAttributeName:paragraphStyle,
NSForegroundColorAttributeName:[UIColor blackColor]}];
CGSize size = [text size];
[label setBounds:CGRectMake(0, 0, size.width, size.height)];
[label setString:text];
label.position = caculateCenter;
label.contentsScale = [[UIScreen mainScreen] scale];
[weakSelf.mapView.layer addSublayer:label];
}
}];
}
You're making the classic mistake of iterating up during removal:
for (i = 0; i < count; i++) {
CALayer *layer = self.mapView.layer.sublayers[i];
if ([layer isKindOfClass:[CATextLayer class]]) {
[layer removeFromSuperlayer];
// NSLog(@"%@, %lu",[layer class],i);
} else {
// NSLog(@"%@",[layer class]);
}
}
Change that for
line to iterate down:
for (i = count-1; i >= 0; i--) {
The reason is that if you start by removing, say, sublayer 0, all the other sublayers move down one index. Thus, if you remove sublayer 0 and sublayer 1 is also a text layer, that text layer is now sublayer 0 and will never be removed because you proceed on to sublayer 1. Thus, as you quite rightly demonstrate, you end up missing exactly half of them.
I'm a little surprised, in fact, that you haven't crashed because of this, but I guess there are so many sublayers that you just never fell off the end of the array; a crash is what usually happens.