问题
Using the help of @yaslam I've created in Core Text an UILabel that show Japanese text in both horizontal and vertical way with Furigana using CTRubyAnnotation. Unfortunately I've a problem. I need to use this label inside a custom cell and I need that the cell dynamically resize the height of the cell based on text. but don't work. the cell doesn't expands
Can you help me?
Thank you very much
Here's code
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)
}
}
TableView 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
}
this is the result:
回答1:
Using CoreText, the height of the View is not automatically determined.
Calculate drawing height of CoreText and set it to height of UIView in Cell.
Make the following settings for UITableView in storyboard.
* Check Automatic of Row Height
* Check Automatic of Estimate
For programs, it is as follows.
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = UITableViewAutomaticDimension
Sample code to calculate drawing height of CoreText. I don't know whether it is the optimum code for calculating height. Sample code is pretty sloppy so please actually refactor it.
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
It seems that the setting value of CTParagraphStyle is not reflected in the height obtained by CTLineGetTypographicBounds. Instead, using CTFramesetterSuggestFrameSizeWithConstraints works.
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
}
minimumLineSpacing = 10.0
lineHeightMultiple = 1.5
来源:https://stackoverflow.com/questions/47636905/custom-uitableviewcell-with-core-text