通过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了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,506评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,024评论 4 62
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,125评论 29 470
  • 有你有我
    木瞪口呆阅读 177评论 0 0
  • 密旅,看到这个字眼,很多人想到的会是情侣、闺密……然而,这一次远行,却是一群拥有相同目的,相同梦想,相同努力却...
    春蚕宝宝阅读 547评论 0 0