IPhoneまたはiPadでアプリがポートレートモードまたはランドスケープモードで実行されているかどうかに応じて長さが異なるUILabel
があります。テキストが長すぎて1行に表示できない場合、ユーザーがそれを押して全文のポップアップを表示できるようにします。
UILabel
がテキストを切り捨てているかどうかを確認するにはどうすればよいですか?それも可能ですか?現在、私は自分がどのモードにいるかに基づいて異なる長さをチェックしていますが、うまく機能しません。
文字列の幅 を計算し、幅がlabel.bounds.size.width
より大きいかどうかを確認できます
NSString UIKit Additions には、特定のフォントで文字列のサイズを計算するためのいくつかのメソッドがあります。ただし、システムがテキストをそのサイズに縮小できるようにするラベルのminimumFontSizeがある場合。その場合、 sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: を使用できます。
CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
...
}
Swift(拡張機能として)-複数行のuilabelで動作します:
Swift4:(attributes
のboundingRect
paramはわずかに変更されました)
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [.font: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
Swift3:
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [NSFontAttributeName: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
Swift2:
extension UILabel {
func isTruncated() -> Bool {
if let string = self.text {
let size: CGSize = (string as NSString).boundingRectWithSize(
CGSize(width: self.frame.size.width, height: CGFloat(FLT_MAX)),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: self.font],
context: nil).size
if (size.height > self.bounds.size.height) {
return true
}
}
return false
}
}
編集:私はちょうど私の答えが支持されたのを見ましたが、私が与えたコードスニペットは非推奨です。
これを行う最良の方法は(ARC)です。
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = mylabel.lineBreakMode;
NSDictionary *attributes = @{NSFontAttributeName : mylabel.font,
NSParagraphStyleAttributeName : paragraph};
CGSize constrainedSize = CGSizeMake(mylabel.bounds.size.width, NSIntegerMax);
CGRect rect = [mylabel.text boundingRectWithSize:constrainedSize
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes context:nil];
if (rect.size.height > mylabel.bounds.size.height) {
NSLog(@"TOO MUCH");
}
計算されたサイズは整数値ではないことに注意してください。したがって、int height = rect.size.height
、一部の浮動小数点の精度が失われ、誤った結果になる可能性があります。
古い回答(非推奨):
ラベルが複数行の場合、次のコードを使用できます。
CGSize perfectSize = [mylabel.text sizeWithFont:mylabel.font constrainedToSize:CGSizeMake(mylabel.bounds.size.width, NSIntegerMax) lineBreakMode:mylabel.lineBreakMode];
if (perfectSize.height > mylabel.bounds.size.height) {
NSLog(@"TOO MUCH");
}
uILabelでカテゴリを作成できます
- (BOOL)isTextTruncated
{
CGRect testBounds = self.bounds;
testBounds.size.height = NSIntegerMax;
CGRect limitActual = [self textRectForBounds:[self bounds] limitedToNumberOfLines:self.numberOfLines];
CGRect limitTest = [self textRectForBounds:testBounds limitedToNumberOfLines:self.numberOfLines + 1];
return limitTest.size.height>limitActual.size.height;
}
このカテゴリを使用して、iOS 7以降でラベルが切り捨てられているかどうかを確認します。
// UILabel+Truncation.h
@interface UILabel (Truncation)
@property (nonatomic, readonly) BOOL isTruncated;
@end
// UILabel+Truncation.m
@implementation UILabel (Truncation)
- (BOOL)isTruncated
{
CGSize sizeOfText =
[self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:@{ NSFontAttributeName : label.font }
context: nil].size;
if (self.frame.size.height < ceilf(sizeOfText.height))
{
return YES;
}
return NO;
}
@end
iDev の答えに追加するには、intrinsicContentSize
の代わりにframe
を使用して、自動レイアウトで機能するようにする必要があります。
- (BOOL)isTruncated:(UILabel *)label{
CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.intrinsicContentSize.width, CGFLOAT_MAX)
options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;
if (self.intrinsicContentSize.height < ceilf(sizeOfText.height)) {
return YES;
}
return NO;
}
Swift
文字列を割り当てた後、行数をカウントし、ラベルの最大行数と比較できます。
import Foundation
import UIKit
extension UILabel {
func countLabelLines() -> Int {
// Call self.layoutIfNeeded() if your view is uses auto layout
let myText = self.text! as NSString
let attributes = [NSFontAttributeName : self.font]
let labelSize = myText.boundingRect(with: CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil)
return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight))
}
func isTruncated() -> Bool {
if (self.countLabelLines() > self.numberOfLines) {
return true
}
return false
}
}
これです。これはattributedText
で機能しますが、それからプレーンtext
にフォールバックします。これは、複数のフォントファミリ、サイズ、さらにはNSTextAttachmentsを扱う人々にとって非常に理にかなっています。
自動レイアウトでは問題なく動作しますが、明らかにisTruncated
をチェックする前に制約を定義および設定する必要があります。そうしないと、ラベル自体がレイアウト方法を認識できなくなるため、切り捨てられたかどうかさえわかりません。
単純なNSString
とsizeThatFits
だけでこの問題にアプローチすることはできません。人々がそのような肯定的な結果をどのように得ていたかはわかりません。ところで、何度も述べたように、sizeThatFits
を使用することは、結果のサイズに対してnumberOfLines
を考慮するため、まったく理想的ではありません。これは、isTruncated
は、切り捨てられているかどうかに関係なく、常にfalse
を返します。
extension UILabel {
var isTruncated: Bool {
layoutIfNeeded()
let rectBounds = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
var fullTextHeight: CGFloat?
if attributedText != nil {
fullTextHeight = attributedText?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, context: nil).size.height
} else {
fullTextHeight = text?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size.height
}
return (fullTextHeight ?? 0) > bounds.size.height
}
}
UILabelの切り捨てを処理するためのカテゴリを作成しました。 iOS 7以降で動作します。それが役に立てば幸い ! ilabel末尾の切り捨て
@implementation UILabel (Truncation)
- (NSRange)truncatedRange
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size];
textContainer.lineFragmentPadding = 0;
[layoutManager addTextContainer:textContainer];
NSRange truncatedrange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:0];
return truncatedrange;
}
- (BOOL)isTruncated
{
return [self truncatedRange].location != NSNotFound;
}
- (NSString *)truncatedText
{
NSRange truncatedrange = [self truncatedRange];
if (truncatedrange.location != NSNotFound)
{
return [self.text substringWithRange:truncatedrange];
}
return nil;
}
@end
これはiOS 8で機能します:
CGSize size = [label.text boundingRectWithSize:CGSizeMake(label.bounds.size.width, NSIntegerMax) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil].size;
if (size.height > label.frame.size.height) {
NSLog(@"truncated");
}
boundingRect(with:options:attributes:context:)
で問題が発生しました(最大高さを設定するために)自動レイアウトと NSParagraph.lineSpacing
で属性付きテキストを使用した場合
行間のスペースは無視された(attributes
でboundingRect
メソッドに渡された場合でも)ため、ラベルは切り捨てられなかったと見なされる場合があります。
私が見つけた解決策は、 UIView.sizeThatFits
を使用することです:
extension UILabel {
var isTruncated: Bool {
layoutIfNeeded()
let heightThatFits = sizeThatFits(bounds.size).height
return heightThatFits > bounds.size.height
}
}
extension UILabel {
public func resizeIfNeeded() -> CGFloat? {
guard let text = text, !text.isEmpty else { return nil }
if isTruncated() {
numberOfLines = 0
sizeToFit()
return frame.height
}
return nil
}
func isTruncated() -> Bool {
guard let text = text, !text.isEmpty else { return false }
let size: CGSize = text.size(withAttributes: [NSAttributedStringKey.font: font])
return size.width > self.bounds.size.width
}
}
文字列の幅を計算し、幅がラベルの幅よりも大きいかどうかを確認できます。
Swift 3(拡張子として)で選択された回答です。OPは約1行のラベルを求めていました。ここで試した多くのSwift複数行のラベルに、単一行のラベルに正しくフラグが付けられていません。
extension UILabel {
var isTruncated: Bool {
guard let labelText = text as? NSString else {
return false
}
let size = labelText.size(attributes: [NSFontAttributeName: font])
return size.width > self.bounds.width
}
}
@ iDev の内容に追加するために、self.frame.size.height
を使用するにはlabel.frame.size.height
また、NSStringDrawingUsesLineFontLeading
も使用しませんでした。これらの変更の後、切り捨てがいつ行われるかを完全に計算しました(少なくとも私の場合)。
- (BOOL)isTruncated:(UILabel *)label {
CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.bounds.size.width, CGFLOAT_MAX)
options: (NSStringDrawingUsesLineFragmentOrigin)
attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;
if (label.frame.size.height < ceilf(sizeOfText.height)) {
return YES;
}
return NO;
}
上記の答えはすべて減価償却方法を使用しているため、これは便利だと思いました。
- (BOOL)isLabelTruncated:(UILabel *)label
{
BOOL isTruncated = NO;
CGRect labelSize = [label.text boundingRectWithSize:CGSizeFromString(label.text) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil];
if (labelSize.size.width / labelSize.size.height > label.numberOfLines) {
isTruncated = YES;
}
return isTruncated;
}
IOS 6を処理するために(はい、私たちの中にはまだ必要な人もいます)、@ iDevの答えに対するもう1つの拡張があります。重要なポイントは、iOS 6では、sizeThatFitsを呼び出す前にUILabelのnumberOfLinesが0に設定されていることを確認することです。そうでない場合、ラベルテキストを描画するには、「numberOfLines相当の高さを描画するポイント」が必要であるという結果が表示されます。
- (BOOL)isTruncated
{
CGSize sizeOfText;
// iOS 7 & 8
if([self.text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
{
sizeOfText = [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:@{NSFontAttributeName:self.font}
context:nil].size;
}
// iOS 6
else
{
// For iOS6, set numberOfLines to 0 (i.e. draw label text using as many lines as it takes)
// so that siteThatFits works correctly. If we leave it = 1 (for example), it'll come
// back telling us that we only need 1 line!
NSInteger origNumLines = self.numberOfLines;
self.numberOfLines = 0;
sizeOfText = [self sizeThatFits:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)];
self.numberOfLines = origNumLines;
}
return ((self.bounds.size.height < sizeOfText.height) ? YES : NO);
}