图片的异步下载—原生与第三方库

直接下载并缓存图片

为了了解新知识,下面的代码分为没有进行封装过的版本和封装过得版本

未封装版
  首先需要一个数据模型,数据模型建立需要的属性在编写代码的时候按需要具体添加

  • dataModel
import UIKit

class DataModel: NSObject {

    var name = ""
    var icon = ""
    var download = ""
    
    init(dict:NSDictionary){
        super.init()
        self.name = dict["name"] as! String
        self.icon = dict["icon"] as! String
        self.download = dict["download"] as! String
    }
}
  • ViewController
    其中的cell是通过Xib自定义的


    图片的异步下载—原生与第三方库_第1张图片
    cell
import UIKit

///图片缓存目录
let imageCache_Path = NSHomeDirectory() + "/Library/Caches/imageCache"

class ViewController: UITableViewController {

    //MARK: - 属性
    //1.数据源数组
    lazy  var dataArray:[DataModel] = {
        return self.getData()
    }()
    
    //2.队列任务
    lazy var queue:NSOperationQueue = {
        var tQueue = NSOperationQueue()
        //设置最大并发数
        tQueue.maxConcurrentOperationCount = 3
        return tQueue
    }()
    
    //3.图片缓存
    //NSCache是一种和字典一样的容器。通过键值对的形式去存储数据。和字典相比,NSCache在程序接收到内存警告的时候会自动删除自己存储的数据
    lazy var imageCach:NSCache = {
        return NSCache()
    }()
    
    //4.任务缓存
    lazy var oprationCache:NSMutableDictionary = {
        return NSMutableDictionary()
    }()
    
    //MARK: - 生命周期
    override func viewDidLoad() {
        super.viewDidLoad()
        //注册cell
       self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
        //设置行高
        self.tableView.rowHeight = 120
        
        //在沙盒目录下创建缓存图片的文件夹
        //在制定目录下创建一个文件夹
        //参数1:需要创建文件夹的目录
        //参数2:是否创建中间目录(保险起见填true)
        //参数3:文件夹的属性(nil -> 默认属性)
        do{
            try NSFileManager.defaultManager().createDirectoryAtPath(imageCache_Path, withIntermediateDirectories: true, attributes: nil)
        }catch{
            print("文件夹已经存在")
        }
    }
    
    //MARK: - 内存警告的时候自动调用
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        //删除缓存
        self.oprationCache.removeAllObjects()//删除字典里面的数据(不一定删除完)
    }
}

extension ViewController{
    func getData() -> [DataModel]{
        var tempArray = [DataModel]()
        //1.获取plist路径
        let path = NSBundle.mainBundle().pathForResource("model.plist", ofType: nil)
        //2.拿到plist文件中的数组
        let plistArray = NSArray(contentsOfFile: path!)
        //3.遍历数组拿到所有字典
        for item in plistArray!{
            let dict = item as! NSDictionary
            //4.根据字典创建对应的字典模型
            let model = DataModel(dict: dict)
            //5.将模型保存到数组中
            tempArray.append(model)
        }
        return tempArray
    }
}

//MARK: - 协议方法
extension ViewController{
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataArray.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //1.创建cell
        let cell = tableView.dequeueReusableCellWithIdentifier("cell",forIndexPath: indexPath) as! TableViewCell
        //2.刷新数据
        let model = self.dataArray[indexPath.row]
        cell.nameLabel.text = model.name
        cell.downloadLabel.text = model.download
        
        //a.判断程序缓存中是否已经有了对应的图片
        if self.imageCach.objectForKey(model.icon) != nil{
            cell.iconImageView.image = self.imageCach.objectForKey(model.icon) as? UIImage
            return cell
        }else{
            
            //b.查看是否有本地缓存
            //获取当前cell对应数据中的图片名字
            let fielName = (model.icon as NSString).lastPathComponent
            //拼接文件对应的路径
            let path = imageCache_Path + "/" + fielName
            //获取本地文件
            let data = NSData(contentsOfFile: path)
            //判断是否找到了对应的本地图片
            if data != nil{
                let image = UIImage(data: data!)
                //保存到程序的缓存中
                self.imageCach.setObject(image!, forKey: model.icon)
                //显示图片
                cell.iconImageView.image = image
                //print("获取本地数据")
                return cell
            }
            
            //c.如果本地缓存没有再做网络请求
            //图片没有加载出来就显示站位图
            cell.iconImageView.image = UIImage(named: "user_default.png")
            //异步下载图片
            self.downloadImage(indexPath)
        }
        
        //3.返回cell
        return cell
    }
}

//MARK: - 图片异步下载
extension ViewController{
    
    func downloadImage(indexPath:NSIndexPath){
        
        let model = self.dataArray[indexPath.row]
        
        //1.!!!判断当前下载任务是否已经创建过!!!(避免网络不好的情况下重复创建同一个下载任务)
        //如果任务缓存里面已经有了这个下载任务就直接返回。
        if self.oprationCache.objectForKey(model.icon) != nil{
            return
        }
        
        //2.异步下载图片
        //注意:只要是网络上的图片就必须异步加载
        let operation = NSBlockOperation {
            let data = NSData(contentsOfURL: NSURL(string: model.icon)!)
            //避免网络不好下载不了图片,在强制解包导致程序崩溃
            if data == nil{
                print("网络错误")
                return
            }
            
            let image = UIImage(data: data!)//强制解包
            
            //3.将数据保存到程序的缓存中
            //将model的icon作为Key,image作为值存到imageCache中
            self.imageCach.setObject(image!, forKey: model.icon)
            
            //4.将数据保存到本地!!!
            //print(NSHomeDirectory())
            //将二进制数据写入指定的文件中
            //参数1:文件路径
            //参数2:是否进行原子操作(是否进行异步进行写操作)
            //获取icon的最后一个部分作为文件名
            let iconStr = model.icon as NSString
            let lastStr = iconStr.lastPathComponent
            data?.writeToFile(imageCache_Path + "/" + lastStr, atomically: false)
            
            //5.回到主线程
            NSOperationQueue.mainQueue().addOperationWithBlock({
                //刷新cell
                self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
            })
        }
        self.queue.addOperation(operation)
        //6.将任务保存到缓存中
        self.oprationCache.setObject(operation, forKey: model.icon)
    }
}

封装版
  封装版用的数据模型DataModel以及cell和未封装版是一样的,这里不重复放出,封装版也就是写了系统自带的UIImageView类的扩展,给它增加一个方法来设置图片(仿照OC第三方库SDWebImage,下文讲解),这个方法的作用是:在需要设置图片的时候,先判断设置所需要的图片是否在本地有缓存,如果有就直接使用,如果没有就异步下载图片并且缓存图片,在下载成功之前用默认图片顶替

  • ViewController
import UIKit

class ViewController: UITableViewController {

    //MARK: - 属性
    //1.数据源数组
    lazy  var dataArray:[DataModel] = {
        return self.getData()
    }()
    
    //MARK: - 生命周期
    override func viewDidLoad() {
        super.viewDidLoad()
        //注册cell
       self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
        //设置行高
        self.tableView.rowHeight = 120
    }
}

extension ViewController{
    func getData() -> [DataModel]{
        var tempArray = [DataModel]()
        //1.获取plist路径
        let path = NSBundle.mainBundle().pathForResource("model.plist", ofType: nil)
        //2.拿到plist文件中的数组
        let plistArray = NSArray(contentsOfFile: path!)
        //3.遍历数组拿到所有字典
        for item in plistArray!{
            let dict = item as! NSDictionary
            //4.根据字典创建对应的字典模型
            let model = DataModel(dict: dict)
            //5.将模型保存到数组中
            tempArray.append(model)
        }
        return tempArray
    }
}

//MARK: - 协议方法
extension ViewController{
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataArray.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //1.创建cell
        let cell = tableView.dequeueReusableCellWithIdentifier("cell",forIndexPath: indexPath) as! TableViewCell
        
        //2.刷新数据
        let model = self.dataArray[indexPath.row]
        cell.nameLabel.text = model.name
        cell.downloadLabel.text = model.download
        
        cell.iconImageView?.ZGJ_setImage(model.icon, placeholderImageName: "user_default.png")
        
        //3.返回cell
        return cell
    }
}
  • UIImageView类的扩展
import UIKit

///图片缓存目录
let imageCache_Path = NSHomeDirectory() + "/Library/Caches/imageCache"

extension UIImageView{
    func ZGJ_setImage(urlStr:String,placeholderImageName:String){
        
        //判断当前这个图片是否有本地缓存
        let fileName = (urlStr as NSString).lastPathComponent
        let getData = NSData(contentsOfFile: imageCache_Path + "/" + fileName)
        if getData != nil{
            self.image = UIImage(data: getData!)
            return
        }else{
            self.image = UIImage(named: placeholderImageName)
        }
        
        //创建缓存图片的文件夹
        do{
            try NSFileManager.defaultManager().createDirectoryAtPath(imageCache_Path, withIntermediateDirectories: true, attributes: nil)
        }catch{}
        
        //1.异步下载图片
        dispatch_async(dispatch_get_global_queue(0, 0)) {
            let data = NSData(contentsOfURL: NSURL(string: urlStr)!)
            if data == nil{
                print("网络错误")
                return
            }
            let image = UIImage(data: data!)
            
            //2.将图片保存到本地
            data?.writeToFile(imageCache_Path + "/" + fileName, atomically: true)
            
            //3.回到主线程显示图片
            dispatch_async(dispatch_get_main_queue(), {
                self.image = image
            })
        }
    }
}

使用第三方库下载图片

下载图片经常使用的第三方库有OC版本的SDWebImage和Swift版本的Kingfisher,在这里只介绍利用第三方库下载图片的相关方法,如何使用第三方库请跳转http://www.jianshu.com/p/734f7b1280f3

使用OC第三方库SDWebImage异步下载图片

GET
使用SDWebImage下载图片的时候会自动缓存到本地,不用自己写

例子中的DataModel及cell与上面的一样

import UIKit

//在swift中使用OC第三方库
//1.将第三方库文件拖到工程中
//2.创建桥接文件
//a.通过新建文件创建一个.h文件(命名规则:xxx-Briding-Header)
//b.在桥接文件中将需要使用的头文件通过“#import”包含进去
//c.设置工程文件

class ViewController: UITableViewController {

    //MARK: - 属性
    //1.数据源数组
    lazy  var dataArray:[DataModel] = {
        return self.getData()
    }()
    
    //MARK: - 生命周期
    override func viewDidLoad() {
        super.viewDidLoad()
        //注册cell
       self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
        //设置行高
        self.tableView.rowHeight = 120
    }
}

extension ViewController{
    func getData() -> [DataModel]{
        var tempArray = [DataModel]()
        //1.获取plist路径
        let path = NSBundle.mainBundle().pathForResource("model.plist", ofType: nil)
        //2.拿到plist文件中的数组
        let plistArray = NSArray(contentsOfFile: path!)
        //3.遍历数组拿到所有字典
        for item in plistArray!{
            let dict = item as! NSDictionary
            //4.根据字典创建对应的字典模型
            let model = DataModel(dict: dict)
            //5.将模型保存到数组中
            tempArray.append(model)
        }
        return tempArray
    }
}

//MARK: - 协议方法
extension ViewController{
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataArray.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //1.创建cell
        let cell = tableView.dequeueReusableCellWithIdentifier("cell",forIndexPath: indexPath) as! TableViewCell
        //2.刷新数据
        let model = self.dataArray[indexPath.row]
        cell.nameLabel.text = model.name
        cell.downloadLabel.text = model.download
        
        //通过SDWebImage做图片的异步下载和缓存
        //参数1:图片网络路径对应的url
        //参数2:占位图
        //老版
        //cell.iconImageView.setImageWithURL(NSURL(string: model.icon), placeholderImage: UIImage(named: "user_default.png"))
        //新版
        cell.iconImageView.sd_setImageWithURL(NSURL(string: model.icon), placeholderImage: UIImage(named: "user_default.png"))
        //print(NSHomeDirectory())
        
        //3.返回cell
        return cell
    }
}

使用Swift的第三方库Kingfisher异步下载图片

GET
使用Kingfisher下载图片的时候会自动缓存到本地,不用自己写

由于在使用Kingfisher下载图片的时候还使用了其他的第三方库,所以这里不单独介绍,请跳转链接:http://www.jianshu.com/p/4d831be7da17

你可能感兴趣的:(图片的异步下载—原生与第三方库)