2017-02-11 5 views
0

Je dois calculer le texte rect dans mon étiquette personnalisée sans utiliser la méthode sizeThatFits de UILabel. Le code ci-dessous ne fonctionne pas correctement. L'idée principale est de trouver CTLine à index = numberOfLines - 1 et de renvoyer sa position max y. Mais en conséquence, la hauteur du texte est parfois trop grande et parfois insuffisante pour dessiner la dernière ligne.NSAttributedString height limité par width et numberOfLines

- (CGSize)fittingSizeWithSize:(CGSize)size numberOfLines:(NSInteger)numberOfLines { 
    if (numberOfLines == 0) { 
     return [self fittingSizeWithSize:size]; 
    } 

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self); 

    if (framesetter == NULL) { 
     return CGSizeZero; 
    } 

    CGPathRef path = CGPathCreateWithRect(CGRectMake(0,0,size.width,size.height), NULL); 
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.length), path, NULL); 

    NSArray *lines = (NSArray *) CTFrameGetLines(frame); 
    if (lines.count == 0) { 
     return CGSizeZero; 
    } 
    NSUInteger lineIndex = MIN((NSUInteger)numberOfLines, lines.count) - 1; 
    CTLineRef line = (__bridge CTLineRef) lines[lineIndex]; 

    CGPoint origins[[lines count]]; 
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); 

    CGAffineTransform transform = CGAffineTransformMakeTranslation(0, size.height); 
    transform = CGAffineTransformScale(transform, 1, -1); 
    CGRect lineRect; 
    CGFloat ascent; 
    CGFloat descent; 
    lineRect.size.width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, NULL); //8 
    lineRect.size.height = ascent + descent; 
    lineRect.origin.y = CGPointApplyAffineTransform(origins[lineIndex], transform).y; 
    CGFloat height = CGRectGetMaxY(lineRect); 

    CFRelease(path); 
    CFRelease(framesetter); 

    return CGSizeMake(size.width, height); 
} 

Cette catégorie de NSAttributedString utilisé dans ma sous-classe UILabel

@implementation SMBDLabel 

- (void)drawTextInRect:(CGRect)rect { 
    if (self.attributedText) { 
     CGContextRef ctx = UIGraphicsGetCurrentContext(); 
     [self.attributedText drawInContext:ctx viewBounds:rect]; 
    } else { 
     [super drawTextInRect:rect]; 
    } 
} 

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { 
    CGSize size = [self.attributedText fittingSizeWithSize:bounds.size numberOfLines:numberOfLines]; 
    return CGRectMake(0, 0, size.width, size.height); 
} 

- (CGSize)sizeThatFits:(CGSize)size { 
    return [self.attributedText fittingSizeWithSize:size numberOfLines:self.numberOfLines]; 
} 

@end 

Je ne sais pas où mon erreur. Peut-être même erreur dans la sous-classe UILabel

+1

Pourquoi utiliser un texte de base, lorsque NSAttributedString a déjà 'boundingRectWithSize: Options: contexte:'? – matt

+0

Aussi bien que je ne peux pas utiliser NSTextAttachment (il y a un bug avec le retour à la ligne et aucune solution de contournement utilisable), je dessine mes pièces jointes d'image personnalisée. Il y a aussi un problème avec le remplacement de UILabel par des sous-classes de UIView. – Arsynth

+0

Je ne suis pas de tout ça. Vous avez Text Kit maintenant. Vous ne devriez pas avoir besoin de Core Text pour quoi que ce soit. Et UITextView vous permet d'utiliser directement Text Kit, en plus il peut simuler le comportement de UILabel. – matt

Répondre

0

La solution semble être la plus simple. Il n'est pas nécessaire d'obtenir les origines des lignes et les limites typographiques. CTFramesetterSuggestFrameSizeWithConstraints avec le texte plage de texte spécifique fera tous les travaux

- (CGSize)fittingSizeWithSize:(CGSize)size numberOfLines:(NSInteger)numberOfLines { 
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self); 

    if (!framesetter) { 
     return CGSizeZero; 
    } 

    if (numberOfLines == 0) { 
     CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0,0), NULL, size, NULL); 

     if (framesetter != NULL) { 
      CFRelease(framesetter); 
     } 

     return CGSizeMake(ceilf(textSize.width), ceilf(textSize.height)); 
    } else { 
     CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, size.width, CGFLOAT_MAX), NULL); 
     CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.length), path, NULL); 
     if (path != NULL) { 
      CFRelease(path); 
     } 


     NSArray *lines = (NSArray *)CTFrameGetLines(frame); 
     __block CFIndex len = 0; 

     [lines enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 
      if (numberOfLines > 0 && idx == numberOfLines) { 
       *stop = YES; 
       return; 
      } 

      CTLineRef line = (__bridge CTLineRef)obj; 
      CFRange range = CTLineGetStringRange(line); 

      len += range.length; 
     }]; 

     CFRange strRange = CFRangeMake(0, len); 
     CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, strRange, NULL, size, NULL); 

     if (framesetter != NULL) { 
      CFRelease(framesetter); 
     } 

     return CGSizeMake(ceilf(textSize.width), ceilf(textSize.height)); 
    } 
}