2017-07-15 7 views
0

複数行のラベルの最適な幅を計算して、固定高さの水平行に複数のラベルを貼り付ける方法を作成したいと思います。スウィフトの複数行テキストの最適なラベル幅を計算する方法

let textAttributes: [String : Any] = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: UIFontTextStyle.title2)] 

let maximalWidth: CGFloat = text!.boundingRect(
     with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: height), 
     options: [NSStringDrawingOptions.usesLineFragmentOrigin], 
     attributes: textAttributes, 
     context: nil).size.width 

は、私の知る限り理解されるように、私はいくつかのラインを持っていることを、ここに示すオプションはありません:テキストの1行で

は問題ありません。このメソッドは、固定幅のテキストの高さを計算するとき、他の方向にもうまく機能します。しかし、私は反対の目標を持っています。変形例として

、私は最も長い単語(私たちは、同じ文字を複数の単語カウントが、異なるレンダリングされた幅を持つことができるよう、最も広い言葉に基づいて、より正確には)に基づいてラベルを作成することができます。

var sizeToReturn = CGSize() 

    let maxWordsCharacterCount = text?.maxWord.characters.count 
    let allLongWords: [String] = text!.wordList.filter {$0.characters.count == maxWordsCharacterCount} 
    var sizes: [CGFloat] = [] 
    allLongWords.forEach {sizes.append($0.size(attributes: attributes).width)} 
    let minimalWidth = (sizes.max()! + constantElementsWidth) 

私は言葉のリストを作成し、すべての最長見つけるためにここに2つの文字列の拡張機能を使用:

extension String { 
    var wordList: [String] { 
    return Array(Set(components(separatedBy: .punctuationCharacters).joined(separator: "").components(separatedBy: " "))).filter {$0.characters.count > 0} 
    } 
} 

extension String { 
    var maxWord: String { 
     if let max = self.wordList.max(by: {$1.characters.count > $0.characters.count}) { 
     return max 
    } else {return ""} 
} 

}

悪くない選択肢を、私たちは肝炎場合、それは醜いですe 3行に収まらないテキストで、最後にいくつかの短い単語と長い単語が1つあります。この長い単語は、幅を決定しただけで切り捨てられます。そして、それはのような3つの短い言葉であまりにも良いではないに見えることのより:

  • 売る

はまあ、私は最大の幅を持って、最小の幅を持っています。おそらく、 は最大から最小になり、ラベルが切り捨てられ始めるときに捕捉できます。 私は優雅な解決策があると感じますが、私は立ち往生しています。

+0

自動レイアウト制約を使用して、保持するコンテンツに基づいて必要に応じてラベルのサイズを設定する必要があります。ストーリーボードでこれを行うのは簡単ですが、必要に応じてプログラムでも行うことができます。私は自動レイアウトに関するいくつかの調査を行います:https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2 – user3353890

+0

私はautolayoutこの場合に役立ちます。 –

+0

私は幅を知っていれば、ラベルのサイズを変更することは問題ではないことを意味します。しかし、私はこの幅を計算することはできません。 –

答えて

0

私は可能な解決策の1つを見つけました。以下のコードをプレイグラウンドで使用することができます。

import UIKit 
import PlaygroundSupport 

//: Just a view to launch playground timeline preview 
let hostView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480)) 
hostView.backgroundColor = .lightGray 
PlaygroundPage.current.liveView = hostView 

// MARK: - Extensions 
extension String { 
    var wordList: [String] { 
     return Array(Set(components(separatedBy: .punctuationCharacters).joined(separator: "").components(separatedBy: " "))).filter {$0.characters.count > 0} 
    } 
} 

extension String { 
    var longestWord: String { 
     if let max = self.wordList.max(by: {$1.characters.count > $0.characters.count}) { 
      return max 
     } else {return ""} 
    } 
} 

// MARK: - Mathod 

func createLabelWithOptimalLabelWidth (
        requestedHeight: CGFloat, 
       constantElementsWidth: CGFloat, 
    acceptableWidthForTextOfOneLine: CGFloat, //When we don't want the text to be shrinked 
           text: String, 
         attributes: [String:Any] 
    ) -> UILabel { 

    let label = UILabel(frame: .zero) 

    label.attributedText = NSAttributedString(string: text, attributes: attributes) 

    let maximalLabelWidth = label.intrinsicContentSize.width 

    if maximalLabelWidth < acceptableWidthForTextOfOneLine { 

     label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: maximalLabelWidth, height: requestedHeight)) 
     return label // We can go with this width 
    } 

    // Minimal width, calculated based on the longest word 

    let maxWordsCharacterCount = label.text!.longestWord.characters.count 
    let allLongWords: [String] = label.text!.wordList.filter {$0.characters.count == maxWordsCharacterCount} 
    var sizes: [CGFloat] = [] 
    allLongWords.forEach {sizes.append($0.size(attributes: attributes).width)} 
    let minimalWidth = (sizes.max()! + constantElementsWidth) 


    // Height calculation 
    var flexibleWidth = maximalLabelWidth 
    var flexibleHeight = CGFloat() 

    var optimalWidth = CGFloat() 
    var optimalHeight = CGFloat() 

    while (flexibleHeight <= requestedHeight && flexibleWidth >= minimalWidth) { 

     optimalWidth = flexibleWidth 
     optimalHeight = flexibleHeight 

     flexibleWidth -= 1 

     flexibleHeight = label.attributedText!.boundingRect(
     with: CGSize(width: flexibleWidth, height: CGFloat.greatestFiniteMagnitude), 
     options: [NSStringDrawingOptions.usesLineFragmentOrigin], 
     context: nil).size.height 

     print("Width: \(flexibleWidth)") 
     print("Height: \(flexibleHeight)") 
     print("_______________________") 
    } 

    print("Final Width: \(optimalWidth)") 
    print("Final Height: \(optimalHeight)") 

    label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: optimalWidth+constantElementsWidth, height: requestedHeight)) 

    return label 
} 

// MARK: - Inputs 

let text: String? = "Determine the fair price"//nil//"Select the appropriate payment method"//"Finalize the order" //"Sell the car"//"Check the payment method" 
let font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.callout) 
let paragraphStyle = NSMutableParagraphStyle() 
paragraphStyle.lineBreakMode = .byWordWrapping 
paragraphStyle.allowsDefaultTighteningForTruncation = true 


let attributes: [String:Any] = [ 
    NSFontAttributeName: font, 
    NSParagraphStyleAttributeName: paragraphStyle, 
    NSBaselineOffsetAttributeName: 0 
] 

if text != nil { 
    let label = createLabelWithOptimalLabelWidth(requestedHeight: 70, constantElementsWidth: 0, acceptableWidthForTextOfOneLine: 120, text: text!, attributes: attributes) 

    label.frame.width 
    label.frame.height 

    label.backgroundColor = .white 
    label.lineBreakMode = .byWordWrapping 
    label.numberOfLines = 3 

    hostView.addSubview(label) 
} 
関連する問題