带有核心文本的自定义 UITableViewCell

Custom UITableViewCell With Core Text

在@yaslam 的帮助下,我在 Core Text 中创建了一个 UILabel,它使用 CTRubyAnnotation 以水平和垂直方式显示日文文本,并带有注音假名。不幸的是我有一个问题。我需要在自定义单元格内使用此标签,并且我需要该单元格根据文本动态调整单元格的高度。但不工作。单元格不展开

你能帮帮我吗?

非常感谢

这是代码

import UIKit

protocol SimpleVerticalGlyphViewProtocol {
}

extension SimpleVerticalGlyphViewProtocol {

    func drawContext(_ attributed:NSMutableAttributedString, textDrawRect:CGRect, isVertical:Bool) {

        guard let context = UIGraphicsGetCurrentContext() else { return }

        var path:CGPath
        if isVertical {
            context.rotate(by: .pi / 2)
            context.scaleBy(x: 1.0, y: -1.0)
            path = CGPath(rect: CGRect(x: textDrawRect.origin.y, y: textDrawRect.origin.x, width: textDrawRect.height, height: textDrawRect.width), transform: nil)
        }
        else {
            context.textMatrix = CGAffineTransform.identity
            context.translateBy(x: 0, y: textDrawRect.height)
            context.scaleBy(x: 1.0, y: -1.0)
            path = CGPath(rect: textDrawRect, transform: nil)
        }

        let framesetter = CTFramesetterCreateWithAttributedString(attributed)
        let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, nil)

        CTFrameDraw(frame, context)
    }
}

class CustomLabel: UILabel, SimpleVerticalGlyphViewProtocol {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */

    override func drawText(in rect: CGRect) {
        let attributed = NSMutableAttributedString(attributedString: self.attributedText!)
        let isVertical = false // if Vertical Glyph, true.
        attributed.addAttributes([NSAttributedStringKey.verticalGlyphForm: isVertical], range: NSMakeRange(0, attributed.length))
        attributed.addAttribute(NSAttributedStringKey.font, value: UIFont(name: "Hiragino Mincho ProN", size: 27)!, range: NSMakeRange(0, attributed.length))
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineHeightMultiple = 2
        paragraphStyle.lineSpacing = 4
        attributed.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, (attributed.length)))
        drawContext(attributed, textDrawRect: rect, isVertical: isVertical)
    }
}

表格视图class

import UIKit

class TableViewController: UITableViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

    let label = cell.viewWithTag(10) as! CustomLabel
    let attributedText = Utility.sharedInstance.furigana(String: "|銀行《ぎんこう》と|郵便局《ゆうびんきょく》の|間《あいだ》の|道《みち》をまっすぐ|行《い》くと、|学校《がっこう》の|前《まえ》に|出《で》ます。")


    label.attributedText = attributedText

    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

    return UITableViewAutomaticDimension
    //return 70
}

这是结果:

使用CoreText,不会自动确定View 的高度。 计算CoreText的绘图高度,设置为Cell中UIView的高度。 在 Storyboard 中对 UITableView 进行以下设置。
* 检查自动行高
* 检查自动估计

对于程序,如下。

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = UITableViewAutomaticDimension

用于计算 CoreText 绘图高度的示例代码。 我不知道它是否是计算高度的最佳代码。 示例代码非常草率,请实际重构它。

import UIKit

class CoreTextWithTableViewController: UITableViewController {

    var texts = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        // tableView.rowHeight = UITableViewAutomaticDimension // or check Automatic of Row Height in storyboard
        // tableView.estimatedRowHeight = UITableViewAutomaticDimension // or check Automatic of Estimate in storyboard

        let text = "すでに|世界《せかい》で最も|先進的《せんしんてき》なモバイルオペレーティングシステムであるiOSに、iOS 11が新あらたな|基準《きじゅん》を打ち立てます。iPhoneは今まで以上に優れたものになり、iPadはかつてないほどの|能力《のうりょく》を手に入れます。さらにこれからはどちらのデバイスにも、ゲームやアプリケーションの|拡張現実《かくちょうげんじつ》のための驚くような|可能性《かのうせい》が広がります。iOS 11を|搭載《とうさい》するiPhoneとiPadは、間違いなくこれまでで最もパワフルで、最もパーソナルで、最も賢いデバイスです。"
        let text2 = "すでに|世界《せかい》で最も|先進的《せんしんてき》なモバイルオペレーティングシステムであるiOSに、iOS 11が新あらたな|基準《きじゅん》を打ち立てます。iPhoneは今まで以上に優れたものになり、iPadはかつてないほどの|能力《のうりょく》を手に入れます。"

        for _ in 0...20 {
            texts.append(text)
            texts.append(text2)
        }

        NotificationCenter.default.addObserver(self, selector: #selector(changeDirection(notification:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return texts.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCellWithCoreText
        let text = texts[indexPath.row]
        cell.coreTextView.text = text
        let height = cell.coreTextView.heightOfCoreText()
        cell.heightOfCoreTextView.constant = height

        // Execute redraw
        cell.coreTextView.setNeedsDisplay()  

        return cell
    }
}

extension CoreTextWithTableViewController {
    @objc func changeDirection(notification: NSNotification){
        tableView.reloadData()
    }
}



class CustomCellWithCoreText: UITableViewCell {
    @IBOutlet weak var coreTextView: CustomViewWithCoreText!
    @IBOutlet weak var heightOfCoreTextView: NSLayoutConstraint!
}

class CustomViewWithCoreText: UIView, SimpleVerticalGlyphViewProtocol {

    var text: String = ""
    lazy var attributed: NSMutableAttributedString = text.attributedStringWithRuby()
    var height = CGFloat()

    override func draw(_ rect: CGRect) {
        let textDrawRect = CGRect(x: rect.origin.x, y: rect.origin.y, width: rect.size.width, height: height)
        drawContext(attributed, textDrawRect: textDrawRect, isVertical: false)
    }

    /// get Height of CoreText Draw Rect
    func heightOfCoreText() -> CGFloat {

        // initialize height and attributed
        height = CGFloat()
        attributed = text.attributedStringWithRuby()


        // MEMO: height = CGFloat.greatestFiniteMagnitude
        let textDrawRect = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude)
        let path = CGPath(rect: textDrawRect, transform: nil)
        let framesetter = CTFramesetterCreateWithAttributedString(attributed)
        let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, nil)
        let anyArray: [AnyObject] = CTFrameGetLines(frame) as [AnyObject]
        let lines = anyArray as! [CTLine]
        for line in lines {
            var ascent = CGFloat()
            var descent = CGFloat()
            var leading = CGFloat()
            CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
            height += ceil(ascent + descent + leading)
        }
        return height
    }
}

sample image
rotate tableView

CTLineGetTypographicBounds获取的高度好像没有体现CTParagraphStyle的设置值。 相反,使用 CTFramesetterSuggestFrameSizeWithConstraints 是可行的。

func heightOfCoreText() -> CGFloat {
    // initialize height and attributed
    height = CGFloat()
    attributed = text.attributedStringWithRuby()

    // MEMO: height = CGFloat.greatestFiniteMagnitude
    let textDrawRect = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude)
    let framesetter = CTFramesetterCreateWithAttributedString(attributed)
    let frameSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, attributed.length), nil, textDrawRect.size, nil)
    height = frameSize.height
    return height
}

最小行距 = 10.0
lineHeightMultiple = 1.5