todo
- [ x ] 自己改造了富文本的属性设置
- [ ] 代码中用到的API后续补上
富文本
存在文字的控件或者组建都可以设置富文本
UILabel, UIButton, UITextView 都可以设置富文本
计算文本Size
Code
import UIKit
enum TextAttributes {
/// 字体
case font(value: UIFont) // UIFont, default Helvetica(Neue) 12
/// 设置段落属性
case paragraphStyle(value: NSParagraphStyle) // NSParagraphStyle, default defaultParagraphStyle
/// 设置前景颜色
case foregroundColor(value: UIColor) // UIColor, default blackColor
/// 设置文本背景颜色
case backgroundColor(value: UIColor) // UIColor, default nil: no background
/// 设置连体属性,取值为NSNumber对象(整数),1表示使用默认的连体字符,0表示不使用,2表示使用所有连体符号(iOS不支持2)。而且并非所有的字符之间都有组合符合。如 fly ,f和l会连起来。
case ligatures(value: Int) // NSNumber containing integer, default 1: default ligatures, 0: no ligatures
/// 设置字距 ,负值间距变窄,正值间距变宽
case kern(value: CFloat) // NSNumber containing floating point value, in points; amount to modify default kerning. 0 means kerning is disabled.
/// 设置删除线
case strikethroughStyle(value: NSUnderlineStyle) // NSNumber containing integer, default 0: no strikethrough
/// 设置下划线样式
case underlineStyle(value: NSUnderlineStyle) // NSNumber containing integer, default 0: no underline
/// 设置笔刷颜色 设置填充部分颜色 设置中间部分颜色可以使用 NSForegroundColorAttributeName 属性来进行
case strokeColor(value: UIColor) // UIColor, default nil: same as foreground color
/// 设置笔画的宽度,负值填充效果,正值是中空效果
case strokeWidth(value: CGFloat) // NSNumber containing floating point value, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0)
/// 设置阴影
case shadow(value: NSShadow) // NSShadow, default nil: no shadow
/// 设置文本特殊效果,目前只有一个可用效果 NSTextEffectLetterpressStyle(凸版印刷效果)
case textEffect(value: String) // NSString, default nil: no text effect NSTextEffectLetterpressStyle
/// 设置文本附件,常用于文字的图文混排
case attachment(value: NSTextAttachment) // NSTextAttachment, default nil
case linkString(value: String) // NSURL (preferred) or NSString
case linkURL(value: URL) // NSURL (preferred) or NSString
/// 设置基线偏移值 正值上偏,负值下偏
case baselineOffset(value: CGFloat) // NSNumber containing floating point value, in points; offset from baseline, default 0
/// 设置下划线颜色
case underlineColor(value: UIColor) // UIColor, default nil: same as foreground color
/// 设置删除线颜色
case strikethroughColor(value: UIColor) // UIColor, default nil: same as foreground color
/// 设置字体倾斜度,正值右倾,负值左倾
case obliqueness(value: CGFloat) // NSNumber containing floating point value; skew to be applied to glyphs, default 0: no skew
/// 设置字体的横向拉伸,取值为NSNumber (float),正值拉伸 ,负值压缩
case expansion(value: CGFloat) // NSNumber containing floating point value; log of expansion factor to be applied to glyphs, default 0: no expansion
/// 设置文字的书写方向
case writingDirection(value: [NSWritingDirection]) // NSArray of NSNumbers representing the nested levels of writing direction overrides as defined by Unicode LRE, RLE, LRO, and RLO characters. The control characters can be obtained by masking NSWritingDirection and NSWritingDirectionFormatType values. LRE: NSWritingDirectionLeftToRight|NSWritingDirectionEmbedding, RLE: NSWritingDirectionRightToLeft|NSWritingDirectionEmbedding, LRO: NSWritingDirectionLeftToRight|NSWritingDirectionOverride, RLO: NSWritingDirectionRightToLeft|NSWritingDirectionOverride,
/// 设置文字排版方向,0表示横排文本,1表示竖排文本 在iOS中只支持0
case verticalGlyphForm(value: Int) // An NSNumber containing an integer value. 0 means horizontal text. 1 indicates vertical text. If not specified, it could follow higher-level vertical orientation settings. Currently on iOS, it's always horizontal. The behavior for any other value is undefined.
}
fileprivate func configureAttributes(attributes: [TextAttributes]?)->[String: Any]? {
guard let attributes = attributes else {
return nil
}
var temp:[String: Any] = [String: Any]()
for attribute in attributes {
switch attribute {
case let .font(value):
temp[NSFontAttributeName] = value
case let .paragraphStyle(value):
temp[NSParagraphStyleAttributeName] = value
case let .foregroundColor(value):
temp[NSForegroundColorAttributeName] = value
case let .backgroundColor(value):
temp[NSBackgroundColorAttributeName] = value
case let .ligatures(value):
temp[NSLigatureAttributeName] = value
case let .kern(value):
temp[NSKernAttributeName] = value
case let .strikethroughStyle(value):
temp[NSStrikethroughStyleAttributeName] = value
case let .underlineStyle(value):
temp[NSUnderlineStyleAttributeName] = value
case let .strokeColor(value):
temp[NSStrokeColorAttributeName] = value
case let .strokeWidth(value):
temp[NSStrokeWidthAttributeName] = value
case let .shadow(value):
temp[NSShadowAttributeName] = value
case .textEffect(_):
temp[NSTextEffectAttributeName] = NSTextEffectLetterpressStyle
case let .attachment(value):
temp[NSAttachmentAttributeName] = value
case let .linkString(value):
temp[NSLinkAttributeName] = value
case let .linkURL(value):
temp[NSLinkAttributeName] = value
case let .baselineOffset(value):
temp[NSBaselineOffsetAttributeName] = value
case let .underlineColor(value):
temp[NSUnderlineColorAttributeName] = value
case let .strikethroughColor(value):
temp[NSStrikethroughColorAttributeName] = value
case let .obliqueness(value):
temp[NSObliquenessAttributeName] = value
case let .expansion(value):
temp[NSExpansionAttributeName] = value
case let .writingDirection(value):
temp[NSWritingDirectionAttributeName] = value
case let .verticalGlyphForm(value):
temp[NSVerticalGlyphFormAttributeName] = value
}
}
return temp
}
extension String {
/// 根据文字属性计算size
///
/// - Parameters:
/// - size: 限定size
/// - attributes: 属性
/// - Returns: 返回新的size
fileprivate func getTextSize(string: String,size: CGSize,attributes: [TextAttributes]?)-> CGRect {
let attribute = configureAttributes(attributes: attributes)
let text = NSString(string: string)
let newSize = text.boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attribute, context: nil)
return newSize
}
/// 根据文字限定的宽度来计算所占高度
///
/// - Parameters:
/// - width: 宽度
/// - attributes: 属性
/// - Returns: 返回的高度
func getTextHeight(width: CGFloat,attributes: [TextAttributes]?) -> CGFloat {
let size = CGSize(width: width, height: 0)
let rect = getTextSize(string: self, size: size, attributes: attributes)
return rect.height
}
/// 根据文字限定的高度来计算所占宽度
///
/// - Parameters:
/// - height: 高度
/// - attributes: 属性
/// - Returns: 返回的宽度
func getTextWidth(height: CGFloat,attributes: [TextAttributes]?) -> CGFloat {
let size = CGSize(width: 0, height: height)
let rect = getTextSize(string: self, size: size, attributes: attributes)
return rect.width
}
/// 转换成不可变富文本
///
/// - Parameter attributes: 富文本属性
/// - Returns: 不可变富文本
func conversion(attributes: [TextAttributes]?) -> NSAttributedString {
let attribute = configureAttributes(attributes: attributes)
let attributedString = NSAttributedString(string: self, attributes: attribute)
return attributedString
}
/// 转换成可变富文本
///
/// - Parameter attributes: 富文本属性
/// - Returns: 可变富文本
func conversionM(attributes: [TextAttributes]?) -> NSMutableAttributedString {
let attribute = configureAttributes(attributes: attributes)
let mutableAttributedString = NSMutableAttributedString(string: self, attributes: attribute)
return mutableAttributedString
}
/// 图文混排
///
/// - Parameters:
/// - image: 图标
/// - bounds: 位置 (0,0,20,20)
/// - string: 文字
/// - textAttributes: 文字属性
/// - Returns: 返回一个可变富文本
func imageWithText(image: UIImage, bounds:CGRect, string: String ,textAttributes: [TextAttributes]?) -> NSMutableAttributedString {
let attachment = NSTextAttachment()
attachment.image = image
attachment.bounds = bounds
let mutableAttributedString = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attachment))
let text:NSAttributedString = string.conversion(attributes: textAttributes)
mutableAttributedString.append(text)
return mutableAttributedString
}
/// 包含字串
///
/// - Parameters:
/// - pattern: 正则表达式
/// - Returns: true yes
func containsMatch(pattern: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let range = NSMakeRange(0, self.characters.count)
return (regex.firstMatch(in: self, options: NSRegularExpression.MatchingOptions.reportCompletion, range: range) != nil)
} catch _ {
fatalError( "rrror")
}
}
/// 高亮
///
/// - Parameters:
/// - pattern: 正则表达式
/// - attributes: 匹配到的字符属性
/// - Returns: 返回新串
func highlightMatches(pattern: String, attributes: [TextAttributes]?) -> NSAttributedString {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let range = NSMakeRange(0, self.characters.count)
let matches = regex.matches(in: self, options: [], range: range)
let attributedText = NSMutableAttributedString(string: self)
let attribute = configureAttributes(attributes: attributes) ?? [:]
for match in matches {
attributedText.addAttributes(attribute, range: match.range)
}
return attributedText.copy() as! NSAttributedString
} catch _ {
fatalError( "字符串匹配错误")
}
}
/// 匹配到的list
///
/// - Parameters:
/// - pattern: 正则表达式
/// - Returns: 返回匹配到的列表
func listMatches(pattern: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let range = NSMakeRange(0, self.characters.count)
let matches = regex.matches(in: self, options: [], range: range)
return matches.map{
let range = $0.range
return NSString(string: self).substring(with: range)
}
} catch _ {
fatalError( "字符串匹配错误")
}
}
func listGroups(pattern: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let range = NSMakeRange(0, self.characters.count)
let matches = regex.matches(in: self, options: [], range: range)
var groupMatches = [String]()
for match in matches {
let rangeCount = match.numberOfRanges
for group in 0..<rangeCount {
let temp = NSString(string: self).substring(with: match.rangeAt(group))
groupMatches.append(temp)
}
}
return groupMatches
} catch _ {
fatalError( "字符串匹配错误")
}
}
/// 替换字符
///
/// - Parameters:
/// - pattern: 正则表达式
/// - replacementString: 替换的字符
/// - Returns: 返回被替换后的字符串
func replaceMatches(pattern: String, withString replacementString: String) -> String? {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let range = NSMakeRange(0, self.characters.count)
return regex.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: replacementString)
} catch _ {
fatalError("字符串匹配错误")
}
}
}