zoukankan      html  css  js  c++  java
  • Core Text 实现富文本显示

    简介:

    Core Text主要用来对文本进行排版布局和字体处理,与其他UI组件相比,由于它直接与Quartz交互,因此排版效率高,渲染速度快。

    下图是Core Text的架构图:

    富文本实现:(GitHub传送门

    接下来我们通过一个例子来看看如何实现富文本展示。我们按职责将功能拆分成几个类来完成,

    1.一个显示的类,CTDisplayView,仅负责显示。

    import UIKit
    
    class CTDisplayView: UIView {
    
        /*
        // Only override draw() if you perform custom drawing.
        // An empty implementation adversely affects performance during animation.
        override func draw(_ rect: CGRect) {
            // Drawing code
        }
        */
        var data:CoreTextData?
        
        override func draw(_ rect: CGRect) {
            let context = UIGraphicsGetCurrentContext()
            
            //翻转坐标系
            context?.textMatrix = CGAffineTransform.identity
            context?.translateBy(x: 0, y: self.bounds.size.height)
            context?.scaleBy(x: 1, y: -1)
            
            if data != nil {
                CTFrameDraw((self.data?.ctFrame)!, context!)
            }
        }
    
    }

    2.一个配置类,CTFrameParserConfig,负责配置一些默认的参数。

    import UIKit
    
    class CTFrameParserConfig: NSObject {
        var CGFloat = 200.0
        var fontName: NSString = "PingFangSC-Regular"
        var fontSize: CGFloat = 17.0
        var lineSpace: CGFloat = 0.0
        var textColor = ColorRGBA(r: 0, g: 0, b: 0, a: 1)
    }

    3.一个排版类,CTFrameParser,负责实现内容的排版。

    class CTFrameParser: NSObject {
        
        // MARK: ------处理模板文件并返回数据源------
        class func parserTemplateFlie(_ path: NSString, config: CTFrameParserConfig) -> CoreTextData {
            let content = self.loadTemplateFile(path, config: config)
            return self.parserAttributedContent(content, config: config)
        }
        
        // MARK: ------加载模板文件------
        class func loadTemplateFile(_ path: NSString, config: CTFrameParserConfig) -> NSAttributedString {
            let data = NSData(contentsOfFile: path as String)
            let result = NSMutableAttributedString()
            if (data != nil) {
                let array:NSArray = try! JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
                if array.isKind(of: NSArray.self) {
                    for dic in array {
                        if let dict = dic as? NSDictionary {
                            let type = dict["type"]
                            if ((type as! NSString).isEqual(to:"txt")) {
                                let attributedStr = self.parserAttributedContentConvert(dict, config: config)
                                result.append(attributedStr)
                            }
                        }
                    }
                }
            }
            return result
        }
        
        // MARK: ------设置文字模板描述------
        class func parserAttributedContentConvert(_ dict: NSDictionary, config: CTFrameParserConfig) -> NSAttributedString {
            let attributes:NSMutableDictionary = self.attributes(config) as! NSMutableDictionary
            
            //colorConvert
            let colorStr = (dict["color"]) as? String
            if (colorStr != nil && colorStr != "default") {
                let hexValue = Int(strtoul(colorStr, nil, 16))
                let color = ColorHEX(hexValue: hexValue)
                attributes.setObject(color, forKey: kCTForegroundColorAttributeName as! NSCopying)
            }
            
            //fontConvert
            var fontName = dict["fontName"] as? String
            var fontSize = dict["fontSize"] as? CGFloat
            if (fontName == nil || fontName == "default") {
               fontName = config.fontName as String
            }
            if (fontSize == nil || fontSize! <= 0) {
               fontSize = config.fontSize
            }
            let font = CTFontCreateWithName(fontName as CFString?, fontSize!, nil)
            attributes.setObject(font, forKey: kCTFontAttributeName as! NSCopying)
        
            let content = dict["content"] as! NSString
            
            return NSAttributedString(string: content as String, attributes: attributes.copy() as? [String : Any])
        }
    
        // MARK: ------设置初始化描述------
        class func attributes(_ config: CTFrameParserConfig) -> NSDictionary {
            //设置字体
            let fontName = config.fontName
            let fontSize = config.fontSize
            let fontRef = CTFontCreateWithName(fontName as CFString?, fontSize, nil)
            
            //设置行间距
            var lineSpace = config.lineSpace
            let settings: [CTParagraphStyleSetting] = [CTParagraphStyleSetting(spec: CTParagraphStyleSpecifier.lineSpacingAdjustment, valueSize: MemoryLayout<CGFloat>.size, value: &lineSpace), CTParagraphStyleSetting(spec: CTParagraphStyleSpecifier.maximumLineSpacing, valueSize: MemoryLayout<CGFloat>.size, value: &lineSpace), CTParagraphStyleSetting(spec: CTParagraphStyleSpecifier.minimumLineSpacing, valueSize: MemoryLayout<CGFloat>.size, value: &lineSpace)]
            let theParagaraphRef = CTParagraphStyleCreate(settings, 3)
            
            //设置字体颜色
            let textColor = config.textColor
            
            let dict = NSMutableDictionary()
            dict.setObject(fontRef, forKey: kCTFontAttributeName as! NSCopying)
            dict.setObject(theParagaraphRef, forKey: kCTParagraphStyleAttributeName as! NSCopying)
            dict.setObject(textColor.cgColor, forKey: kCTForegroundColorAttributeName as! NSCopying)
            
            return dict
        }
        
        // MARK: ------获取数据源------
        class func parserAttributedContent(_ content: NSAttributedString, config: CTFrameParserConfig) -> CoreTextData {
            let frameSetter = CTFramesetterCreateWithAttributedString(content)
            let restrictSize = CGSize( config.width, height: CGFloat(MAXFLOAT))
            let coretextSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0, 0), nil, restrictSize, nil)
            let textHeight = coretextSize.height
            
            let frame = self.createFrame(frameSetter, config: config, height: textHeight)
            
            let data = CoreTextData(ctFrame: frame, height: textHeight)
            
            return data
        }
        
        // MARK: ------创建frame------
        class func createFrame(_ frameSetter: CTFramesetter, config: CTFrameParserConfig, height: CGFloat) -> CTFrame {
            let path = CGMutablePath()
            path.addRect(CGRect(x: 0, y: 0,  config.width, height: height))
            
            let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
            
            return frame
        }
    
    }

    4.一个数据源,CoreTextData,负责提供显示类的所需数据。

    class CoreTextData: NSObject {
        var ctFrame: CTFrame?
        var height:CGFloat = 0
        
        init(ctFrame: CTFrame, height: CGFloat) {
            self.ctFrame = ctFrame
            self.height = height
        }
    }

    此外,我们需要通过一个模板文件来自定义需要展示的文本的信息,参考如下:

     [
      {
      "type": "txt",
      "content": "This is a ",
      "fontName": "default",
      "fontSize": 0,
      "color": "default"
      },
      {
      "type": "txt",
      "content": "simple attributeString ",
      "fontName": "Snell Roundhand Bold",
      "fontSize": 26,
      "color": "0xe7292e"
      },
      {
      "type": "txt",
      "content": "demo, ",
      "fontName": "PingFangSC-Light",
      "fontSize": 18,
      "color": "0xf9f9f9"
      },
      {
      "type": "txt",
      "content": "you can use it ",
      "fontName": "PingFangSC-Bold",
      "fontSize": 13,
      "color": "0x85d64d"
      },
      {
      "type": "txt",
      "content": "by adding a json template.",
      "fontName": "Papyrus",
      "fontSize": 20,
      "color": "0x333333"
      }
      ]

    然后我们去控制器里面,把displayView添加进来,完成必要的配置,

    override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            
            let ctView = CTDisplayView()
            view.addSubview(ctView)
            
            ctView.backgroundColor = UIColor.gray
            ctView.setX(view.x())
            ctView.setY(view.y()+64)
            ctView.setWidth( view.width())
            
            let config:CTFrameParserConfig = CTFrameParserConfig()
            config.width = ctView.width()
            config.lineSpace = 10
            
            let path = Bundle.main.path(forResource: "template", ofType: "json")
            let data = CTFrameParser.parserTemplateFlie(path! as NSString , config: config)
            ctView.data = data
            ctView.setHeight(height: data.height)
            
        }

    最后的实现效果如下:

  • 相关阅读:
    SpringBoot2.0之一 新建项目helloWorld
    spring boot 的maven设置阿里云仓库
    新建SpringBoot项目运行页面报错Whitelabel Error Page This application has no explicit mapping for /error, so yo
    SpringBoot2.0 最简单的 idea 快速创建项目
    postgresql 按日期范围查询
    postgreSQL 应用case when的例子
    PostgreSQL 数据库NULL值的默认排序行为与查询、索引定义规范
    ASP.NET中在不同的子域中共享Session
    YKCW6-BPFPF-BT8C9-7DCTH-QXGWCYQ7PR-QTHDM-HCBCV-9GKGG-TB2TM
    Asp.net中基于Forms验证的角色验证授权
  • 原文地址:https://www.cnblogs.com/jadonblog/p/7054369.html
Copyright © 2011-2022 走看看