I read this and this but they don't seem to address the issue. And the answers to this seem to go off a tangent in NSCoding
and friends, so ...
Consider:
- (NSAttributedString *) attributedStringForText: (NSString *) text {
NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString:text];
UIFont * bigF = [UIFont fontWithName:@"OpenSans-Extrabold" size:20.0f] ;
UIFont * smaF = [UIFont fontWithName:@"OpenSans-Extrabold" size:12.0f] ;
[attrStr addAttribute:NSFontAttributeName value:bigF range:(NSRange){0, 3}] ;
[attrStr addAttribute:NSFontAttributeName value:smaF range:(NSRange){3, 6}] ;
[attrStr addAttribute:NSFontAttributeName value:bigF range:(NSRange){6, [text length]-6}] ;
[attrStr addAttribute:NSBackgroundColorAttributeName value:[UIColor brownColor] range:(NSRange){3, 6}] ;
return attrStr ;
}
- (CALayer *) stealLayerFromUILabelForText: (NSString *) text inRect: (CGRect) bounds {
UILabel * label = [[UILabel alloc] initWithFrame:bounds] ;
label.textColor = [UIColor whiteColor] ;
label.font = [UIFont fontWithName:@"OpenSans-Extrabold" size:20.0f] ;
label.attributedText = [self attributedStringForText:text] ;
[label sizeToFit] ;
[label.layer display] ; // Yup!
return label.layer ;
}
and
- (void) setupLayer: (CALayer *) tileLayer
text: (NSString *) text
index: (NSUInteger) index {
CGRect (^center_rect)(CGRect, CGRect) = ^CGRect (CGRect r, CGRect into) {
CGRect centered = into ;
centered.origin.x += (into.size.width - r.size.width) / 2.0f ;
centered.origin.y += (into.size.height - r.size.height) / 2.0f ;
centered.size = r.size ;
return centered ;
} ;
CALayer * layer = [self stealLayerFromUILabelForText:text inRect:tileLayer.bounds] ;
CGRect frame = center_rect(layer.bounds, tileLayer.bounds) ;
[tileLayer addSublayer:layer] ;
layer.frame = frame ;
}
This works:

But I am concerned that I am somehow abusing UILabel & CALayer. On one hand UIView
, UILabel
and all are just a thin touch aware veneer on top of the real CALayer
hierarchy. OTOH I am somehow relying on probably many not even stated assumptions here.
Anyone sees why this would break further down the road, or, better, can prove that it won't?
I've used this technique since 2013 and it's still stable. So yes, I think this is safe for now. Previously I was doing this without calling -display on iOS 4 or 5, which no longer works when compiled against newer SDKs, so thanks for this tidbit.
There is more information here if you're looking for other options: Add text to CALayer
This technique still works in iOS 10. IMO a UIView(in this case UILabel) is simply a delegate of its backing CALayer, so it should be safe to manipulate the underlying CALayer directly. The only problem is if you don't add the label as a subview, the label would be released and the underlying layer.delegate would become nil and the text never gets a chance to be rendered.
So we need a way to force rendering before the label is released. However it is worth mentioning that [CALayer display]
should NOT be called directly per Apple's documentation. So based on the same idea, consider a safe alternative [CALayer displayIfNeeded]
.
- (CALayer *) stealLayerFromUILabelForText: (NSString *) text inRect: (CGRect) bounds {
UILabel * label = [[UILabel alloc] initWithFrame:bounds] ;
label.textColor = [UIColor whiteColor] ;
label.font = [UIFont fontWithName:@"OpenSans-Extrabold" size:20.0f] ;
label.attributedText = [self attributedStringForText:text] ;
[label sizeToFit] ;
[label.layer displayIfNeeded] ;
return label.layer ;
}
来源:https://stackoverflow.com/questions/23171790/is-it-safe-to-add-a-stolen-uilabels-layer-to-some-other-calayers-sublayer