Как получить текст/строку из n-й строки UILabel?
Есть ли простой способ получить (или просто отобразить) текст из данной строки в UILabel?
Мой UILabel правильно отображает мой текст и красиво оформляет его, но иногда мне нужно просто показывать определенные строки, но, очевидно, мне нужно знать, как UILabel позиционирует все для этого.
Я знаю, что это легко можно сделать с помощью подстроки, но мне нужно знать начальную и конечную точки строки.
В качестве альтернативы я мог бы прокручивать UILabel, если было какое-то смещение к кадру UILabel и скрыть остальную часть содержимого, которое я не хотел видеть.
Я не смог раскрыть что-нибудь, что показывает, как это можно сделать легко. У кого-нибудь есть хорошие идеи?
Спасибо
iphaaw
Ответы
Ответ 1
Я не думаю, что есть собственный способ сделать это (например, метод "takethenline" ).
Я могу найти сложное решение, но я не уверен, что это лучший.
Вы можете разделить свой ярлык на массив слов.
Затем вы можете зацикливать массив и проверить высоту текста до этого слова следующим образом:
NSString *texttocheck;
float old_height = 0;
int linenumber = 0;
for (x=0; x<[wordarray lenght]; x++) {
texttocheck = [NSString stringWithFormat:@"%@ %@", texttocheck, [wordarray objectAtIndex:x]];
float height = [text sizeWithFont:textLabel.font
constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999)
lineBreakMode:UILineBreakModeWordWrap].height;
if (old_height < height) {
linenumber++;
}
}
Если высота изменяется, это означает, что перед словом происходит разрыв строки.
Я не могу проверить, правильно ли написано синтаксис, поэтому вы должны проверить его самостоятельно.
Ответ 2
У меня есть лучший способ найти его.
Вы можете получить это с помощью CoreText.framework.
1.Добавьте CoreText.framework.
2.Import #import <CoreText/CoreText.h>
.
Затем используйте метод ниже:
- (NSArray *)getLinesArrayOfStringInLabel:(UILabel *)label {
NSString *text = [label text];
UIFont *font = [label font];
CGRect rect = [label frame];
CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc]init];
for (id line in lines)
{
CTLineRef lineRef = (__bridge CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
[linesArray addObject:lineString];
}
return (NSArray *)linesArray;
}
Назовите этот метод: -
NSArray *linesArray = [self getLinesArrayOfStringInLabel:yourLabel];
Теперь вы можете использовать linesArray
.
SWIFT 4 VERSION
func getLinesArrayOfString(in label: UILabel) -> [String] {
/// An empty string array
var linesArray = [String]()
guard let text = label.text, let font = label.font else {return linesArray}
let rect = label.frame
let myFont: CTFont = CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil)
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute(kCTFontAttributeName as NSAttributedStringKey, value: myFont, range: NSRange(location: 0, length: attStr.length))
let frameSetter: CTFramesetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path: CGMutablePath = CGMutablePath()
path.addRect(CGRect(x: 0, y: 0, width: rect.size.width, height: 100000), transform: .identity)
let frame: CTFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
guard let lines = CTFrameGetLines(frame) as? [Any] else {return linesArray}
for line in lines {
let lineRef = line as! CTLine
let lineRange: CFRange = CTLineGetStringRange(lineRef)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString: String = (text as NSString).substring(with: range)
linesArray.append(lineString)
}
return linesArray
}
Использование:
let lines: [String] = getLinesArrayOfString(in: label)
Ответ 3
Swift 3
func getLinesArrayFromLabel(label:UILabel) -> [String] {
let text:NSString = label.text! as NSString // TODO: Make safe?
let font:UIFont = label.font
let rect:CGRect = label.frame
let myFont:CTFont = CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil)
let attStr:NSMutableAttributedString = NSMutableAttributedString(string: text as String)
attStr.addAttribute(String(kCTFontAttributeName), value:myFont, range: NSMakeRange(0, attStr.length))
let frameSetter:CTFramesetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path:CGMutablePath = CGMutablePath()
path.addRect(CGRect(x:0, y:0, width:rect.size.width, height:100000))
let frame:CTFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
let lines = CTFrameGetLines(frame) as NSArray
var linesArray = [String]()
for line in lines {
let lineRange = CTLineGetStringRange(line as! CTLine)
let range:NSRange = NSMakeRange(lineRange.location, lineRange.length)
let lineString = text.substring(with: range)
linesArray.append(lineString as String)
}
return linesArray
}
версия Swift 2 (Xcode 7) (протестирована и отредактирована из ответа Swift 1)
func getLinesArrayOfStringInLabel(label:UILabel) -> [String] {
let text:NSString = label.text! // TODO: Make safe?
let font:UIFont = label.font
let rect:CGRect = label.frame
let myFont:CTFontRef = CTFontCreateWithName(font.fontName, font.pointSize, nil)
let attStr:NSMutableAttributedString = NSMutableAttributedString(string: text as String)
attStr.addAttribute(String(kCTFontAttributeName), value:myFont, range: NSMakeRange(0, attStr.length))
let frameSetter:CTFramesetterRef = CTFramesetterCreateWithAttributedString(attStr as CFAttributedStringRef)
let path:CGMutablePathRef = CGPathCreateMutable()
CGPathAddRect(path, nil, CGRectMake(0, 0, rect.size.width, 100000))
let frame:CTFrameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
let lines = CTFrameGetLines(frame) as NSArray
var linesArray = [String]()
for line in lines {
let lineRange = CTLineGetStringRange(line as! CTLine)
let range:NSRange = NSMakeRange(lineRange.location, lineRange.length)
let lineString = text.substringWithRange(range)
linesArray.append(lineString as String)
}
return linesArray
}
Ответ 4
Ответ на правильный выпуск!!!!
-(NSArray *)getLinesArrayOfStringInLabel:(UILabel *)label
{
NSString *text = [label text];
UIFont *font = [label font];
CGRect rect = [label frame];
CTFontRef myFont = CTFontCreateWithName(( CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:( id)myFont range:NSMakeRange(0, attStr.length)];
CFRelease(myFont);
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(( CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = ( NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc]init];
for (id line in lines)
{
CTLineRef lineRef = ( CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr, lineRange, kCTKernAttributeName, (CFTypeRef)([NSNumber numberWithFloat:0.0]));
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr, lineRange, kCTKernAttributeName, (CFTypeRef)([NSNumber numberWithInt:0.0]));
//NSLog(@"''''''''''''''''''%@",lineString);
[linesArray addObject:lineString];
}
[attStr release];
CGPathRelease(path);
CFRelease( frame );
CFRelease(frameSetter);
return (NSArray *)linesArray;
}
Ответ 5
Это версия Swift 3 для получения всех строк в ярлыке. (@fredpi имеет аналогичный ответ, но только для первой строки)
extension UILabel {
func getArrayOfLinesInLabel() -> [String] {
let text = NSString(string: self.text ?? "-- -- -- --")
let font = self.font ?? // Your default font here
let rect = self.frame
let myFont = CTFontCreateWithName(font.fontName as CFString?, font.pointSize, nil)
let attStr = NSMutableAttributedString(string: text as String)
attStr.addAttribute(String(kCTFontAttributeName), value:myFont, range: NSRange(location: 0, length: attStr.length))
let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = CGPath(rect: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height), transform: nil)
let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
guard let lines = CTFrameGetLines(frame) as? [CTLine] else {
return []
}
var linesArray = [String]()
for line in lines {
let lineRange = CTLineGetStringRange(line)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString = text.substring(with: range)
linesArray.append(lineString as String)
}
return linesArray
}
}
Ответ 6
Swift 3 - Xcode 8.1
Я собрал код из предыдущих ответов, чтобы создать Swift 3, Xcode 8.1-совместимый extension
to UILabel
, возвращающий первую строку метки.
import CoreText
extension UILabel {
/// Returns the String displayed in the first line of the UILabel or "" if text or font is missing
var firstLineString: String {
guard let text = self.text else { return "" }
guard let font = self.font else { return "" }
let rect = self.frame
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute(String(kCTFontAttributeName), value: CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil), range: NSMakeRange(0, attStr.length))
let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = CGMutablePath()
path.addRect(CGRect(x: 0, y: 0, width: rect.size.width + 7, height: 100))
let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
guard let line = (CTFrameGetLines(frame) as! [CTLine]).first else { return "" }
let lineString = text[text.startIndex...text.index(text.startIndex, offsetBy: CTLineGetStringRange(line).length-2)]
return lineString
}
}
Чтобы использовать его, просто вызовите firstLineString
в свой экземпляр UILabel
следующим образом:
let firstLine = myLabel.firstLineString
Ответ 7
Если все ваши символы отображаются одинакового размера, т.е. они заключены в ящик с общим размером, вы можете воспользоваться этим. (Например, это похоже на японские символы.)
В противном случае вы можете запросить размер каждого символа в шрифте дисплея и рассчитать, какая строка должна быть.
Единственное беспокойство в том, что ваш расчет может не совпадать с тем, что Apple делает за кулисами, и в этом случае я рекомендую вам перейти к переопределению чертежа текстового фрейма. Посмотрите на основной текст в документах для этого.
(Возможно, я делал это неправильно, но я не нашел метод Apple, как указано в документах, был очень точным, поэтому я сделал что-то еще сам.)
Ответ 8
Очень важное изменение относительно iOS 11+
Начиная с iOS 11, Apple намеренно изменила поведение своей функции переноса UILabel
для UILabel
которая влияет на обнаружение содержимого String
отдельных строк в многострочном UILabel
. По замыслу, перенос UILabel
в UILabel
теперь позволяет избежать UILabel
текста, как обсуждалось здесь: перенос слов в iOS 11
Из-за этого способ, которым CTFrameGetLines(frame)
возвращает объекты CTLine
больше не работает правильно, если вступает в силу новая переноса слов, которая позволяет избежать потерянного текста. Это приводит к появлению частей String
которые, согласно новому дизайну переноса слов, будут в следующей строке, все еще заканчивая строкой в фокусе.
Исправление можно найти в моей измененной версии ответа @TheTiger, в которой используется расчет фактического размера содержимого UILabel
с использованием sizeThatFits(size:)
:
extension UILabel {
/// creates an array containing one entry for each line of text the label has
var lines: [String]? {
guard let text = text, let font = font else { return nil }
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute(NSAttributedString.Key.font, value: font, range: NSRange(location: 0, length: attStr.length))
let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = CGMutablePath()
// size needs to be adjusted, because frame might change because of intelligent word wrapping of iOS
let size = sizeThatFits(CGSize(width: self.frame.width, height: .greatestFiniteMagnitude))
path.addRect(CGRect(x: 0, y: 0, width: size.width, height: size.height), transform: .identity)
let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path, nil)
guard let lines = CTFrameGetLines(frame) as? [Any] else { return nil }
var linesArray: [String] = []
for line in lines {
let lineRef = line as! CTLine
let lineRange = CTLineGetStringRange(lineRef)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString = (text as NSString).substring(with: range)
linesArray.append(lineString)
}
return linesArray
}
}