通过AutoLayout制作微博首页的cell(dispatch_group_create)

在ios7.0的时候,一直习惯用frame去计算view中所有子控件的位置和大小。前段时间已经推出了ios10,不得不承认必须快速的学习和使用自动布局,现在用自动布局来写一下微博的cell样式,记录使用的时候遇到的坑,和一些技巧。

想要实现的效果
不同图片,有不同的排版和布局
  • 步骤
  • 1.先去处理头像姓名,会员等级,认证以及底部操作栏
  • 2.设置内部的微博内容
  • 3.缓存照片
  • 4获取缓存的图片,添加图片样式

使用过程中遇到的坑
  • 1.设置微博内部内容
       //在ios以后有了这个预估属性,可以动态的去处理cell的高度,非常方便
        tableView.estimatedRowHeight = 200
        tableView.rowHeight = UITableViewAutomaticDimension
  • 2.缓存图片到本地

因为微博后台返回来的数据是图片的地址,没有照片的尺寸,所以我们必须自己去获取图片,然后使用img.size 去计算照片尺寸和布局,但是如何通过SDImage去缓存所有的图片以及如何确定所有的图片全部加载完毕,这个时候要使用到多线程的知识,下面是在首页控制器中使用SDWebImageManager缓存图片的思路及用法

 WXAPITool.shareNetWorkTools().GET(path, parameters: params, success: { (_, JSON) in
            
            //0.创建一个线程
            let group = dispatch_group_create()
            //通过这句话,可以找到我的cache路径
//            print("OKJson".cacheDir())
            //1.获取具体的json字典
            let statusDics = JSON!["statuses"] as! [[String:AnyObject]]
            //2.便利字典
            for index in 0...(statusDics.count-1) {
                let dic = statusDics[index] as [String:AnyObject]

                //3.字典转模型
                let status = WXStatusModel.init(dict: dic) 
                //4.添加到一个数组
                self.models.addObject(status)
                //4.1判断当前的微博是否有配图
                //4.2  此处status已经将picURLs转化好了,使用一个for循环,去下载照片
                for picURL in status.picULRs
                {
                    
                    
                    //进入线程
                    dispatch_group_enter(group)
                    SDWebImageManager.sharedManager().downloadWithURL(picURL, options: SDWebImageOptions(rawValue:0), progress: nil, completed: { (image, _, _, _) in
//                        print("SDWebImageManager下载的照片是-image")
                        //下载完毕,离开线程
                        dispatch_group_leave(group)
                    })
                }
            }
            //因为微博并没有给我照片尺寸,只有一个具体的照片,那么我们要先去获取具体的照片,然后根据image.size去获取照片的尺寸,但是我就蛋疼了,过去MJ是怎么做的?好在这一次可以去使用SDImage的下载照片的功能,还有就是使用移步线程去缓存照片
            //所有的线程都下载完毕,去更新UI
            dispatch_group_notify(group, dispatch_get_main_queue(), { 
//                print("最后的刷新UI")
                //5.更新tableview
                self.tableView.reloadData()
            })
            }) { (_, error) in
                print(error)
        }
    }
}
  • 3.制作WXStatusCell中图片的布局样式

对不同数量的图片布局,有很多的方法
1.自定义view,然后创建不同的子控件,设置view的尺寸
2.使用collectView,然后里面的图片设置成item

这里我们采用collectView的方法,正好数量一下collectView(在WXStatusCell中)

 //重写Frame:CollectionViewLayout 方法
    private lazy var flowLayout : UICollectionViewFlowLayout = {
        let fly = UICollectionViewFlowLayout()
        fly.minimumLineSpacing = WXCommonMargin
        fly.minimumInteritemSpacing = WXCommonMargin
        return fly
    }()
    //照片们
    private lazy var picsView:WXPicsCollectionView  = {
        let pV =  WXPicsCollectionView(frame:CGRectZero,collectionViewLayout: self.flowLayout)
        return pV
    }()

这里要去注意一下,WXPicsCollectionView就是一个封装的collectView,啥也没有特殊的,还有就是在去懒加载的时候WXPicsCollectionView(frame:CGRectZero,collectionViewLayout: flowLayout)可能会报错,如果设置成self.flowLayout就好了,应该都是懒加载,所以系统认为是错误的。

设置一下picsView的数据源方法

//MARK: - 实现collectionView代理方法和数据源方法
extension WXStatusCell:UICollectionViewDelegate,UICollectionViewDataSource
{
    //数据源方法
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return statusM?.picULRs.count ?? 0
    }
    
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = WXSingleImgCell.cellWithCollectionView(collectionView, indexPath: indexPath) as! WXSingleImgCell
        cell.imgURL = statusM?.picULRs[indexPath.item]
        return cell
    }
}

分情况讨论collectView的尺寸和内部item尺寸,这里使用的是元组

 //计算将要显示图片的尺寸
    private func calculateImageSize() -> (picsViewSize:CGSize,imgSize:CGSize)
    {
        //1.取出配图的个数
        let count = statusM?.picULRs.count
        //2.如果没有配图的话,返回zero
        if(count == 0 || count == nil)
        {
            return (CGSizeZero,CGSizeZero)
        }
        //3.如果有一张配图,返回配图实际的大小
        if count == 1 {
            let key = statusM?.picULRs.first?.absoluteString
            let image = SDWebImageManager.sharedManager().imageCache.imageFromDiskCacheForKey(key)
            return (image.size,image.size)
       
        }
        //4.如果有四张配图,就计算一下田字格的大小
        let margin = 10
        let width = 90
        if count == 4
        {
            let imgWidth = width * 2 + margin
            return (CGSizeMake(CGFloat(imgWidth), CGFloat(imgWidth)),CGSizeMake(CGFloat(width), CGFloat(width)))
        }
        //5.如果他是其他数量的,计算九宫格的大小
        
        // 5.如果是其它(多张), 计算九宫格的大小
        /*
         2/3
         5/6
         7/8/9
         */
        // 5.1计算列数
        let colNumber = 3
        // 5.2计算行数
        //(8 - 1) / 3 + 1
        let rowNumber = (count! - 1) / 3 + 1
        // 宽度 = 列数 * 图片的宽度 + (列数 - 1) * 间隙
        let viewWidth = colNumber * width + (colNumber - 1) * margin
        // 高度 = 行数 * 图片的高度 + (行数 - 1) * 间隙
        let viewHeight = rowNumber * width + (rowNumber - 1) * margin
        return (CGSizeMake(CGFloat(viewWidth), CGFloat(viewHeight)),CGSizeMake(CGFloat(width), CGFloat(width)))
        
    }

自定义一个cell,注册,复用 (省略了)...
更新UI布局

    
    //MARK: - 对外的属性
    var statusM : WXStatusModel?{
        didSet{
           //更新UI
            //头像
            iconView.sd_setImageWithURL(statusM?.user?.imageURL, placeholderImage: UIImage(named: "avatar_default"))
            //时间
            timeLab.text = NSDate.dateWithTimeStamp("\(statusM?.createAt)!").descDate
            //来源
            sourceLab.text = statusM?.source
            //内容
            contentLab.text = statusM?.text
            //名字
            nameLab.text = statusM?.user?.name
            //会员几级
            vertifiedView.image = statusM?.user?.verifiedImage
            //更新vip的级别图片
            vipView.image = statusM?.user?.vipRankImage
            //更新照片的尺寸
            flowLayout.itemSize = calculateImageSize().imgSize
            
            picsViewWidht?.constant = calculateImageSize().picsViewSize.width
            picsViewHeight?.constant = calculateImageSize().picsViewSize.height
            picsView .reloadData()
            
     updateConstraintsIfNeeded()
        }
    }

防止cell复用的问题,一定要去赋值完毕后刷新UI,重新布局,否则会有问题,还有就是使用collectView的时候,赋值之后,一定要去reloadData ()一下,否则会有问题!!!亲生经历啊。这里还要说明一点,很重要,在调用override func updateConstraints()这个方法的时候,我们给collectVIew设置约束的时候,是这样的

        picsView.autoPinEdgeToSuperviewEdge(ALEdge.Left, withInset:WXCommonMargin)
        picsView.autoPinEdge(ALEdge.Top, toEdge: ALEdge.Bottom, ofView: contentLab, withOffset: WXCommonMargin)
        picsViewHeight = picsView.autoSetDimension(ALDimension.Height, toSize: 15)
        picsViewWidht = picsView.autoSetDimension(ALDimension.Width, toSize: 15)

每次给cell刷新页面的时候,重新调整一下约束的picsViewHeight.constantpicsViewWidht.constant,否则会出问题
还有一定要说的一个方法是在WXStatuasCell的初始化方法中一定要记得调用updateConstraintsIfNeeded()这个方法,初始化执行完要去调用它,才会执行布局方法。赋值不执行,如果不调用,会有什么问题?

上图看看


有问题的.gif

问题就是刚开始的时候,前几个collectionView没有正确的样式,往下滑动,地下的是正确的,当往上滑动到最初的cell,所有的都正确了
为什么?
因为给cell赋值的时候,执行了这句话,而且二者都是nil

 picsViewWidht?.constant = calculateImageSize().picsViewSize.width
            picsViewHeight?.constant = calculateImageSize().picsViewSize.height

MJ时代为毛没记得有这个?
所以只能通过先用SDImage缓存图片,然后在更新UI

1.statusModel内部
  • 用swift的时候,最好要去使用胖model,让项目的可读性变得更更好
  • 先去给statusModel添加一个新的属性picURLs
  • 在** picThumbnails数组赋值的setVFK的时候,直接给 picURLs**添加处理过的数据
    /**照片的资源str*/
    var picThumbnails:[[String:AnyObject]]?{
        didSet{
           //将str -> url
            for picdic in picThumbnails! {
                let pic = picdic["thumbnail_pic"] as! String
                picULRs.append(NSURL(string: pic)!)
            }
        }
    }
    // 照片的url数组,通过picThumbnails 转换而来
    var picULRs = [NSURL]()     

解释两点:

  • 1.** picURLs必须初始化**,picURLs只是指针,生成status对象的时候,picRULs是个空指针,如果没有外力给他的空间指向,或者没有一开始就初始化一个内存空间,他就是个nil
  • 2.这样可以直接告诉系统是什么类型的数组

2.首页控制器内部
  • 1.字典转模型
  • 2.直接获取具体picURLs数据
  • 3.添加一个线程group,去下载图片
  • 4.所有的图片的呗缓存完毕之后,更新UI
   //获取网络数据
    private func loadNewData(){
        
        let path = "2/statuses/home_timeline.json"
        let params = ["access_token": WXUser.fetchUserInfo()!.accessToken!]
         WXAPITool.shareNetWorkTools().GET(path, parameters: params, success: { (_, JSON) in
            
            //0.创建一个线程
            let group = dispatch_group_create()
            //通过这句话,可以找到我的cache路径
            print("OKJson".cacheDir())
            //1.获取具体的json字典
            let statusDics = JSON!["statuses"] as! [[String:AnyObject]]
            //2.便利字典
            for index in 0...(statusDics.count-1) {
                let dic = statusDics[index] as [String:AnyObject]

                //3.字典转模型
                let status = WXStatusModel.init(dict: dic)
                //3.1  此处status已经将picURLs转化好了,使用一个for循环,去下载照片
                for picURL in status.picULRs
                {
                    
                    //4.添加到一个数组
                    self.models.addObject(status)
                    //进入线程
                    dispatch_group_enter(group)
                    SDWebImageManager.sharedManager().downloadWithURL(picURL, options: SDWebImageOptions(rawValue:0), progress: nil, completed: { (image, _, _, _) in
                        print("SDWebImageManager下载的照片是-image")
                        //下载完毕,离开线程
                        dispatch_group_leave(group)
                    })
                }
            }
            //因为微博并没有给我照片尺寸,只有一个具体的照片,那么我们要先去获取具体的照片,然后根据image.size去获取照片的尺寸,但是我就蛋疼了,过去MJ是怎么做的?好在这一次可以去使用SDImage的下载照片的功能,还有就是使用移步线程去缓存照片
            //所有的线程都下载完毕,去更新UI
            dispatch_group_notify(group, dispatch_get_main_queue(), { 
                print("最后的刷新UI")
                //5.更新tableview
                self.tableView.reloadData()
            })
            }) { (_, error) in
                print(error)
        }
    }
}

一直以来感觉线程用的地方比较少,但是此处必须有线程,没有线程就不好使,因为这个位置是要有好多model,而且下载的速度不一样,次序不一样,必须所有的都下载完毕才行,否则可能崩溃,这个就要进入group,下载完毕出去,然后所有的都搞定之后,才能去通知成功了,一个都不能少,为了验证次序,特意做了打印,结果如下

/Users/wangxin/Library/Developer/CoreSimulator/Devices/7D2A98A9-0339-4664-B900-0CF935D62FE5/data/Containers/Data/Application/74CDAA3D-1889-49FC-AD0F-7359C3460642/Library/Caches/OKJson
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
SDWebImageManager下载的照片是-image
最后的刷新UI

所有的图片都下载完毕了,这样我们就可以对应的更新不同照片数量的UI了

你可能感兴趣的:(通过AutoLayout制作微博首页的cell(dispatch_group_create))