根据其属性字符串调整 DTAttributedTextContentView 的大小

问题描述 投票:0回答:1

我正在尝试根据其属性文本在集合单元格内调整 DTAttributedTextContentView 的大小。 我面临的问题是,当我像这样设置 attributeTextContentView 宽度约束时:

attributedTextContentView.widthAnchor.constraint(lessThanOrEqualToConstant: 260)

它将整个恒定宽度(在本例中为 260)应用于 textContentView,即使 attributeString 长度小于宽度,也会留下一些额外的空间:

我的问题是,如何调整 DTAttributedTextContentView 框架的大小,以便它只包含它包含的文本?

最初我使用基本的 UITextView,但是当有多个单元格时,通过集合视图滚动单元格并不那么顺利,而且它还可以轻松访问我的应用程序所需的文本的最后一行,所以我想要坚持使用 DTAttributedTextContentView。

这是示例代码:

import UIKit
import DTCoreText

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        configureCollectionView()
    }

// MARK: - Collection view setup

    let collectionView: UICollectionView = {
        let layout = UICollectionViewCompositionalLayout { (section, environment) -> NSCollectionLayoutSection? in
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(10))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            section.interGroupSpacing = 5
            return section
        }
        layout.configuration.scrollDirection = .vertical
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)

        collectionView.register(ConversationCollectionViewCell.self, forCellWithReuseIdentifier: "ConversationCell")
        return collectionView
    }()
    
    private func configureCollectionView() {
       collectionView.dataSource = self
       collectionView.backgroundColor = .brown
       view.addSubview(collectionView)

       collectionView.translatesAutoresizingMaskIntoConstraints = false
          NSLayoutConstraint.activate([
              collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
              collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
              collectionView.topAnchor.constraint(equalTo: view.topAnchor),
              collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
       ])
    }
}

  // MARK: - Collection Data Source

 extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCell", for: indexPath) as! ConversationCollectionViewCell
        return cell
    }
 }
  // MARK: - Collection View Custon Cell

final class ConversationCollectionViewCell: UICollectionViewCell, DTAttributedTextContentViewDelegate {

     var mainCellContainerView = UIView()
     var attributedTextContentView = DTAttributedTextContentView()

//MARK: - LIFECYCLE
     override init(frame: CGRect) {
        super.init(frame: frame)
    
        setupmainCellContainerView()
        setupAttributedTextContentView()
        layoutIfNeeded()
    }

   required init?(coder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
   }

// MARK: - UI STEUP

   private func setupmainCellContainerView() {
       contentView.addSubview(mainCellContainerView)
       mainCellContainerView.translatesAutoresizingMaskIntoConstraints = false

       NSLayoutConstraint.activate([
           mainCellContainerView.topAnchor.constraint(equalTo: contentView.topAnchor),
           mainCellContainerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
           mainCellContainerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
           mainCellContainerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
       ])
   }
   private func setupAttributedTextContentView() {
       mainCellContainerView.addSubview(attributedTextContentView)

       attributedTextContentView.backgroundColor = .systemIndigo
       attributedTextContentView.delegate = self
       attributedTextContentView.sizeToFit()

       let attributedString = NSAttributedString(string: "Simple message for testing purpose @", attributes: [
        .font:  UIFont(name: "HelveticaNeue", size: 17),
        .foregroundColor: UIColor.white,
        .paragraphStyle: {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = .left
            paragraphStyle.lineBreakMode = .byWordWrapping
            return paragraphStyle
        }()
    ])
        attributedTextContentView.attributedString = attributedString
        attributedTextContentView.contentMode = .redraw

       attributedTextContentView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        attributedTextContentView.widthAnchor.constraint(lessThanOrEqualToConstant: 260),
        attributedTextContentView.topAnchor.constraint(equalTo: mainCellContainerView.topAnchor),
        attributedTextContentView.bottomAnchor.constraint(equalTo: mainCellContainerView.bottomAnchor),
    ])
  }
}
ios swift uikit autolayout
1个回答
0
投票

DTAttributedTextContentView
(与
UITextView
和多行
UILabel
)将文本包裹在您指定的 width 内。

在您的代码中,您将宽度设置为

lessThanOrEqualToConstant: 260
...因此
DTAttributedTextContentView
使用该宽度。实际上,因为您实际上没有分配宽度,所以我有点惊讶它不会产生这样的结果:

无论如何,为了得到你想要的结果,你需要使用

attributedString.boundingRect
自己计算文本的宽度。

因此,我们修改您的单元格类以在“DT”视图上使用可变宽度约束,并在设置文本时更新:

public func updateText(_ str: String) {

    let attributedString = NSAttributedString(string: str, attributes: [
        .font:  UIFont(name: "HelveticaNeue", size: 17),
        .foregroundColor: UIColor.white,
        .paragraphStyle: {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = .left
            paragraphStyle.lineBreakMode = .byWordWrapping
            return paragraphStyle
        }()
    ])

    // max width of 260.0
    let r: CGRect = attributedString.boundingRect(with: .init(width: 260.0, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
    widthConstraint.constant = ceil(r.width)

    attributedTextContentView.attributedString = attributedString

}

cellForItemAt
变为:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCell", for: indexPath) as! ConversationCollectionViewCell
    cell.updateText(yourData[indexPath.item])
    return cell
}

这是您的代码,已修改:


细胞类

final class ConversationCollectionViewCell: UICollectionViewCell, DTAttributedTextContentViewDelegate {
    
    var mainCellContainerView = UIView()
    var attributedTextContentView = DTAttributedTextContentView()
    
    var widthConstraint: NSLayoutConstraint!
    
    //MARK: - LIFECYCLE
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupmainCellContainerView()
        setupAttributedTextContentView()
        layoutIfNeeded()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - UI STEUP
    
    private func setupmainCellContainerView() {
        contentView.addSubview(mainCellContainerView)
        mainCellContainerView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            mainCellContainerView.topAnchor.constraint(equalTo: contentView.topAnchor),
            mainCellContainerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            mainCellContainerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            mainCellContainerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
        ])
    }
    private func setupAttributedTextContentView() {
        mainCellContainerView.addSubview(attributedTextContentView)
        
        attributedTextContentView.backgroundColor = .systemIndigo
        attributedTextContentView.delegate = self
        attributedTextContentView.contentMode = .redraw
        
        attributedTextContentView.translatesAutoresizingMaskIntoConstraints = false
        
        // initialize the width constraint - it will be updated when the text is set
        widthConstraint = attributedTextContentView.widthAnchor.constraint(equalToConstant: 260.0)

        NSLayoutConstraint.activate([
            widthConstraint,
            attributedTextContentView.topAnchor.constraint(equalTo: mainCellContainerView.topAnchor),
            attributedTextContentView.bottomAnchor.constraint(equalTo: mainCellContainerView.bottomAnchor),
        ])
    }
    
    public func updateText(_ str: String) {

        let attributedString = NSAttributedString(string: str, attributes: [
            .font:  UIFont(name: "HelveticaNeue", size: 17),
            .foregroundColor: UIColor.white,
            .paragraphStyle: {
                let paragraphStyle = NSMutableParagraphStyle()
                paragraphStyle.alignment = .left
                paragraphStyle.lineBreakMode = .byWordWrapping
                return paragraphStyle
            }()
        ])

        // max width of 260.0
        let r: CGRect = attributedString.boundingRect(with: .init(width: 260.0, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
        widthConstraint.constant = ceil(r.width)

        attributedTextContentView.attributedString = attributedString

    }
    
}

控制器类

class ViewController: UIViewController {
    
    let samples: [String] = [
        "Sample 1",
        "The second sample.",
        "This is the third sample string.",
        "Simple message for testing purpose @",
        "This is yet another sample, which should wrap onto at least three lines (based on a max-width of 260 points).",
        "Here is the final sample string for this example.",
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        configureCollectionView()
    }
    
    // MARK: - Collection view setup
    
    let collectionView: UICollectionView = {
        let layout = UICollectionViewCompositionalLayout { (section, environment) -> NSCollectionLayoutSection? in
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(10))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            section.interGroupSpacing = 5
            return section
        }
        layout.configuration.scrollDirection = .vertical
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        
        collectionView.register(ConversationCollectionViewCell.self, forCellWithReuseIdentifier: "ConversationCell")
        return collectionView
    }()
    
    private func configureCollectionView() {
        collectionView.dataSource = self
        collectionView.backgroundColor = .brown
        view.addSubview(collectionView)
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            collectionView.topAnchor.constraint(equalTo: view.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

// MARK: - Collection Data Source

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return samples.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCell", for: indexPath) as! ConversationCollectionViewCell
        cell.updateText(samples[indexPath.item])
        return cell
    }
}

在 iPhone 15 Pro 上应该是这样的:

© www.soinside.com 2019 - 2024. All rights reserved.