问题
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?
回答1:
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
回答2:
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