iOS Swift 如何实现文字竖直/垂直展示
经过多番尝试, 使用NSMutableAttributedString搭配CoreText来实现较为简单和灵活。
如果想要试用UILabel来做,通过限制label宽度,每一个字符换一行来实现的话,哪怕是暂时实现了效果, 也终是害人害己,先看看效果图:

做法:
```swift
let text = "我总想要穿越人海和潮流\n但穿越不了的是你我的鸿沟\nso this way \n 终究还是要自己走\n……\n嗯呀"
let style = NSMutableParagraphStyle.init()
style.lineSpacing = 10
//这个对齐方式,对竖直排版同样有效,左对齐对应竖直排版的头部对齐,右对齐对应尾部对齐
style.alignment = .right
style.firstLineHeadIndent = 2
let number1 = NSNumber.init(value: NSWritingDirection.rightToLeft.rawValue)
let number2 = NSNumber.init(value: NSWritingDirectionFormatType.embedding.rawValue)
let attriStr = NSMutableAttributedString.init(string: text, attributes: [.foregroundColor: UIColor.white,.font:UIFont.exoBoldFont(size: 18), .verticalGlyphForm: true,.writingDirection:[number1,number2],.paragraphStyle:style])
let ttLable = TestTextLabel()
ttLable.attributeStr = attriStr
self.view.addSubview(ttLable)
ttLable.sizeToFit()
ttLable.snp.makeConstraints { (make) in
make.center.equalToSuperview()
make.width.equalTo(400)
make.height.equalTo(400)
}
//自定义一个label 继承UIView
class TestTextLabel: UIView {
var attributeStr:NSMutableAttributedString?
var contentSize:CGSize = .zero
overrideinit(frame:CGRect) {
super.init(frame: frame)
self.backgroundColor = .black
}
requiredinit?(coder:NSCoder) {
fatalError("init(coder:) has not been implemented")
}
overridefuncdraw(_rect:CGRect) {
super.draw(rect)
let context = UIGraphicsGetCurrentContext()
context?.textMatrix= .identity
//CFAttributedString
letattrString =self.attributeStr!asCFAttributedString
let framesetter = CTFramesetterCreateWithAttributedString(attrString)
//作用范围
letrangeAll =CFRangeMake(0,self.attributeStr?.length??0)
varfitRange:CFRange= .init()
//获取适应的尺寸。竖直展示这里设置height为无穷大,width为0,意思就是,高度可以无限长,宽度由AttributedString的文字决定, 如果想要设置为横着展示,则width设置为无穷大, height设置为0
lets =CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeAll,nil,CGSize.init(width:0, height:CGFloat(MAXFLOAT)), &fitRange)
//coretext的原点坐标(0,0)在左下角,UIKit的原点坐标(0,0)系在左上角,这里要把UIKit的坐标转为
context?.translateBy(x:0, y:self.height)
//翻转矩阵, 把画面翻转过来, 如果不设置这个,那么出现的效果就好像是照镜子一样,可以自行尝试
context?.scaleBy(x:1, y:-1)
//设置坐标 L 形坐标系, 因为原本是横着展示的,现在要竖直展示,所以需要交换s的宽高, s.width实际上现在变成了文字的高度
letframeRect =CGRect.init(x: (self.width-s.height)/2.0, y: (self.height-s.width)/2.0, width: s.height, height: s.width)
//笔画路径
letframePath:CGPath=CGPath.init(rect: frameRect, transform:nil)
//.leftToRight 竖直状态,从左边往右读,.rightToLeft从右往左读,.topToBottom 横着的状态
/*如果是竖直的状态, 需要attributeString 也设置为竖直的形式
var attriStr = NSMutableAttributedString.init(string: text, attributes: [.foregroundColor: UIColor.red,.font:UIFont.exoBoldFont(size: 14), .verticalGlyphForm: true])
也就是这句代码:.verticalGlyphForm: true
*/
letprogressionNumber =NSNumber.init(value:CTFrameProgression.rightToLeft.rawValue)
// CTFrameProgression 一定要先转成number类型,否则会crash
letdic = [kCTFrameProgressionAttributeName:progressionNumberasCFNumber]
letframe =CTFramesetterCreateFrame(framesetter, rangeAll, framePath, dicasCFDictionary)
CTFrameDraw(frame, context!)
}
}
```
效果图:

