在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.constant
和picsViewWidht.constant
,否则会出问题
还有一定要说的一个方法是在WXStatuasCell
的初始化方法中一定要记得调用updateConstraintsIfNeeded()
这个方法,初始化执行完要去调用它,才会执行布局方法。赋值不执行,如果不调用,会有什么问题?
上图看看
问题就是刚开始的时候,前几个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了