1

我测试了一个使用自定义 UIFont 的应用程序。此字体用于可缩放的 UILabel - 它有一个 CATiledLayer 图层。

这是 UILabel 类的代码:

#import "ZoomableLabel.h"

@implementation ZoomableLabel

+ (Class)layerClass
{
    return [CATiledLayer class];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setupView];
    }
    return self;
}

//- (id)initWithCoder:(NSCoder *)aDecoder {
//    self = [super initWithCoder:aDecoder];
//    if (self) {
//        // Initialization code
//        [self setupView];
//    }
//    return self;
//}

-(void)awakeFromNib {
    [super awakeFromNib];
    [self setupView];
}

- (void)setupView {
    CATiledLayer *layerForView = (CATiledLayer *)self.layer;
    layerForView.levelsOfDetailBias = 3;
    layerForView.levelsOfDetail = 1;
}

-(void)setText:(NSString *)value {
    self.layer.contents = nil;
    [super setText:value];
    [self setNeedsDisplay];
}

-(void)setTextColor:(UIColor *)value {
    self.layer.contents = nil;
    [super setTextColor:value];
    [self setNeedsDisplay];
}

@end

当我第一次在设备或模拟器上运行应用程序(即应用程序第一次安装在设备或模拟器上)时,我遇到了崩溃。然后这种崩溃再也不会发生了!更新:崩溃非常随机发生(尤其是在演示应用程序时......),但不仅仅是第一次。这是我设法从 XCode 获得的所有信息:

Thread 3 name:  Dispatch queue: com.apple.root.default-priority
Thread 3 Crashed: 0   WebCore                           0x333adbfa WTF::HashTable<WebCore::FontData const*, std::__1::pair<WebCore::FontData const*, WebCore::GlyphPageTreeNode*>, WTF::PairFirstExtractor<std::__1::pair<WebCore::FontData const*, WebCore::GlyphPageTreeNode*> >, WTF::PtrHash<WebCore::FontData const*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::FontData const*>, WTF::HashTraits<WebCore::GlyphPageTreeNode*> >, WTF::HashTraits<WebCore::FontData const*> >::rehash(int) + 42
1   WebCore                         0x333adcd4 WTF::HashTableAddResult<WTF::HashTableIterator<WebCore::FontData const*, std::__1::pair<WebCore::FontData const*, WebCore::GlyphPageTreeNode*>, WTF::PairFirstExtractor<std::__1::pair<WebCore::FontData const*, WebCore::GlyphPageTreeNode*> >, WTF::PtrHash<WebCore::FontData const*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::FontData const*>, WTF::HashTraits<WebCore::GlyphPageTreeNode*> >, WTF::HashTraits<WebCore::FontData const*> > > WTF::HashTable<WebCore::FontData const*, std::__1::pair<WebCore::FontData const*, WebCore::GlyphPageTreeNode*>, WTF::PairFirstExtractor<std::__1::pair<WebCore::FontData const*, WebCore::GlyphPageTreeNode*> >, WTF::PtrHash<WebCore::FontData const*>, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::FontData const*>, WTF::HashTraits<WebCore::GlyphPageTreeNode*> >, WTF::HashTraits<WebCore::FontData const*> >::add<WTF::HashMapTranslator<WTF::HashMapValueTraits<WTF::HashTraits<WebCore::FontData const*>, WTF::HashTraits<WebCore::GlyphPageTreeNode*> >, WTF::PtrHash<WebCore::FontData const*> >, WebCore::FontData const*, WebCore::GlyphPageTreeNode*>(WebCore::FontData const* const&, WebCore::GlyphPageTreeNode* const&) + 56
2   WebCore                         0x333a5cac WebCore::GlyphPageTreeNode::getChild(WebCore::FontData const*, unsigned int) + 264
3   WebCore                         0x333a55d8 WebCore::Font::glyphDataAndPageForCharacter(int, bool, WebCore::FontDataVariant) const + 528
4   WebCore                         0x333a53b6 WebCore::Font::glyphDataForCharacter(int, bool, WebCore::FontDataVariant) const + 18
5   WebCore                         0x333a4b36 WebCore::WidthIterator::advance(int, WebCore::GlyphBuffer*) + 398
6   WebCore                         0x333a4794 WebCore::Font::floatWidthForSimpleText(WebCore::TextRun const&, WebCore::GlyphBuffer*, WTF::HashSet<WebCore::SimpleFontData const*, WTF::PtrHash<WebCore::SimpleFontData const*>, WTF::HashTraits<WebCore::SimpleFontData const*> >*, WebCore::GlyphOverflow*) const + 60
7   WebCore                         0x333a4546 WebCore::Font::width(WebCore::TextRun const&, WTF::HashSet<WebCore::SimpleFontData const*, WTF::PtrHash<WebCore::SimpleFontData const*>, WTF::HashTraits<WebCore::SimpleFontData const*> >*, WebCore::GlyphOverflow*) const + 250
8   WebCore                         0x333a60e0 WebCore::truncateString(WTF::String const&, float, WebCore::Font const&, unsigned int (*)(WTF::String const&, unsigned int, unsigned int, unsigned short*, bool), bool, float*, bool, float, bool) + 296
9   WebCore                         0x333a5fac WebCore::StringTruncator::rightTruncate(WTF::String const&, float, WebCore::Font const&, WebCore::StringTruncator::EnableRoundingHacksOrNot, float&, bool, float) + 60
10  WebKit                          0x375fc718 applyEllipsisStyle(WTF::String const&, WebEllipsisStyle, float, WebCore::Font const&, WebCore::StringTruncator::EnableRoundingHacksOrNot, float*, bool, float, bool) + 464
11  WebKit                          0x375ff3a8 -[NSString(WebStringDrawing) __web_drawInRect:withFont:ellipsis:alignment:letterSpacing:lineSpacing:includeEmoji:truncationRect:measureOnly:renderedStringOut:drawUnderline:] + 5036
12  WebKit                          0x375fdfe8 -[NSString(WebStringDrawing) __web_drawInRect:withFont:ellipsis:alignment:letterSpacing:lineSpacing:includeEmoji:truncationRect:measureOnly:renderedStringOut:] + 112
13  WebKit                          0x375fdf64 -[NSString(WebStringDrawing) __web_drawInRect:withFont:ellipsis:alignment:letterSpacing:lineSpacing:includeEmoji:truncationRect:measureOnly:] + 108
14  WebKit                          0x375fdee4 -[NSString(WebStringDrawing) _web_drawInRect:withFont:ellipsis:alignment:lineSpacing:includeEmoji:truncationRect:measureOnly:] + 108
15  WebKit                          0x375fde64 -[NSString(WebStringDrawing) _web_sizeInRect:withFont:ellipsis:lineSpacing:] + 80
16  UIKit                           0x353698c2 -[NSString(UIStringDrawing) sizeWithFont:constrainedToSize:lineBreakMode:lineSpacing:] + 122
17  UIKit                           0x3535daa6 -[UILabel _legacy_drawTextInRect:baselineCalculationOnly:] + 594
18  UIKit                           0x35321c5a -[UILabel _drawTextInRect:baselineCalculationOnly:] + 162
19  UIKit                           0x35320a26 -[UILabel drawTextInRect:] + 446
20  UIKit                           0x35320860 -[UILabel drawRect:] + 68
21  UIKit                           0x3531fd20 -[UIView(CALayerDelegate) drawLayer:inContext:] + 360
22  QuartzCore                      0x37b84bb8 -[CALayer drawInContext:] + 108
23  QuartzCore                      0x37c62624 tiled_layer_render(_CAImageProvider*, unsigned int, unsigned int, unsigned int, unsigned int, void*) + 1416
24  QuartzCore                      0x37bd755c CAImageProviderThread(unsigned int*, bool) + 508
25  libdispatch.dylib               0x37b5e95c _dispatch_root_queue_drain + 248
26  libdispatch.dylib               0x37b5eabc _dispatch_worker_thread2 + 80
27  libsystem_c.dylib               0x38862a0e _pthread_wqthread + 358
28  libsystem_c.dylib               0x388628a0 start_wqthread + 4

有人对此有任何想法吗?我认为这种崩溃不会发生在 iOS 5 设备上......

4

2 回答 2

2

这是我对 Summon 的回答的看法,它使用核心文本来解决英语以外的语言(尽管我只测试过瑞典语)和奇怪的字体符号的问题。请注意,它使用 initWithCoder,就像我使用 StoryBoard 一样。它还显示了如何将标签中的文本水平居中(垂直方向的运气不太好,我只需要测试我的方式到正确的位置)。

标题:

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface DNLabel : UILabel 

@end

执行:

#import "DNLabel.h"
#import <CoreText/CoreText.h>

@implementation DNLabel

+ (Class)layerClass
{
    return [CATiledLayer class];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self)
    {
        [self setupView];
    }
return self;
}

-(void)awakeFromNib 
{
    [super awakeFromNib];
    [self setupView];
}

- (void)setupView {
CATiledLayer *layerForView = (CATiledLayer *)self.layer;
layerForView.levelsOfDetailBias = 10;
layerForView.levelsOfDetail = 10;
}

// LEAVE IT EMPTY
-(void)drawRect:(CGRect)r
{
// UIView uses the existence of -drawRect: to determine if should allow its CALayer
// to be invalidated, which would then lead to the layer creating a backing store and
// -drawLayer:inContext: being called.
// By implementing an empty -drawRect: method, we allow UIKit to continue to implement
// this logic, while doing our real drawing work inside of -drawLayer:inContext:
}

// These calls inside this method are thread SAFE
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
    // Core Text version
    CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)self.font.fontName,
                                            self.font.pointSize,
                                            NULL);
    // set color
    CGColorRef color = [[[UIColor blackColor] colorWithAlphaComponent:0.75f] CGColor];

    // pack it into attributes dictionary
    NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
                                    (id)CFBridgingRelease(ctFont), (id)kCTFontAttributeName,
                                    color, (id)kCTForegroundColorAttributeName,
                                    nil, (id)kCTUnderlineStyleAttributeName, nil];

    // make the attributed string
    NSAttributedString *stringToDraw = [[NSAttributedString alloc] initWithString:self.text
                                                                   attributes:attributesDict];
    // flip the coordinate system
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // check size of text and set position centered horizontally
    CGSize size = [self.text sizeWithFont:self.font];
    CGContextSetTextPosition(context, self.bounds.size.width/2 - size.width/2, 1);

    // draw
    CTLineRef line = CTLineCreateWithAttributedString(
                                                    (CFAttributedStringRef)CFBridgingRetain(stringToDraw));
    CTLineDraw(line, context);
}

@end
于 2013-03-12T22:30:13.997 回答
1

这些是基于 CATiledLayer 且永不崩溃的可缩放标签的 .h 和 .m 类:

标题:

#import <QuartzCore/QuartzCore.h>

@interface ZoomableLabel : UILabel {
    CGPoint textDrawPoint;
    CGPoint shadowDrawPoint;
}

@end

执行:

#import "ZoomableLabel.h"

@implementation ZoomableLabel

+ (Class)layerClass
{
    return [CATiledLayer class];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setupView];
    }
    return self;
}

-(void)awakeFromNib {
    [super awakeFromNib];
    [self setupView];
}

- (void)setupView {
    CATiledLayer *layerForView = (CATiledLayer *)self.layer;
    layerForView.levelsOfDetailBias = 3;
    layerForView.levelsOfDetail = 1;
    textDrawPoint = CGPointMake(11, -22);
    shadowDrawPoint = CGPointMake(10, -23);
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        textDrawPoint = CGPointMake(11, -42);
        shadowDrawPoint = CGPointMake(8, -46);
    }
}

// DO NOT USER THESE METHODS ANYMORE
//-(void)setText:(NSString *)value {
////    self.layer.contents = nil;
//    [super setText:value];
//    [self setNeedsDisplayInRect:self.bounds];
//}
//
//-(void)setTextColor:(UIColor *)value {
////    self.layer.contents = nil;
//    [super setTextColor:value];
//    [self setNeedsDisplayInRect:self.bounds];
//}

// LEAVE IT EMPTY
-(void)drawRect:(CGRect)r
{
    // UIView uses the existence of -drawRect: to determine if should allow its CALayer
    // to be invalidated, which would then lead to the layer creating a backing store and
    // -drawLayer:inContext: being called.
    // By implementing an empty -drawRect: method, we allow UIKit to continue to implement
    // this logic, while doing our real drawing work inside of -drawLayer:inContext:
}

// These calls inside this method are thread SAFE
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
    // Do all your drawing here. Do not use UIGraphics to do any drawing, use Core Graphics instead.
    CGContextScaleCTM(context, 1.0f, -1.0f);
    CGContextSelectFont(context, [self.font.fontName UTF8String], self.font.pointSize, kCGEncodingMacRoman);
    CGContextSetTextDrawingMode(context, kCGTextFill);
//    CGContextSetShouldAntialias(context, true);
    CGContextSetFillColorWithColor(context, [[[UIColor blackColor] colorWithAlphaComponent:0.75f] CGColor]);
    CGContextShowTextAtPoint(context, shadowDrawPoint.x, shadowDrawPoint.y, [self.text UTF8String], self.text.length);
    CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
    CGContextShowTextAtPoint(context, textDrawPoint.x, textDrawPoint.y, [self.text UTF8String], self.text.length);
}

@end
于 2013-03-01T12:19:06.047 回答