1. 程式人生 > >iOS swift 富文字內容顯示

iOS swift 富文字內容顯示

專案中很多地方都會用到富文字的內容:比如一般的商品詳情,視訊詳情,資訊詳情等,運營人員通過後臺的富文字編輯器編輯的內容,前端拿到的就是一段富文字的程式碼,這富文字大多都是圖片和文字的組合。我們今天介紹的RichTextView就是一個用來載入富文字的檢視

富文字要顯示出來可以使用NSAttributedString來載入通過label或者textView來顯示出來,如果只是純文字的話,直接用label和textView顯示出來當然沒什麼問題,可是如果有圖片就很麻煩了,由於是網路圖片,要將圖片和文字佈局起來幾乎很難做到

RichTextView採用的是WebView的方式來載入富文字,將富文本當做一段html程式碼來載入,這樣,當所有在富文字編輯器裡面的css,在webview中都可以生效

我同時也知道,網頁載入內容的時候,如果webView覆蓋整個controller的view,可以直接設定webView的寬高和view的寬高相同,但是如果富文字的網頁只是介面內容的一部分,可以是tableView的tableheaderView或者可以是tableView的某一個cell,那麼我們就不得不面臨一個問題,需要在內容載入完成之後將webView的高度回調回來,同時還要保證webView不會重複被載入

如果webView中有圖片,那麼可以通過JavaScript注入標籤的方式,將圖片的url找出來,然後通過你想要展示的方式展示出來,我這裡使用的是GKPhotoBrowser這個圖片瀏覽器展示的

RichTextView可以完全解決上面的幾個問題

RichTextView介紹

    /// 富文字載入完成後返回高度
    var webHeight: ((_ height: CGFloat)->Void)?

    /// 是否允許圖片點選彈出
    var isShowImage: Bool = true

    /// webView是否可以滾動 預設可以滾動  根據情況設定
    var isScrollEnabled: Bool = true
    
    /// 富文字內容
   var richText: String? 

下面通過3個實際的應用場景來詮釋RichTextView的用法 程式碼全部使用swift4.0編寫 佈局使用snapKit 圖片顯示採用GKPhotoBrowser 彈窗使用MBProgressHUD

場景1: 全覆蓋 富文字佔滿整個螢幕

這種情況下設定富文字的寬高同controller的view的寬高一致,這樣有一個好處就是不用太關心富文字的高度時多少了反正可以鋪滿整個螢幕
一些注意點我歸納總結一下:

  • 讓RichText isScrollEnabled設定為true 這樣能夠保證RichText能夠在整個螢幕滾動
  • WebView載入富文字需要時間並不是瞬間就可以載入完成,所以當開始載入的時候可以設定一個loadingView作為遮罩,當載入完成之後移除遮罩,這樣可以提升使用者體驗
    程式碼部分:
import UIKit

class RichTextDemo1VC: UIViewController{
        
    var html: String!
    
    var loadingView: LoadingView = {
        let loadingView = LoadingView()
        return loadingView
    }()
    
    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        html = "<p><img src=\"http://oss.hxquan.cn/bd/e0-27817083505076241.jpg\" title=\"迪麗熱巴banner沒有LOGO.jpg\" alt=\"迪麗熱巴banner沒有LOGO.jpg\"/></p><p>在2018年農曆新年來臨之際</p><p>火星圈&amp;Dear迪麗熱巴後援會相約深圳進行春節探訪</p><p>活動現場捐贈了製氧機、助行器、北京老布鞋、手絹套裝等急需且貼心的物資和禮物;</p><p>和老人們在一起聊天、活動度過了愉快的時光</p><p>阿達飛奔來送上圖片集錦~~</p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173692906635673.jpg\" title=\"6.jpg\" alt=\"6.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173702920452585.jpg\" title=\"1.jpg\" alt=\"1.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173712152970070.jpg\" title=\"2.jpg\" alt=\"2.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173720437853217.jpg\" title=\"3.jpg\" alt=\"3.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173731212434044.jpg\" title=\"4.jpg\" alt=\"4.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173737211482094.jpg\" title=\"5.jpg\" alt=\"5.jpg\"/></p><p><br/></p><p><br/></p><p><br/></p><p style=\"text-align: center;\"><img src=\"http://oss.hxquan.cn/bd/e0-27817539871407219.png\" title=\"可愛.png\" alt=\"可愛.png\"/></p>"
        
        demo1()
    }
    
    private func demo1(){
        //第一種情況 全覆蓋 富文字佔滿整個螢幕
        /// 載入網頁
        
        let richTextView = RichTextView(frame: view.bounds, fromVC: self)
        view.addSubview(richTextView)
        richTextView.webHeight = {[unowned self] height in
            self.loadingView .removeFromSuperview()
        }
        
        richTextView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
        richTextView.richText = html
        
        //載入loadingView
        view.addSubview(loadingView)
        loadingView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
    }
    
    deinit {
        print("RichTextDemo1VC dealloc")
    }
    
}

場景2: 富文字作為TableView的cell

把RichText作為TableViewcell的一部分,RichText做了處理防止TableView滾動過程中webView自動load,RichText會在webView載入完成後將RichText高度回調出來,將高度快取起來,這樣就不用擔心cell來回重新整理導致效能不行的問題了
一些注意點我歸納總結一下:

  • 讓RichText isScrollEnabled設定為false 這樣防止cell滾動過程中RichText還在滾動導致異常卡頓的情況
  • 將RichText回撥的高度快取起來
  • 注意不要造成迴圈引用
  • WebView載入富文字需要時間並不是瞬間就可以載入完成,所以當開始載入的時候可以設定一個loadingView作為遮罩,當載入完成之後移除遮罩,這樣可以提升使用者體驗

程式碼部分:


import UIKit

class RichTextDemo2VC: UIViewController{
    
    private var richTextView: RichTextView!
    
    private var html: String!
    
    private var webHeight: CGFloat = 0
    
    private var loadingView: LoadingView = {
        let loadingView = LoadingView()
        return loadingView
    }()
    
    lazy var tableView: UITableView = {
        let tableView = UITableView(frame: .zero, style: .plain)
        tableView.showsVerticalScrollIndicator = false
        tableView.showsHorizontalScrollIndicator = false
        tableView.delegate = self
        tableView.dataSource = self
        if #available(iOS 11.0, *) {
            tableView.contentInsetAdjustmentBehavior = .never
        }
        tableView.estimatedSectionHeaderHeight = 0
        tableView.estimatedSectionFooterHeight = 0
        
        tableView.tableFooterView = UIView()
        
        tableView.rowHeight = UITableView.automaticDimension;
        tableView.estimatedRowHeight = 44;
        return tableView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white

        html = "<p><img src=\"http://oss.hxquan.cn/bd/e0-27817083505076241.jpg\" title=\"迪麗熱巴banner沒有LOGO.jpg\" alt=\"迪麗熱巴banner沒有LOGO.jpg\"/></p><p>在2018年農曆新年來臨之際</p><p>火星圈&amp;Dear迪麗熱巴後援會相約深圳進行春節探訪</p><p>活動現場捐贈了製氧機、助行器、北京老布鞋、手絹套裝等急需且貼心的物資和禮物;</p><p>和老人們在一起聊天、活動度過了愉快的時光</p><p>阿達飛奔來送上圖片集錦~~</p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173692906635673.jpg\" title=\"6.jpg\" alt=\"6.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173702920452585.jpg\" title=\"1.jpg\" alt=\"1.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173712152970070.jpg\" title=\"2.jpg\" alt=\"2.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173720437853217.jpg\" title=\"3.jpg\" alt=\"3.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173731212434044.jpg\" title=\"4.jpg\" alt=\"4.jpg\"/></p><p><img src=\"http://oss.hxquan.cn/bd/e0-28173737211482094.jpg\" title=\"5.jpg\" alt=\"5.jpg\"/></p><p><br/></p><p><br/></p><p><br/></p><p style=\"text-align: center;\"><img src=\"http://oss.hxquan.cn/bd/e0-27817539871407219.png\" title=\"可愛.png\" alt=\"可愛.png\"/></p>"
        
        demo2()
    }
    
    private func demo2(){
        
        //第二種情況 富文字作為cell的一部分
        view.addSubview(tableView)
        //設定tableView約束 安全區域
        tableView.snp.makeConstraints { (make) in
            if #available(iOS 11.0, *) {
                make.edges.equalTo(self.view.safeAreaLayoutGuide.snp.edges)
            }else{
                make.edges.equalToSuperview()
            }
        }
        //由於webView載入需要時間 所以可以在webView載入期間 在介面設定一個loadingView遮擋 當webview載入完無論成功或者失敗都會在回撥方法中關閉所謂的遮罩,這樣可能會給使用者一個更好的使用體驗
        view.addSubview(loadingView)
        loadingView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
    }
    
    deinit {
        print("RichTextDemo2VC dealloc")
    }
}


extension RichTextDemo2VC: UITableViewDelegate,UITableViewDataSource{
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.row == 0{
            var cell = tableView.dequeueReusableCell(withIdentifier: "customCell")
            if cell == nil {
                cell = UITableViewCell(style: .default, reuseIdentifier: "customCell")
                cell?.selectionStyle = .none
                let richTextView = RichTextView(frame: .zero, fromVC: self)
                richTextView.webHeight = { [unowned self] height in
                    self.webHeight = height
                    self.loadingView .removeFromSuperview()
                    self.tableView.reloadData()
                }
                //放在cell中不要讓webView滾動
                richTextView.isScrollEnabled = false
                richTextView.richText = html
                cell?.contentView.addSubview(richTextView)
                richTextView.snp.makeConstraints { (make) in
                    make.edges.equalToSuperview()
                }
            }
            return cell!
        }
        
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
            cell?.selectionStyle = .none
        }
        cell?.textLabel?.text = "jkdlsfkjsld"
        return cell!
    }
    
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.row == 0 {
            return self.webHeight
        }
        return UITableView.automaticDimension
    }
    
}

場景2: 富文字作為TableView的tableheaderView的一部分

將富文字作為TableViewHeaderView的一部分,這種場景的使用頻率特別高,典型的像今日頭條資訊詳情部分,像一般商場app商品詳情部分大都是在TableView的TableHeaderView中巢狀webView
一些注意點我歸納總結一下:

  • RichText載入完成後需要重試header的高度,並且重設self.tableView.tableHeaderView = self.headerView
  • 設定TableView的header高度部分我這裡用的是利用約束自適應高度,如果你也是這種方法設定的,請注意設定完約束後一定要呼叫一次self.headerView.layoutIfNeeded()方法讓高度生效
  • RichText嵌入到Header中,RichText原本高度預設設定為0,只有當RichText載入ok才會通過約束設定RichText的真實高度,header傳遞出去的高度應該是RichText的高度+header中其它控制元件的高度
        richView.webHeight = { [unowned self] height in
            
            // 將高度傳遞出去 RichText高度+header中其它控制元件高度
            self.headerHeight?(self.height+height)
        }
  • RichText本身帶有防止webview重複載入的,所以不用擔心效能問題
  • WebView載入富文字需要時間並不是瞬間就可以載入完成,所以當開始載入的時候可以設定一個loadingView作為遮罩,當載入完成之後移除遮罩,這樣可以提升使用者體驗

demo下載