18

UILabelの特定の行からテキストを取得する(または単に表示する)簡単な方法はありますか?

私のUILabelはテキストを正しく表示し、美しくレイアウトしていますが、特定の行だけを表示できるようにする必要がある場合もありますが、UILabelがこれを行うためにすべてをどのように配置しているかを知る必要があります。

これは部分文字列で簡単に実行できることはわかっていますが、行の始点と終点を知る必要があります。

または、UILabelのフレームに何らかのオフセットがある場合は、UILabelをスクロールして、表示したくない残りのコンテンツを非表示にすることもできます。

これを簡単に行う方法を示すものは何も見つかりませんでした。誰か良いアイデアがありますか?

ありがとう

iphaaw

4

10 に答える 10

32

私はそれを見つけるためのより良い方法があります。

CoreText.frameworkを使用してこれを取得できます。

1.CoreText.frameworkを追加します。
2.インポート#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

SWIFT4バージョン

func getLinesArrayOfString(in label: UILabel) -> [String] {
        
        /// An empty string's array
        var linesArray = [String]()
        
        guard let text = label.text, let font = label.font else {return linesArray}
        
        let rect = label.frame
        
        let myFont = CTFontCreateWithFontDescriptor(font.fontDescriptor, 0, nil)
        let attStr = NSMutableAttributedString(string: text)
        attStr.addAttribute(kCTFontAttributeName as NSAttributedString.Key, 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)
于 2013-01-19T10:42:23.050 に答える
9

スウィフト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
}
于 2015-06-30T02:04:21.360 に答える
5

適切なリリースで答えてください!!!!

-(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;
}
于 2013-02-19T08:28:01.517 に答える
5

iOS11以降に関する非常に重要な変更

iOS 11以降、Appleは、複数行の個々の行の内容を検出する効果を持つワードラップ機能の動作を意図的に変更しました。設計上、ここで説明するように、現在のワードラップは孤立したテキスト(新しい行の単一のワード)を回避します:iOS11でのワードラップUILabelStringUILabelUILabel

CTFrameGetLines(frame)そのため、孤立したテキストを回避する新しいワードラップが特定の行で有効になると、ラベル内のすべての行の配列を返す方法がCTLine正しく機能しなくなります。それどころか、String新しいワードラップデザインによって次の行に属し、代わりにフォーカスのある行になってしまう部分が発生します。

この問題のテスト済みの修正は、@ TheTigerの回答の変更バージョンにあります。これは、 Swift4で記述されたrect/ pathを作成するためにそのサイズを使用する前に、 UILabelusingの実際のコンテンツサイズの計算を利用します。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
    }
}

このUILabel拡張機能は、ラベルの内容をString、ユーザーの目に表示されたとおりに1行に1つのエントリを持つ配列として返します。

于 2018-12-14T16:06:06.733 に答える
4

これを行うためのネイティブな方法(「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++;
    }
}

高さが変わる場合は、単語の前に改行があることを意味します。
現在、構文が正しく記述されているかどうかを確認できないため、自分で確認する必要があります。

于 2010-12-12T13:27:22.610 に答える
2

これは、ラベルのすべての行を取得するためのSwift3バージョンです。(@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
   }
}
于 2017-04-07T18:23:18.457 に答える
1

Swift 3 – Xcode 8.1

extension以前の回答のコードをまとめて、ラベルの最初の行をUILabel返すのと互換性のあるSwift 3、Xcode8.1を作成しました。

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
于 2016-11-06T12:22:00.010 に答える
0

すべての文字が同じサイズで表示されている場合、つまり、共通のサイズのボックスで囲まれている場合は、それを利用できます。(たとえば、これは日本語の文字の場合のようです。)

それ以外の場合は、表示フォントの各文字のサイズを照会して、行の数を計算できます。

唯一の心配は、あなたの計算がAppleが舞台裏で行っていることと一致しないかもしれないということです-その場合、私はあなたがテキストフレームの描画を上書きする問題に行くことをお勧めします。これについては、ドキュメントでCoreTextを検索してください。

(私はこれを間違って行っていたかもしれませんが、ドキュメントに記載されているAppleの方法が非常に正確であるとは思わなかったので、自分で何か他のことをしました。)

于 2011-03-28T07:21:58.620 に答える
0

受け入れられた答えはとても良いです。

私は2つの場所をリファクタリングしました:

  1. 10000をに変更CGFloat.greatestFiniteMagnitude

  2. に追加しましextensionUILabel

  3. また、フレームを設定してラベルを作成すると、問題なく動作します。自動レイアウトを使用する場合は、電話することを忘れないでください

    youLabel.layoutIfNeeded()

正しいフレームサイズを取得します。

コードは次のとおりです。

extension UILabel {
    var stringLines: [String] {
        guard let text = text, let font = font else { return [] }
        let ctFont = CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil)
        let attStr = NSMutableAttributedString(string: text)
        attStr.addAttribute(kCTFontAttributeName as NSAttributedString.Key, value: ctFont, range: NSRange(location: 0, length: attStr.length))
        let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
        let path = CGMutablePath()
        path.addRect(CGRect(x: 0, y: 0, width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude), transform: .identity)
        let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
        guard let lines = CTFrameGetLines(frame) as? [Any] else { return [] }
        return lines.map { line in
            let lineRef = line as! CTLine
            let lineRange: CFRange = CTLineGetStringRange(lineRef)
            let range = NSRange(location: lineRange.location, length: lineRange.length)
            return (text as NSString).substring(with: range)
        }
    }
}
于 2019-03-14T07:22:42.530 に答える
0

申し訳ありませんが、私の評判は低すぎてコメントできません。これは、PhilippJahodaからのhttps://stackoverflow.com/a/53783203/2439941へのコメントです。

UILabelで動的タイプを有効にするまで、コードスニペットは問題なく機能しました。iOS設定アプリでテキストサイズを最大値に設定すると、返された配列の最後の行の文字が欠落し始めました。または、かなりの量のテキストで最後の行を完全に見逃すことさえあります。

別の方法を使用してこれを解決することができましたframe

let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: self.frame.width, height: .greatestFiniteMagnitude))
let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path.cgPath, nil)
guard let lines = CTFrameGetLines(frame) as? [Any] else { return nil }

これで、どのダイナミックタイプサイズでも正しく機能します。

完全な機能は次のとおりです。

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 = UIBezierPath(rect: CGRect(x: 0, y: 0, width: self.frame.width, height: .greatestFiniteMagnitude))
        let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path.cgPath, 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
    }
}
于 2019-05-31T07:07:34.247 に答える