CATextLayer结合NSParagraphStyle时渲染与截断异常问题求助
嘿,我仔细扒了你的代码,发现几个核心问题导致了这些诡异的渲染和计算异常,咱们一步步来解决:
1. NSParagraphStyle根本没被应用到富文本中!
你在text属性的setter里创建了NSMutableParagraphStyle,设置了对齐和换行模式,但完全没把它加入到富文本的attributes字典里!这就难怪段落样式完全不起作用——你辛辛苦苦配置的样式根本没跟着文本一起传给CATextLayer。
修改text的setter,把paragraphStyle加到attributes里:
set { if let value = newValue { let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.alignment = self.textAlignment paragraphStyle.lineBreakMode = self.lineBreakMode // 关键:把段落样式加入属性字典 self.textLayer.string = NSMutableAttributedString(string: value, attributes: [ .foregroundColor: self.textColor, .font: self.font, .paragraphStyle: paragraphStyle // 新增这一行 ]) } else { self.textLayer.string = nil } }
2. CATextLayer的属性与段落样式冲突,截断/换行失效
你同时在lineBreakMode的setter里修改textLayer.isWrapped和textLayer.truncationMode,但当CATextLayer使用**富文本(NSAttributedString)**时,这些属性会被富文本里的NSParagraphStyle覆盖!所以你之前的操作等于做了无用功,还可能导致冲突。
修改lineBreakMode的setter,只需要更新段落样式并刷新富文本即可,不需要直接修改CATextLayer的属性:
public var lineBreakMode: NSLineBreakMode { get { return _lineBreak } set { _lineBreak = newValue // 只要更新文本的段落样式,然后重新设置text即可触发更新 if let currentText = self.text { self.text = currentText // 触发setter重新生成带新段落样式的富文本 } } }
同理,textAlignment的setter也应该改成这种方式——直接修改CATextLayer的alignmentMode对富文本无效,必须通过更新段落样式来实现。
3. sizeOfTextThatFits总是返回21的原因
这个方法里的CTFramesetter计算逻辑有问题:
- 你没有把正确的段落样式传给CTFramesetter(因为之前富文本里就没包含),导致CTLine的高度计算异常;
- 初始的
yOffset是0,但CTLine的ascent + descent + leading是单行的高度,当文本为空或者样式错误时,可能只算出单行默认高度(刚好21,对应系统字体17号的行高)。
另外,你在layoutSubviews里用boundingRect计算尺寸时,也需要确保传入的富文本包含正确的段落样式。修改后的layoutSubviews可以简化成这样:
internal override func layoutSubviews() { super.layoutSubviews() guard let attrString = self.textLayer.string as? NSAttributedString else { self.textLayer.frame = .zero return } // 用正确的富文本计算尺寸 let rect = attrString.boundingRect(with: self.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil) self.textLayer.contentsScale = UIScreen.main.scale self.textLayer.rasterizationScale = UIScreen.main.scale self.textLayer.frame = CGRect.centerRect(rect, in: self.bounds) self.textLayer.backgroundColor = UIColor.lightGray.cgColor }
最后验证效果
把这些修改做完后,你会发现:
- 段落样式的换行、对齐、截断都正常生效了;
- 文本尺寸计算不再异常;
- 最后一行的间距也会因为正确的段落样式而恢复正常。
内容的提问来源于stack exchange,提问作者Brandon

