Như mọi người khác đã đề cập, bạn không thể thực hiện việc này chỉ với NSAttributedString
. Nikolai có cách tiếp cận đúng, sử dụng CTFrameSetters
. Tuy nhiên, là có thể yêu cầu bộ khung hình hiển thị văn bản trong một khu vực cụ thể (nghĩa là được xác định bởi CGPath).
Bạn sẽ phải tạo 2 khung hình, một cho nắp thả và một cho phần còn lại của văn bản.
Sau đó, bạn lấy khung của nắp thả và tạo CGPathRef
chạy quanh không gian của khung của nắp thả.
Sau đó, bạn hiển thị cả hai khung hình vào chế độ xem của mình.
Tôi đã tạo một dự án mẫu với đối tượng có tên DropCapView là lớp con của UIView. Chế độ xem này hiển thị ký tự đầu tiên và bao bọc văn bản còn lại xung quanh nó.
Nó trông như thế này:
Có khá một vài bước, vì vậy tôi đã thêm một liên kết đến một dự án github lưu trữ ví dụ. Có ý kiến trong dự án sẽ giúp bạn cùng.
DropCap project on GitHub
Bạn sẽ phải chơi xung quanh với hình dạng của phần tử textBox
(ví dụ: các CGPathRef) cho đệm xung quanh các cạnh của quan điểm, và để thắt chặt nó lên đến bức thư thả nắp là tốt.
Dưới đây là can đảm của phương pháp vẽ:
- (void)drawRect:(CGRect)rect {
//make sure that all the variables exist and are non-nil
NSAssert(_text != nil, @"text is nil");
NSAssert(_textColor != nil, @"textColor is nil");
NSAssert(_fontName != nil, @"fontName is nil");
NSAssert(_dropCapFontSize > 0, @"dropCapFontSize is <= 0");
NSAssert(_textFontSize > 0, @"textFontSize is <=0");
//convert the text aligment from NSTextAligment to CTTextAlignment
CTTextAlignment ctTextAlignment = NSTextAlignmentToCTTextAlignment(_textAlignment);
//create a paragraph style
CTParagraphStyleSetting paragraphStyleSettings[] = { {
.spec = kCTParagraphStyleSpecifierAlignment,
.valueSize = sizeof ctTextAlignment,
.value = &ctTextAlignment
}
};
CFIndex settingCount = sizeof paragraphStyleSettings/sizeof *paragraphStyleSettings;
CTParagraphStyleRef style = CTParagraphStyleCreate(paragraphStyleSettings, settingCount);
//create two fonts, with the same name but differing font sizes
CTFontRef dropCapFontRef = CTFontCreateWithName((__bridge CFStringRef)_fontName, _dropCapFontSize, NULL);
CTFontRef textFontRef = CTFontCreateWithName((__bridge CFStringRef)_fontName, _textFontSize, NULL);
//create a dictionary of style elements for the drop cap letter
NSDictionary *dropCapDict = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)dropCapFontRef, kCTFontAttributeName,
_textColor.CGColor, kCTForegroundColorAttributeName,
style, kCTParagraphStyleAttributeName,
@(_dropCapKernValue) , kCTKernAttributeName,
nil];
//convert it to a CFDictionaryRef
CFDictionaryRef dropCapAttributes = (__bridge CFDictionaryRef)dropCapDict;
//create a dictionary of style elements for the main text body
NSDictionary *textDict = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)textFontRef, kCTFontAttributeName,
_textColor.CGColor, kCTForegroundColorAttributeName,
style, kCTParagraphStyleAttributeName,
nil];
//convert it to a CFDictionaryRef
CFDictionaryRef textAttributes = (__bridge CFDictionaryRef)textDict;
//clean up, because the dictionaries now have copies
CFRelease(dropCapFontRef);
CFRelease(textFontRef);
CFRelease(style);
//create an attributed string for the dropcap
CFAttributedStringRef dropCapString = CFAttributedStringCreate(kCFAllocatorDefault,
(__bridge CFStringRef)[_text substringToIndex:1],
dropCapAttributes);
//create an attributed string for the text body
CFAttributedStringRef textString = CFAttributedStringCreate(kCFAllocatorDefault,
(__bridge CFStringRef)[_text substringFromIndex:1],
textAttributes);
//create an frame setter for the dropcap
CTFramesetterRef dropCapSetter = CTFramesetterCreateWithAttributedString(dropCapString);
//create an frame setter for the dropcap
CTFramesetterRef textSetter = CTFramesetterCreateWithAttributedString(textString);
//clean up
CFRelease(dropCapString);
CFRelease(textString);
//get the size of the drop cap letter
CFRange range;
CGSize maxSizeConstraint = CGSizeMake(200.0f, 200.0f);
CGSize dropCapSize = CTFramesetterSuggestFrameSizeWithConstraints(dropCapSetter,
CFRangeMake(0, 1),
dropCapAttributes,
maxSizeConstraint,
&range);
//create the path that the main body of text will be drawn into
//i create the path based on the dropCapSize
//adjusting to tighten things up (e.g. the *0.8,done by eye)
//to get some padding around the edges of the screen
//you could go to +5 (x) and self.frame.size.width -5 (same for height)
CGMutablePathRef textBox = CGPathCreateMutable();
CGPathMoveToPoint(textBox, nil, dropCapSize.width, 0);
CGPathAddLineToPoint(textBox, nil, dropCapSize.width, dropCapSize.height * 0.8);
CGPathAddLineToPoint(textBox, nil, 0, dropCapSize.height * 0.8);
CGPathAddLineToPoint(textBox, nil, 0, self.frame.size.height);
CGPathAddLineToPoint(textBox, nil, self.frame.size.width, self.frame.size.height);
CGPathAddLineToPoint(textBox, nil, self.frame.size.width, 0);
CGPathCloseSubpath(textBox);
//create a transform which will flip the CGContext into the same orientation as the UIView
CGAffineTransform flipTransform = CGAffineTransformIdentity;
flipTransform = CGAffineTransformTranslate(flipTransform,
0,
self.bounds.size.height);
flipTransform = CGAffineTransformScale(flipTransform, 1, -1);
//invert the path for the text box
CGPathRef invertedTextBox = CGPathCreateCopyByTransformingPath(textBox,
&flipTransform);
CFRelease(textBox);
//create the CTFrame that will hold the main body of text
CTFrameRef textFrame = CTFramesetterCreateFrame(textSetter,
CFRangeMake(0, 0),
invertedTextBox,
NULL);
CFRelease(invertedTextBox);
CFRelease(textSetter);
//create the drop cap text box
//it is inverted already because we don't have to create an independent cgpathref (like above)
CGPathRef dropCapTextBox = CGPathCreateWithRect(CGRectMake(_dropCapKernValue/2.0f,
0,
dropCapSize.width,
dropCapSize.height),
&flipTransform);
CTFrameRef dropCapFrame = CTFramesetterCreateFrame(dropCapSetter,
CFRangeMake(0, 0),
dropCapTextBox,
NULL);
CFRelease(dropCapTextBox);
CFRelease(dropCapSetter);
//draw the frames into our graphic context
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
CGContextConcatCTM(gc, flipTransform);
CTFrameDraw(dropCapFrame, gc);
CTFrameDraw(textFrame, gc);
} CGContextRestoreGState(gc);
CFRelease(dropCapFrame);
CFRelease(textFrame);
}
T.B. điều này đi kèm với một số nguồn cảm hứng từ: https://stackoverflow.com/a/9272955/1218605
Bạn có thể đặt ảnh chụp màn hình về những gì bạn đã đạt được cho đến nay không? Và mã thử nghiệm? –
Tôi sử dụng Thả chuỗi giới hạn trong ứng dụng của mình, nhưng để thực hiện, tôi đã sử dụng UIWebView và HTML được sử dụng để thực hiện tác vụ đó. Tôi không chắc chắn nó có thể được thực hiện trong một UILabel – Youssef