UITableViewCell圖片加載優(yōu)化

  • 自定義單元格

import UIKit
import RxSwift
import RxCocoa
 
//單元格類
class MyTableCell: UITableViewCell {
     
    var disposeBag: DisposeBag?
     
    //單元格尾部的活動指示器
    var indicator:UIActivityIndicatorView!
     
    //當(dāng)前顯示的數(shù)據(jù)
    var movieRecord:MovieRecord! {
        didSet{
            let disposeBag = DisposeBag() //保證 cell 被重用的時候不會被多次訂閱
             
            //設(shè)置標(biāo)題
            self.textLabel?.text = movieRecord.name
             
            //先判斷圖片當(dāng)前的狀態(tài)
            if movieRecord.state == .filtered || movieRecord.state == .failed {
                //之前已經(jīng)處理完畢的就直接顯示出來
                self.imageView?.image = movieRecord.image

                // 也可以保存到緩存中,在這里從緩存中拿數(shù)據(jù)
                // let img = QSFileManager.qs_readFromFile(in: .cache, contentType: .data, fileName: movieRecord.name + ".txt")
                // self.imageView?.image = UIImage.init(data: img as! Data)
            }else{
                //只要圖片狀態(tài)是沒處理完畢的,一律先顯示個占位符圖片
                self.imageView?.image = UIImage(named: "placeholder")
                //顯示活動指示器
                self.indicator.startAnimating()
                //訂閱序列
                Observable.of(movieRecord)
                    .delay(0.3, scheduler: MainScheduler.instance) //延遲0.3秒
                    .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))//后臺
                    .flatMap { return self.fetchImage($0) }  //下載電影圖片
                    .flatMap { self.applySepiaFilter($0) } //給圖片添加濾鏡
                    .observeOn(MainScheduler.instance)  //回到主線程顯示圖片
                    .subscribe(onNext: {
                        self.imageView?.image = $0.image
                        self.indicator.stopAnimating()
                    })
                    .disposed(by: disposeBag) //cell離開可視區(qū)域后自動取消訂閱
            }
             
            self.disposeBag = disposeBag
        }
    }
     
    //單元格重用時調(diào)用
    override func prepareForReuse() {
        super.prepareForReuse()
         
        disposeBag = nil //保證 cell 被重用的時候不會被多次訂閱
    }
     
    //初始化
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
         
        //為了提示用戶,將cell的accessory view設(shè)置為UIActivityIndicatorView。
        indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        self.accessoryView = indicator
    }
     
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
     
    //獲取圖片
    func fetchImage(_ movie:MovieRecord) -> Observable<MovieRecord> {
        //如果當(dāng)前的電影條目還未獲取圖片則去獲取圖片
        if movie.state == .new {
            if let imageData = try? Data(contentsOf: movie.url){
                movie.image = UIImage(data:imageData)
                movie.state = .downloaded  //圖片狀態(tài)改成下載成功
            }else{
                movie.image = UIImage(named: "failed")! //未加載到海報顯示默認(rèn)的“暫無圖片”
                movie.state = .failed //圖片狀態(tài)改成失敗
            }
        }
         
        return Observable.of(movie)
    }
     
    //給圖片添加棕褐色濾鏡
    func applySepiaFilter(_ movie:MovieRecord) -> Observable<MovieRecord> {
        //只有剛下載的圖片需要處理
        if movie.state == .downloaded {
            let inputImage = CIImage(data:UIImagePNGRepresentation(movie.image!)!)
            let context = CIContext(options:nil)
            let filter = CIFilter(name:"CISepiaTone")
            filter!.setValue(inputImage, forKey: kCIInputImageKey)
            filter!.setValue(0.8, forKey: "inputIntensity")
            if let outputImage = filter!.outputImage {
                let outImage = context.createCGImage(outputImage, from: outputImage.extent)
                movie.image = UIImage(cgImage: outImage!)
                movie.state = .filtered //圖片狀態(tài)改成添加濾鏡成功
            } else {
                movie.image = UIImage(named: "failed")! //處理失敗的海報顯示默認(rèn)的“暫無圖片”
                movie.state = .failed //圖片狀態(tài)改成失敗
            }

            // 圖片處理完保存到緩存中
            // _ = QSFileManager.qs_writeToFile(in: .cache, data: NSData.init(data: UIImagePNGRepresentation(movie.image!)!) as AnyObject, fileName: movie.name + ".txt")
        }
        return Observable.of(movie)
    }
}
  • QSFileManager

/**
 *  把數(shù)據(jù)寫入文件
 *
 *  @param pathType 放在哪個路徑下,Document或Cache,默認(rèn) unKnown
 *  @param data 要寫入的數(shù)據(jù)
 *  @param fileName 文件名,可以一個文件名,也可以傳一個路徑,如果pathType為unKnown,需要傳入完整路徑
 *
 *  return 是否寫入成功
 */
class func qs_writeToFile(in pathType: QSPathType = .unKnown, data: AnyObject, fileName: String) -> Bool {
    // 獲取路徑
    var path = String.init()
    switch pathType {
    case .document:
        path = self.qs_getDocumentPath()
    case .cache:
        path = self.qs_getCachePath()
    case .unKnown:
        path = fileName
    }
    
    // 文件路徑
    if !fileName.hasPrefix("/") {
        path.append("/")
    }
    let filePath = path + fileName
    
    return data.write(toFile: filePath, atomically: true)
}

/**
 *  讀取文件內(nèi)容
 *
 *  @param pathType 放在哪個路徑下,Document或Cache,默認(rèn) unKnown
 *  @param contentType 文件內(nèi)容的類型,默認(rèn) string
 *  @param fileName 文件名,可以一個文件名,也可以傳一個路徑,如果pathType為unKnown,需要傳入完整路徑
 *
 *  return 返回文件中數(shù)據(jù),拿不到數(shù)據(jù)返回nil
 */
class func qs_readFromFile(in pathType: QSPathType = .unKnown, contentType: QSFileContentType = .string, fileName: String) -> AnyObject? {
    // 獲取路徑
    var path = String.init()
    switch pathType {
    case .document:
        path = self.qs_getDocumentPath()
    case .cache:
        path = self.qs_getCachePath()
    case .unKnown:
        path = fileName
    }
    
    // 文件路徑
    if !fileName.hasPrefix("/") {
        path.append("/")
    }
    let filePath = path + fileName
    
    switch contentType {
    case .array:
        return NSArray.init(contentsOfFile: filePath)
        
    case .dictionary:
        return NSDictionary.init(contentsOfFile: filePath)
        
    case .string:
        return try? String.init(contentsOfFile: filePath, encoding: String.Encoding.utf8) as AnyObject
        
    case .data:
        return try? NSData.init(contentsOfFile: filePath) as AnyObject
    }
}
  • Cell屬性的記錄(MovieRecord)

import UIKit
 
// 這個枚舉包含所有電影圖片的狀態(tài)
enum MovieRecordState {
    case new, downloaded, filtered, failed
}
 
// 電影條目類
class MovieRecord {
    let name:String //電影標(biāo)題
    let url:URL //電影海報圖片地址
    var image:UIImage? //電影海報圖片
     
    var state = MovieRecordState.new //圖片當(dāng)前狀態(tài)
     
    init(name:String, url:URL) {
        self.name = name
        self.url = url
    }
}
  • ViewController

//創(chuàng)建表格視圖
self.tableView = UITableView(frame: self.view.frame, style:.plain)
//創(chuàng)建一個重用的單元格
self.tableView!.register(MyTableCell.self, forCellReuseIdentifier: "Cell")
//單元格無法選中
self.tableView.allowsSelection = false
//設(shè)置單元格高度
self.tableView!.rowHeight = 100
self.view.addSubview(self.tableView!)

//數(shù)據(jù)源地址
let dataSourcePath = Bundle.main.path(forResource: "movies", ofType: "plist")
let datasourceDictionary = NSDictionary(contentsOfFile: dataSourcePath!)

//初始化數(shù)據(jù)
for(key,value) in datasourceDictionary!{
    let name = key as? String
    let url = URL(string:value as? String ?? "")
    if name != nil && url != nil {
        let movieRecord = MovieRecord(name:name!, url:url!)
        self.movies.append(movieRecord)
    }
}

//設(shè)置單元格數(shù)據(jù)(其實就是對 cellForRowAt 的封裝)
Observable.just(movies)
    .bind(to: tableView.rx.items) { (tableView, row, element) in
        //初始化cell
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
            as! MyTableCell
        //設(shè)置單元格數(shù)據(jù)
        cell.movieRecord = element
        //返回單元格
        return cell
    }
    .disposed(by: disposeBag)

參考文章:Swift - RxSwift的使用詳解65(表格圖片加載優(yōu)化)
Swift - 表格圖片加載優(yōu)化(拖動表格時不加載,停止時只加載當(dāng)前頁圖片)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,211評論 3 119
  • 大家好,我叫劉浚睿。今天我要分享我最高興的事情,我最高興的事情就是去新瑪特畫畫。老師先畫一條跳躍的鯉魚,是先畫的魚...
    張會新閱讀 167評論 0 0
  • 早餐做了酸湯水餃,還加了香噴噴的蛋皮絲,喵喵鼻頭唆啊唆,一路從杯架喵嗚著跑到灶臺來,直奔蛋皮。我趕緊一邊把裝著蛋皮...
    Candice橙閱讀 263評論 0 0
  • 國畫之美,乃藝術(shù)之巔、文化之最。 國畫之美,始于凡塵,卻終于升華,意味深長而韻味十足。塵世之間,吸取點滴精華,予以...
    人人為眾閱讀 1,626評論 0 1
  • 怎樣為一個網(wǎng)站制定一個SEO優(yōu)化計劃? 為一個網(wǎng)站制定一個SEO優(yōu)化計劃這是一個運營主管或經(jīng)理都應(yīng)該要做的事情,具...
    李亞濤愛分享閱讀 461評論 0 1

友情鏈接更多精彩內(nèi)容