轻松学习swift--swift项目初体验(二)

项目源代码: Photo-Browser

一.传递数据

1.传递什么数据?

要在PhotoBrowserVc中查看大图,首先要拿到图片数据

2.怎么传递数据?直接传递图片?

直接传递图片url也可以,不过要从模型数组中抽离出来,不太好
最好的做法是:直接把模型数组传递给PhotoBrowserVc

二.自定义PhotoBrowserCell,用于展示数据

1.PhotoBrowserVc是UICollectionViewController,要想展示图片,需要在cell上添加UIImageView

注意:如果一个构造函数前有required,那么重写了其他构造函数时,那么该构造函数也必须被重写

// MARK:- 重写构造函数    
override init(frame: CGRect) {        
    super.init(frame: frame)               
    setupUI()    
}    
// required : 如果一个构造函数前有required,那么重写了其他构造函数时,那么该构造函数也必须被重写    
required init?(coder aDecoder: NSCoder) {              
    fatalError("init(coder:) has not been implemented")
}

2.要想展示图片,需要设置什么?

2.1 设置UIImageView的image

直接从 SDWebImage缓存中取出原来cell的图片(小图)
注意:取出的图片类型是可选类型,要先进行判断再使用

2.2 设置UIImageView的frame

2.21 根据取出的图片的尺寸,计算图片的frame(设置UIImageView的宽度等于屏幕宽度)

2.22 让图片的宽度等于UIImageView的宽度

2.23 UIImageView的高度,就等于 图片高度 * UIImageView的宽度 / 图片宽度 (让图片等宽高比拉伸)

3.加载高清图片

3.1 为什么要加载高清图片?

上面取出的图片是小图,不清晰. 查看大图的时候,要换成高清图片

3.2 怎么设置?

用SDWebImage加载大图,把小图设置为占位图片
占位图片:图片还没加载的时候,先用内存中的一张图片显示到屏幕上,加载好图片, 就显示加载的图片

4.设置完成后,查看大图,发现滚动到后面,发现图片被压缩了,为什么?

4.1 因为在MainVc(首页)展示小图的时候,给小图也设置了占位图片

4.2 在PhotoBrowserVc中查看大图,滚动到后面的时候,MainVc中的cell还没显示,小图就不会被加载,就把占位图片赋值给小图

 // 2.获取小图片
var smallImage = SDWebImageManager.sharedManager().imageCache.imageFromDiskCacheForKey(shop.q_pic_url)            
if smallImage == nil {                
        smallImage = UIImage(named: "empty_picture")
        }

4.3 这是UIImageView的尺寸就是根据占位图片的尺寸计算出来的,跟实际图片的尺寸会有差别,实际显示的图片就可能被压缩

5.怎么解决图片压缩问题?

大图请求成功时,重新计算UIImageView的尺寸就可以了

三.把collectionView滚动到正确的位置

1.为什么要滚动collectionView?

PhotoBrowserVc的cell是从第0个cell开始显示的, 所以每次点击查看大图都是从第0张图片开始显示
当点击MainVc(首页)cell的时候,要显示对应的大图,不一定是第0张图片

2.怎么滚动?

2.1 在PhotoBrowserVc中定义indexPath属性, 在MainVc中拿到cell的indexPath,对 PhotoBrowserVc的indexPath赋值

2.2 滚动到对应的位置(用下面这个方法)

collectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: .CenteredHorizontally, animated: false)

2.3 滚动代码应该写到哪里?

点击MainVc的cell,就弹出查看大图控制器(PhotoBrowserVc),就要滚动要对应的位置
所以,代码可以写到viewDidLoad里面

3. ??的使用

  // ?? : 先判断前面的可选链是否有值, 如果有值,解包并且获取对应类型的值. 如果没有值直接取后面的值
   return shops?.count ?? 0

四.设置大图之间的间距

1.怎么设置大图之间的间距?用 minimumLineSpacing?

不可以,虽然能让大图之间有间距,但是会把后面的cell往后移 ,后面的cell就不能完全显示在屏幕上

2.思考:可以collectionView的cell的宽度比屏幕宽度大一点,多出来的宽度就当做间距

不可行,collectionView(scrollView)的分页效果,会让用户看到多出来的那部分

scrollView分页效果的滚动距离 是由scrollView的宽度来决定的

3.最终解决方案

3.1 只用一句代码就可以搞定,把控制器的view的宽度增大一点就可以了

      view.frame.size.with += 15

3.2 注意collectionView的宽度和 cell的宽度 要等于控制器的view的宽度才可以

4.全局函数的定义

4.1 什么是全局函数?
就是在工程目录下的任何地方都能使用的函数

4.2 怎么定义全局函数?
只要把函数定义到AppDelegate里面就可以了

五.保存图片

1.先要拿到对应的图片,根据indexPath拿?

点击查看大图后可能会被滚动,所以不能根据indexPath拿

2.怎么拿到正在显示的图片

2.1 先拿到正在显示的cell
2.2 cell里面保存的就有image

3.怎么拿到cell

可以通过苹果自带的api拿到正在显示的cell

// 1.1.拿到正在显示的Cell        
// visibleCells 返回所有在屏幕中显示的Cell        
let cell = collectionView.visibleCells()[0] as! PhotoBrowserViewCell        
 guard let image = cell.imageView.image else {            
    return
    }

4.保存图片到相册

苹果自带api保存到相册

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)

六.点击大图关闭控制器

1.需求:点击大图或关闭按钮,把控制器dismiss掉

2.点击按钮关闭
给按钮设置点击方法就可以了 addTarget

3.点击大图关闭怎么实现?
点击大图相当于点击了cell,在cell代理方法里面dismiss即可

七.自定义转场(淡入淡出)

1.怎么自定义转场动画?

遵守转场的代理协议UIViewControllerTransitioningDelegate,实现代理方法

2.实现了代理方法,发现程序还是报错,为什么?

代理方法都有一个返回值,返回值要遵守一个协议UIViewControllerContextTransitioning才能作为返回值

3.在UIViewControllerContextTransitioning代理方法里面设置动画(具体看代码)

4.设置消失动画

4.1 设置完显示动画后,消失的时候也自动会有一个动画效果,为什么?
因为,大图view消失的时候,也是主控制器的view显示的时候
看到的消失动画,实际上是主控制器的view的显示动画

4.2 怎么判断是显示,还是消失?

定义一个属性记录即可
在UIViewControllerTransitioningDelegate代理方法中记录

// MARK:- 遵守转场的代理协议,和实现对应的方法
extension HomeViewController : UIViewControllerTransitioningDelegate {    
// 为弹出控制器做一个动画
func animationControllerForPresentedController(presented: 
UIViewController, presentingController presenting: 
UIViewController, sourceController source: UIViewController) 
-> UIViewControllerAnimatedTransitioning? {

 //记录当前为显示阶段
 isPresented = true
    return self
}

// 为消失控制器做一个动画
func animationControllerForDismissedController(dismissed: 
UIViewController) -> UIViewControllerAnimatedTransitioning? {

  //记录当前为消失阶段
  isPresented = false
    return self    
  }
}

extension HomeViewController : UIViewControllerAnimatedTransitioning {    
// 返回动画执行的时间    
func transitionDuration(transitionContext: 
UIViewControllerContextTransitioning?) 
-> NSTimeInterval {        

return 3    
}        
// transitionContext : 转场上下文    
// 作用 : 可以通过上下文获取到弹出的View和消失的View    
// UITransitionContextFromViewKey : 获取消失的View
// UITransitionContextToViewKey : 获取弹出的View
func animateTransition(transitionContext: 
UIViewControllerContextTransitioning) {       

 if isPresented {            
// 获取弹出的View
let presentedView = transitionContext.viewForKey(UITransitionContextToViewKey)!
//需要把view添加到父控件上,才能有动画效果  
//父控件就是widow的containerView,   通过transitionContext.containerView()拿到
transitionContext.containerView()?.addSubview(presentedView) 
// 修改View alpha值            
presentedView.alpha = 0.0                       
// 执行动画            
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {                
presentedView.alpha = 1.0
}) { (isFinished : Bool) in
//告诉控制器,转场动画完成
transitionContext.completeTransition(isFinished)            
  }        
} else {            
// 1.获取消失的View            
let dismissedView = transitionContext.viewForKey(UITransitionContextFromViewKey)!  
// 2.执行动画            
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {                
    dismissedView.alpha = 0.0
  }, completion: { (isFinished : Bool) in
 //移除view,显示主控制器的view
       dismissedView.removeFromSuperview()                    
        transitionContext.completeTransition(isFinished)            
      })        
    }   
  }
}

5.性能优化,代码抽取

5.1 把转场动画的代理设置为主控制器,代理要全部写在主控制器中,代码臃肿,阅读性差

5.2 怎么优化?

把代理设置为其它对象,让其它对象实现代理方法即可

5.3 具体实现步骤

5.31 创建一个对象(任何对象)

5.32 设置这个对象为转场动画的代理

5.33 在对象中实现代理方法即可

5.34 注意:代理属性为弱引用,要让一个强引用指向它

八.最终动画效果

1.想要做动画,必须要拿到三个元素

1.1 图片的起始位置(相对于控制器view的坐标系)

1.2 图片的终点位置(相对于控制器view的坐标系)

1.3 转场图片的父控件UIImageView

2.弹出动画

2.1 获取动画的三个元素
图片的起始位置,终点位置和UIImageView 只有主控制器(mainVc)最清楚,可以定义代理

2.2 mainVc成为动画代理对象的代理,提供三个元素

3.消失动画

3.1 消失的时候,图片的终点位置有可能发生变化,需要重新计算

3.2 怎么计算消失的图片的终点位置?

只要拿到对应cell的indexPath就可以计算位置

3.3 怎么拿到indexPath?

cell的indexPath只有PhotoBrowserVc最清楚,可以设置代理,让PhotoBrowserVc提供indexPath
indexPath就是最后显示在屏幕上cell的indexPath

// 1.获取在屏幕中显示的cell
let cell = collectionView.visibleCells()[0]  
// 2.获取cell对应的indexPath
let indexPath = collectionView.indexPathForCell(cell)!

3.4 根据indexPath计算终点位置,完成动画

4.性能优化

4.1 当查看大图滚动的时候,indexPath会大于屏幕上显示的indexPath最大值,这个时候,就获取不到终点位置,就会没有消失动画

4.2 怎么解决?

方法一: 当超出的时候,给定一个终点位置,让它在指定的位置消失
效果可以,但是满足不了需求

4.3 最终方案(参考微信的解决方案)

当获取不到终点位置的时候,让图片消失的动画 设置为渐变消失动画

九.版本适配bug的解决

1.当项目运行到6s Plus上的时候,collectionView只能显示两列(需求是三列)

产生bug的原因是苹果对临界值得处理不太好

具体来说就是,屏幕宽度三等分,得到的数值是无限循环小数,苹果会根据数据类型对小数向前进一位

这时,屏幕的宽度就不足以放三个cell,就会把第三个cell挤到下一行显示,就变成了两列

2.bug解决

让得到的cell的宽度减去一个临界值小数即可(0.000001) 这个数值随便写

想了解更多请查看:轻松学习swift--swift项目初体验(一)

项目源代码: Photo-Browser
喜欢就给个星星吧

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,595评论 4 59
  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,833评论 3 38
  • 本来是打算开学后就给你写一封信的,但是一直因为自己的原因,一直没有写成,今晚以简书的名义代替信吧。 1、若不曾体会...
    琉璃木子李阅读 445评论 0 2
  • 重新看了一遍《悟空传》 然后去操场跑了五公里 人生第一次必须要嘚瑟 听的是郭燕的《天空之城》 调子太悲书也悲心里自...
    旧时咸阳阅读 304评论 0 0
  • 1、什么是cocoaPods cocoaPods是开发OS X和iOS应用程序的一个第三方库的依赖管理工具 2,为...
    禅牧阅读 831评论 10 2