我有一个多行标签,lineBreakMode 设置为 UILineBreakModeWordWrap。如何确定最后一行的宽度?
问问题
3218 次
4 回答
8
从 iOS 7.0 开始,您可以使用此功能(也许您需要针对您的情况对文本容器进行更多调整):
public func lastLineMaxX(message: NSAttributedString, labelWidth: CGFloat) -> CGFloat {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let labelSize = CGSize(width: bubbleWidth, height: .infinity)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: labelSize)
let textStorage = NSTextStorage(attributedString: message)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = .byWordWrapping
textContainer.maximumNumberOfLines = 0
let lastGlyphIndex = layoutManager.glyphIndexForCharacter(at: message.length - 1)
let lastLineFragmentRect = layoutManager.lineFragmentUsedRect(forGlyphAt: lastGlyphIndex,
effectiveRange: nil)
return lastLineFragmentRect.maxX
}
目标-C:
- (CGFloat)lastLineMaxXWithMessage:(NSAttributedString *)message labelWidth:(CGFloat)labelWidth
{
CGSize labelSize = CGSizeMake(labelWidth, CGFLOAT_MAX);
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:labelSize];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:message];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
textContainer.lineFragmentPadding = 0;
textContainer.lineBreakMode = NSLineBreakByWordWrapping;
textContainer.maximumNumberOfLines = 0;
NSUInteger lastGlyphIndex = [layoutManager glyphIndexForCharacterAtIndex:[message length] - 1];
CGRect lastLineFragmentRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:lastGlyphIndex effectiveRange:nil];
return CGRectGetMaxX(lastLineFragmentRect);
}
然后您可以决定最后一行是否有足够的位置放置您的日期标签
用法示例:
// you definitely have to set at least the font to calculate the result
// maybe for your case you will also have to set other attributes
let attributedText = NSAttributedString(string: label.text,
attributes: [.font: label.font])
let lastLineMaxX = lastLineMaxX(message: attributedText,
labelWidth: label.bounds.width)
于 2018-09-05T17:41:39.627 回答
5
这就是我的做法。首先将标签的行放在 NSArray 中,然后检查最后一行的宽度。在 viewDidLoad 中:
NSArray* lines = [self getSeparatedLinesFromLbl:srcLabel];
NSString *lastLine=[lines lastObject];
float lastLineWidth=[lastLine sizeWithFont:srcLabel.font constrainedToSize:boundingSize lineBreakMode:NSLineBreakByWordWrapping].width;
并 getSeparatedLinesFromLbl:
-(NSArray*)getSeparatedLinesFromLbl:(UILabel*)lbl
{
if ( lbl.lineBreakMode != NSLineBreakByWordWrapping )
{
return nil;
}
NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10];
NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString* currentLine = lbl.text;
int textLength = [lbl.text length];
NSRange rCurrentLine = NSMakeRange(0, textLength);
NSRange rWhitespace = NSMakeRange(0,0);
NSRange rRemainingText = NSMakeRange(0, textLength);
BOOL done = NO;
while ( !done )
{
// determine the next whitespace word separator position
rWhitespace.location = rWhitespace.location + rWhitespace.length;
rWhitespace.length = textLength - rWhitespace.location;
rWhitespace = [lbl.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace];
if ( rWhitespace.location == NSNotFound )
{
rWhitespace.location = textLength;
done = YES;
}
NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location);
NSString* textTest = [lbl.text substringWithRange: rTest];
CGSize sizeTest = [textTest sizeWithFont: lbl.font forWidth: 1024.0 lineBreakMode: NSLineBreakByWordWrapping];
if ( sizeTest.width > lbl.bounds.size.width )
{
[lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
rRemainingText.location = rCurrentLine.location + rCurrentLine.length;
rRemainingText.length = textLength-rRemainingText.location;
continue;
}
rCurrentLine = rTest;
currentLine = textTest;
}
[lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
return lines;
}
于 2013-05-15T06:55:48.700 回答
2
更新 Swift 4.2 (IOS 12)
extension UILabel {
func getSeparatedLines() -> [Any] {
if self.lineBreakMode != NSLineBreakMode.byWordWrapping {
self.lineBreakMode = .byWordWrapping
}
var lines = [Any]() /* capacity: 10 */
let wordSeparators = CharacterSet.whitespacesAndNewlines
var currentLine: String? = self.text
let textLength: Int = (self.text?.count ?? 0)
var rCurrentLine = NSRange(location: 0, length: textLength)
var rWhitespace = NSRange(location: 0, length: 0)
var rRemainingText = NSRange(location: 0, length: textLength)
var done: Bool = false
while !done {
// determine the next whitespace word separator position
rWhitespace.location = rWhitespace.location + rWhitespace.length
rWhitespace.length = textLength - rWhitespace.location
rWhitespace = (self.text! as NSString).rangeOfCharacter(from: wordSeparators, options: .caseInsensitive, range: rWhitespace)
if rWhitespace.location == NSNotFound {
rWhitespace.location = textLength
done = true
}
let rTest = NSRange(location: rRemainingText.location, length: rWhitespace.location - rRemainingText.location)
let textTest: String = (self.text! as NSString).substring(with: rTest)
let fontAttributes: [String: Any]? = [NSAttributedString.Key.font.rawValue: font]
let maxWidth = (textTest as NSString).size(withAttributes: [NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue): font]).width
if maxWidth > self.bounds.size.width {
lines.append(currentLine?.trimmingCharacters(in: wordSeparators) ?? "")
rRemainingText.location = rCurrentLine.location + rCurrentLine.length
rRemainingText.length = textLength - rRemainingText.location
continue
}
rCurrentLine = rTest
currentLine = textTest
}
lines.append(currentLine?.trimmingCharacters(in: wordSeparators) ?? "")
return lines
}
var lastLineWidth: CGFloat {
let lines: [Any] = self.getSeparatedLines()
if !lines.isEmpty {
let lastLine: String = (lines.last as? String)!
let fontAttributes = [NSAttributedString.Key.font.rawValue: font]
return (lastLine as NSString).size(withAttributes: [NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue): font]).width
}
return 0
}
}
用法
print(yourLabel.lastLineWidth)
斯威夫特 4.2 (IOS 12)
于 2017-07-31T08:51:54.443 回答
0
我发现 NSLayoutManager 为此提供了 API:
// Returns the usage rect for the line fragment in which the given glyph is laid and (optionally) by reference the whole range of glyphs that are in that fragment.
// This will cause glyph generation and layout for the line fragment containing the specified glyph, or if non-contiguous layout is not enabled, up to and including that line fragment.
// Line fragment used rects are always in container coordinates.
open func lineFragmentUsedRect(forGlyphAt glyphIndex: Int, effectiveRange effectiveGlyphRange: NSRangePointer?) -> CGRect
所以,我认为我们可以这样做:
let lastGlyphIndex = self.text.count - 1
let lastLineWidth = layoutManager.lineFragmentUsedRect(forGlyphAt: lastGlyphIndex, effectiveRange: nil).size.with
于 2020-03-30T12:02:44.173 回答