CTFramesetterSuggestFrameSizeWithConstraints con attributes de recorte que devuelven cero altura

¡MARIDO! Estoy escribiendo una vista de text con las funciones de recorte de subview. La idea es hacer que todo el text dibuje alnetworkingedor de todas las subvenciones. El problema es get su altura de contenido.

Debido a la falta de documentation, decidí que el dictionary de attributes para CTFramesetterSuggestFrameSizeWithConstraints es el mismo que para el CTFramesetterCreateFrame.

Aquí está mi código de routes de recorte:

-(CFDictionaryRef)clippingPathsDictionary{ if(self.subviews.count==0)return NULL; NSMutableArray *pathsArray = [[NSMutableArray alloc] init]; CGAffineTransform transform = CGAffineTransformIdentity; transform = CGAffineTransformScale(transform, 1, -1); transform = CGAffineTransformTranslate(transform, 0, -self.bounds.size.height); for (int i=0; i<self.subviews.count; i++) { UIView *clippingView = self.subviews[i]; CGPathRef clipPath = CGPathCreateWithRect(clippingView.frame, &transform); NSDictionary *clippingPathDictionary = [NSDictionary dictionaryWithObject:(__bridge id)(clipPath) forKey:(__bridge NSString *)kCTFramePathClippingPathAttributeName]; [pathsArray addObject:clippingPathDictionary]; CFRelease(clipPath); } int eFrameWidth=0; CFNumberRef frameWidth = CFNumberCreate(NULL, kCFNumberNSIntegerType, &eFrameWidth); int eFillRule = kCTFramePathFillEvenOdd; CFNumberRef fillRule = CFNumberCreate(NULL, kCFNumberNSIntegerType, &eFillRule); int eProgression = kCTFrameProgressionTopToBottom; CFNumberRef progression = CFNumberCreate(NULL, kCFNumberNSIntegerType, &eProgression); CFStringRef keys[] = { kCTFrameClippingPathsAttributeName, kCTFramePathFillRuleAttributeName, kCTFrameProgressionAttributeName, kCTFramePathWidthAttributeName}; CFTypeRef values[] = { (__bridge CFTypeRef)(pathsArray), fillRule, progression, frameWidth}; CFDictionaryRef clippingPathsDictionary = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); return clippingPathsDictionary;} 

Lo uso para dibujar text y funciona bien. Aquí está mi código de dibujo:

 - (void)drawRect:(CGRect)rect{ CGAffineTransform transform = CGAffineTransformIdentity; transform = CGAffineTransformScale(transform, 1, -1); transform = CGAffineTransformTranslate(transform, 0, -rect.size.height); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextConcatCTM(context, transform); CFAttributedStringRef attributedString = (__bridge CFAttributedStringRef)self.attributedString; CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedString); CFDictionaryRef attributesDictionary = [self clippingPathsDictionary]; CGPathRef path = CGPathCreateWithRect(rect, &transform); CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, self.attributedString.length), path, attributesDictionary); CFRelease(path); CFRelease(attributesDictionary); CTFrameDraw(frame, context); CFRelease(frameSetter); CFRelease(frame); CGSize contentSize = [self contentSizeForWidth:self.bounds.size.width]; CGPathRef highlightPath = CGPathCreateWithRect((CGRect){CGPointZero, contentSize}, &transform); CGContextSetFillColorWithColor(context, [UIColor colorWithRed:.0 green:1 blue:.0 alpha:.3].CGColor); CGContextAddPath(context, highlightPath); CGContextDrawPath(context, kCGPathFill); CFRelease(highlightPath); 

}

Ese es el resultado:

Resultado de dibujo ¡Eso es exactamente lo que esperaba!

Finalmente, aquí está el código para verificar la altura:

 -(CGSize)contentSizeForWidth:(float)width{ CFAttributedStringRef attributedString = (__bridge CFAttributedStringRef)self.attributedString; CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedString); CFDictionaryRef attributesDictionary = [self clippingPathsDictionary]; CGSize size = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0, self.attributedString.length), attributesDictionary, CGSizeMake(width, CGFLOAT_MAX), NULL); NSLog(@"%s: size = %@",__PRETTY_FUNCTION__, NSStringFromCGSize(size)); CFRelease(attributesDictionary); CFRelease(frameSetter); return size; 

}

La salida se ve así:

 Core Text Demo[2222:a0b] -[DATextView contentSizeForWidth:]: size = {729.71484, 0} 

¿Alguien tiene alguna solución? Gracias por la atención:)

Esto me parece un error de iOS7. He estado jugueteando, y bajo iOS6, CTFramesetterSuggestFrameSizeWithConstraints devuelve un tamaño con una altura mayor que 0. El mismo código en iOS7 devuelve una altura de 0.

CTFramesetterSuggestFrameSizeWithConstraints es conocido por su comportamiento cochecito e indocumentado. Por ejemplo, su código en iOS6 devuelve una altura incorrecta debido a que CTFramesetterSuggestFrameSizeWithConstraints realiza un cálculo incorrecto y la última línea se omite del cálculo. Aquí está su código que se ejecuta en iOS6:

introduzca la descripción de la imagen aquí

Debería abrir un informe de errores para estos problemas, así como solicitar mejoras de documentation, en: https://bugreport.apple.com

En iOS7, con TextKit, puedes lograr el mismo resultado mucho más rápido y elegante:

 @interface LeoView : UIView @property (nonatomic, assign) UIEdgeInsets contentInset; @property (nonatomic, retain) NSLayoutManager* layoutManager; @property (nonatomic, retain) NSTextContainer* textContainer; @property (nonatomic, retain) NSTextStorage* textStorage; @end @implementation LeoView -(void)awakeFromNib { //Leo: This should not be done here. Just for example. You should do this in the init. self.textStorage = [[NSTextStorage alloc] initWithString:@"Lorem ipsum dolor [...]"]; self.layoutManager = [[NSLayoutManager alloc] init]; [self.textStorage addLayoutManager:self.layoutManager]; self.textContainer = [[NSTextContainer alloc] initWithSize:self.bounds.size]; [self.layoutManager addTextContainer:self.textContainer]; } - (void)layoutSubviews { [super layoutSubviews]; //Leo: At this point the user may have set content inset, so need to take into account. CGSize size = self.bounds.size; size.width -= (self.contentInset.left + self.contentInset.right); size.height -= (self.contentInset.top + self.contentInset.bottom); self.textContainer.size = size; NSMutableArray* exclussionPaths = [NSMutableArray new]; for (UIView* subview in self.subviews) { if(subview.isHidden) continue; CGRect frame = subview.frame; //Leo: If there is content inset, need to compensate. frame.origin.y -= self.contentInset.top; frame.origin.x -= self.contentInset.left; [exclussionPaths addObject:[UIBezierPath bezierPathWithRect:frame]]; } self.textContainer.exclusionPaths = exclussionPaths; [self setNeedsDisplay]; } - (void)drawRect:(CGRect)rect { [self.layoutManager drawGlyphsForGlyphRange:[self.layoutManager glyphRangeForTextContainer:self.textContainer] atPoint:CGPointMake(self.contentInset.left, self.contentInset.top)]; //Leo: Move used rectangle according to content inset. CGRect usedRect = [self.layoutManager usedRectForTextContainer:self.textContainer]; usedRect.origin.x += self.contentInset.left; usedRect.origin.y += self.contentInset.top; CGAffineTransform transform = CGAffineTransformIdentity; transform = CGAffineTransformScale(transform, 1, -1); transform = CGAffineTransformTranslate(transform, 0, -rect.size.height); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextConcatCTM(context, transform); CGPathRef highlightPath = CGPathCreateWithRect(usedRect, &transform); CGContextSetFillColorWithColor(context, [UIColor colorWithRed:.0 green:1 blue:.0 alpha:.3].CGColor); CGContextAddPath(context, highlightPath); CGContextDrawPath(context, kCGPathFill); CFRelease(highlightPath); } @end 

Y aquí está el resultado con contentInset establecido en UIEdgeInsetsMake(20, 200, 0, 35) :

introduzca la descripción de la imagen aquí

Usé su código para dibujar el rectángulo verde alnetworkingedor del text.