複数行の文字列である可能性があるテキストに下線を付けるにはどうすればよいですか? UIWebViewを提案する人もいますが、明らかにテキストレンダリングには重すぎるクラスです。
私の考えは、各行の各文字列の開始点と長さを把握することでした。そして、それに応じて線を引きます。
私は、文字列の長さと開始点を把握する方法で問題に出会います。
-[UILabel textRectForBounds:limitedToNumberOfLines:]
を使用しようとしましたが、これはテキストの描画境界四角形である必要がありますか?その後、私はアライメントに取り組む必要がありますか?中央揃えおよび右揃えの場合、どのようにして各行の開始点を取得できますか?
UILabelからサブクラス化し、drawRectメソッドをオーバーライドできます。
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 207.0f/255.0f, 91.0f/255.0f, 44.0f/255.0f, 1.0f); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, self.bounds.size.width, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
UPD:
iOS 6 Appleの時点で、UILabelのNSAttributedStringサポートが追加されたため、はるかに簡単になり、複数行で機能します。
NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
myLabel.attributedText = [[NSAttributedString alloc] initWithString:@"Test string"
attributes:underlineAttribute];
それでもiOS 4およびiOS 5をサポートしたい場合は、ラベルに手動で下線を付けるのではなく、 TTTAttributedLabel を使用することをお勧めします。ただし、1行のUILabelに下線を引く必要があり、サードパーティのコンポーネントを使用したくない場合は、上記のコードで問題を解決できます。
これは私がやったことです。バターのように機能します。
1)CoreText.frameworkをフレームワークに追加します。
2)下線付きラベルが必要なクラスに<CoreText/CoreText.h>をインポートします。
3)次のコードを記述します。
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"My Messages"];
[attString addAttribute:(NSString*)kCTUnderlineStyleAttributeName
value:[NSNumber numberWithInt:kCTUnderlineStyleSingle]
range:(NSRange){0,[attString length]}];
self.myMsgLBL.attributedText = attString;
self.myMsgLBL.textColor = [UIColor whiteColor];
Swiftの場合:
let underlineAttriString = NSAttributedString(string: "attriString",
attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString
属性文字列を使用します。
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:@"Your String"]
[attrString addAttribute:(NSString*)kCTUnderlineStyleAttributeName
value:[NSNumber numberWithInt:kCTUnderlineStyleSingle]
range:(NSRange){0,[attrString length]}];
そして、ラベル void)drawTextInRect:(CGRect)aRectをオーバーライドし、次のようなテキストをレンダリングします。
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
drawingRect = self.bounds;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, drawingRect);
textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
CGPathRelease(path);
CFRelease(framesetter);
CTFrameDraw(textFrame, ctx);
CGContextRestoreGState(ctx);
または、オーバーライドする代わりに、Olivier Halligonによって作成された---(OHAttributedLabel を使用してください
以下をサポートするUILabelサブクラスを作成するために、提供された回答のいくつかを結合しました(少なくとも私の要件では)。
ビュー(UILabel/UIButton)などをサブクラス化することを望まない人...「forgetButton」は、任意のラベルに置き換えることもできます。
-(void) drawUnderlinedLabel {
NSString *string = [forgetButton titleForState:UIControlStateNormal];
CGSize stringSize = [string sizeWithFont:forgetButton.titleLabel.font];
CGRect buttonFrame = forgetButton.frame;
CGRect labelFrame = CGRectMake(buttonFrame.Origin.x + buttonFrame.size.width - stringSize.width,
buttonFrame.Origin.y + stringSize.height + 1 ,
stringSize.width, 2);
UILabel *lineLabel = [[UILabel alloc] initWithFrame:labelFrame];
lineLabel.backgroundColor = [UIColor blackColor];
//[forgetButton addSubview:lineLabel];
[self.view addSubview:lineLabel];
}
NSString *tem =self.detailCustomerCRMCaseLabel.text;
if (tem != nil && ![tem isEqualToString:@""]) {
NSMutableAttributedString *temString=[[NSMutableAttributedString alloc]initWithString:tem];
[temString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){0,[temString length]}];
self.detailCustomerCRMCaseLabel.attributedText = temString;
}
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;
別の解決策は(iOS 7以降)NSBaselineOffsetAttributeName
に負の値を指定することです。たとえば、NSAttributedString
は次のようになります。
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:@"my text goes here'
attributes:@{NSFontAttributeName: [UIFont fontWithName:@"Helvetica-Regular" size:12],
NSForegroundColorAttributeName: [UIColor blackColor],
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSBaselineOffsetAttributeName: @(-3)}];
これが役立つことを願っています;-)
追加のコードを書かずに私のために働く最も簡単な解決策は次のとおりです。
// To underline text in UILable
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Type your text here"];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
lblText.attributedText = text;
UnderlinedLabelという名前のカスタムラベルを作成し、drawRect関数を編集できます。
#import "UnderlinedLabel.h"
@implementation UnderlinedLabel
- (void)drawRect:(CGRect)rect
{
NSString *normalTex = self.text;
NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
self.attributedText = [[NSAttributedString alloc] initWithString:normalTex
attributes:underlineAttribute];
[super drawRect:rect];
}
Kovpasのコードの拡張バージョン(色と行サイズ)
@implementation UILabelUnderlined
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);
CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, tmpSize.width, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
@end
私は下線付きの複数行のUIラベルを作成しました:
フォントサイズ8〜13の場合、int lineHeight = self.font.pointSize + 3;を設定します。
フォントサイズが14〜20の場合、int lineHeight = self.font.pointSize + 4;を設定します。
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);
CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(self.frame.size.width, 9999)];
int height = tmpSize.height;
int lineHeight = self.font.pointSize+4;
int maxCount = height/lineHeight;
float totalWidth = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(1000, 9999)].width;
for(int i=1;i<=maxCount;i++)
{
float width=0.0;
if((i*self.frame.size.width-totalWidth)<=0)
width = self.frame.size.width;
else
width = self.frame.size.width - (i* self.frame.size.width - totalWidth);
CGContextMoveToPoint(ctx, 0, lineHeight*i-1);
CGContextAddLineToPoint(ctx, width, lineHeight*i-1);
}
CGContextStrokePath(ctx);
[super drawRect:rect];
}
NSNumber(0は下線なし)をとるNSUnderlineStyleAttributeNameを属性辞書に追加できます。これが簡単かどうかはわかりません。しかし、私の目的にとっては簡単でした。
NSDictionary *attributes;
attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: style, NSUnderlineStyleAttributeName:[NSNumber numberWithInteger:1]};
[text drawInRect:CGRectMake(self.contentRect.Origin.x, currentY, maximumSize.width, textRect.size.height) withAttributes:attributes];
ここに、別のより簡単な解決策があります(下線の幅は最も正確ではありませんが、私にとっては十分でした)
UIView (_view_underline)
があり、その背景は白で、高さは1ピクセルで、テキストを更新するたびに幅を更新します
// It's a shame you have to do custom stuff to underline text
- (void) underline {
float width = [[_txt_title text] length] * 10.0f;
CGRect prev_frame = [_view_underline frame];
prev_frame.size.width = width;
[_view_underline setFrame:prev_frame];
}
Kovpas&Damien Pracaの回答に基づいて、ここにtextAlignemntもサポートするUILabelUnderlignedの実装があります。
#import <UIKit/UIKit.h>
@interface UILabelUnderlined : UILabel
@end
および実装:
#import "UILabelUnderlined.h"
@implementation DKUILabel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);
CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA
CGContextSetLineWidth(ctx, 1.0f);
CGSize textSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];
// handle textAlignement
int alignementXOffset = 0;
switch (self.textAlignment) {
case UITextAlignmentLeft:
break;
case UITextAlignmentCenter:
alignementXOffset = (self.frame.size.width - textSize.width)/2;
break;
case UITextAlignmentRight:
alignementXOffset = self.frame.size.width - textSize.width;
break;
}
CGContextMoveToPoint(ctx, alignementXOffset, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, alignementXOffset+textSize.width, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
@end
Swift 4.1 ver:
let underlineAttriString = NSAttributedString(string:"attriString", attributes:
[NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue])
label.attributedText = underlineAttriString
Kovpasが示したように、ほとんどの場合、境界ボックスを使用できますが、境界ボックスがテキストの周りにきちんと収まることは常に保証されません。 UILabelの構成によっては、高さが50でフォントサイズが12のボックスでは、希望する結果が得られない場合があります。
UILabel内のUIStringを照会して正確なメトリックを決定し、これらを使用して、kovpasによって既に提供されている描画コードを使用して、境界ボックスまたはフレームに関係なく下線を配置します。
また、特定のフォントに基づいてベースライン間の距離を与えるUIFontの「主要な」プロパティを確認する必要があります。ベースラインは、下線を引く場所です。
NSStringに追加されたUIKitを検索します。
(CGSize)sizeWithFont:(UIFont *)font
//Returns the size of the string if it were to be rendered with the specified font on a single line.
(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
// Returns the size of the string if it were rendered and constrained to the specified size.
(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
//Returns the size of the string if it were rendered with the specified constraints.
私はオープンソースのラインビューを使用し、ボタンサブビューに追加しました:
UILabel *label = termsButton.titleLabel;
CGRect frame = label.frame;
frame.Origin.y += frame.size.height - 1;
frame.size.height = 1;
SSLineView *line = [[SSLineView alloc] initWithFrame:frame];
line.lineColor = [UIColor lightGrayColor];
[termsButton addSubview:line];
これは上記のカリムに触発されました。