1,效果图

(1)图片从左至右横向排列(只有一行),通过手指拖动可以前后浏览图片。
(2)视图滚动时,每张图片根据其与屏幕中心距离的不同,显示尺寸也会相应地变化。越靠近屏幕中心尺寸就越大,远离屏幕中心的就逐渐变小。
(3)滑动结束后,会有位置自动修正的功能。即将当前最靠近屏幕中点的图片移动到正中央。
(4)点击图片则将该图片删除,点击空白处会在最开始的位置插入一张图片。不管新增还是删除都有动画效果。
(5)点击导航栏上的“切换”按钮,可以在普通的流式布局和我们自定义的画廊布局间相互切换。切换时也是有动画效果的。
           

2,画廊布局类:LinearCollectionViewLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import UIKit
 
class LinearCollectionViewLayout: UICollectionViewFlowLayout {
     
    //元素宽度
    var itemWidth:CGFloat = 100
    //元素高度
    var itemHeight:CGFloat = 100
     
    //对一些布局的准备操作放在这里
    override func prepare() {
        super.prepare()
        //设置元素大小
        self.itemSize = CGSize(width: itemWidth, height: itemHeight)
        //设置滚动方向
        self.scrollDirection = .horizontal
        //设置间距
        self.minimumLineSpacing = self.collectionView!.bounds.width / 2 -  itemWidth
         
        //设置内边距
        //左右边距为了让第一张图片与最后一张图片出现在最中央
        //上下边距为了让图片横行排列,且只有一行
        let left = (self.collectionView!.bounds.width - itemWidth) / 2
        let top = (self.collectionView!.bounds.height - itemHeight) / 2
        self.sectionInset = UIEdgeInsetsMake(top, left, top, left)
    }
     
    //边界发生变化时是否重新布局(视图滚动的时候也会触发)
    //会重新调用prepareLayout和调用
    //layoutAttributesForElementsInRect方法获得部分cell的布局属性
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }
     
    //rect范围下所有单元格位置属性
    override func layoutAttributesForElements(in rect: CGRect)
        -> [UICollectionViewLayoutAttributes]? {
        //从父类得到默认的所有元素属性
        let array = super.layoutAttributesForElements(in: rect)
         
        //可见区域(目前显示出来的位于collection view上的矩形区域)
        let visiableRect = CGRect(x: self.collectionView!.contentOffset.x,
                                  y: self.collectionView!.contentOffset.y,
                                  width: self.collectionView!.frame.width,
                                  height: self.collectionView!.frame.height)
         
        //当前屏幕中点,相对于collect view上的x坐标
        let centerX = self.collectionView!.contentOffset.x
            + self.collectionView!.bounds.width / 2
         
        //这个是为了计算缩放比例的
        let maxDeviation = self.collectionView!.bounds.width / 2 + itemWidth / 2
         
        for attributes in array! {
            //与可见区域做碰撞,如果该单元格没显示则直接跳过
            if !visiableRect.intersects(attributes.frame) {continue}
            //显示的单元根据偏移量决定放大倍数(最大放大1.8倍,而离屏幕中央越远的单元格缩放的越小)
            let scale = 1 + (0.8 - abs(centerX - attributes.center.x) / maxDeviation)
            attributes.transform = CGAffineTransform(scaleX: scale, y: scale)
        }
         
        return array
    }
     
    /**
     用来设置collectionView停止滚动那一刻的位置(实现目的是当停止滑动,时刻有一张图片是位于屏幕最中央的)
     proposedContentOffset: 原本collectionView停止滚动那一刻的位置
     velocity:滚动速度
     返回:最终停留的位置
     */
    override func targetContentOffset(forProposedContentOffset
        proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        //停止滚动时的可见区域
        let lastRect = CGRect(x: proposedContentOffset.x, y: proposedContentOffset.y,
                              width: self.collectionView!.bounds.width,
                              height: self.collectionView!.bounds.height)
        //当前屏幕中点,相对于collect view上的x坐标
        let centerX = proposedContentOffset.x + self.collectionView!.bounds.width * 0.5;
        //这个可见区域内所有的单元格属性
        let array = self.layoutAttributesForElements(in: lastRect)
         
        //需要移动的距离
        var adjustOffsetX = CGFloat(MAXFLOAT);
        for attri in array! {
            //每个单元格里中点的偏移量
            let deviation = attri.center.x - centerX
            //保存偏移最小的那个
            if abs(deviation) < abs(adjustOffsetX) {
                adjustOffsetX = deviation
            }
        }
        //通过偏移量返回最终停留的位置
        return CGPoint(x: proposedContentOffset.x + adjustOffsetX, y: proposedContentOffset.y)
    }
}

3,使用样例

(1)自定义单元格类:MyCollectionViewCell.swift(创建的时候生成对应的 xib 文件)
1
2
3
4
5
6
7
8
9
10
11
12
import UIKit
 
//自定义的Collection View单元格
class MyCollectionViewCell: UICollectionViewCell {
 
    //用于显示图片
    @IBOutlet weak var imageView: UIImageView!
     
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

(2)主视图代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import UIKit
 
class ViewController: UIViewController {
    //普通的flow流式布局
    var flowLayout:UICollectionViewFlowLayout!
    //自定义的线性布局
    var linearLayput:LinearCollectionViewLayout!
     
    var collectionView:UICollectionView!
     
    //重用的单元格的Identifier
    let CellIdentifier = "myCell"
     
    //所有书籍数据
    var images = ["c#.png", "html.png", "java.png", "js.png", "php.png",
                  "react.png", "ruby.png", "swift.png", "xcode.png"]
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //初始化Collection View
        initCollectionView()
         
        //注册tap点击事件
        let tapRecognizer = UITapGestureRecognizer(target: self,
                                    action: #selector(ViewController.handleTap(_:)))
        collectionView.addGestureRecognizer(tapRecognizer)
    }
     
    private func initCollectionView() {
        //初始化flow布局
        flowLayout = UICollectionViewFlowLayout()
        flowLayout.itemSize = CGSize(width: 60, height: 60)
        flowLayout.sectionInset = UIEdgeInsets(top: 74, left: 0, bottom: 0, right: 0)
         
        //初始化自定义布局
        linearLayput = LinearCollectionViewLayout()
         
        //初始化Collection View
        collectionView = UICollectionView(frame: view.bounds,
                                          collectionViewLayout: linearLayput)
         
        //Collection View代理设置
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.backgroundColor = .white
         
        //注册重用的单元格
        let cellXIB = UINib.init(nibName: "MyCollectionViewCell", bundle: Bundle.main)
        collectionView.register(cellXIB, forCellWithReuseIdentifier: CellIdentifier)
         
        //将Collection View添加到主视图中
        view.addSubview(collectionView)
    }
     
    //点击手势响应
    func handleTap(_ sender:UITapGestureRecognizer){
        if sender.state == UIGestureRecognizerState.ended{
            let tapPoint = sender.location(in: self.collectionView)
            //点击的是单元格元素
            if let  indexPath = self.collectionView.indexPathForItem(at: tapPoint) {
                //通过performBatchUpdates对collectionView中的元素进行批量的插入,删除,移动等操作
                //同时该方法触发collectionView所对应的layout的对应的动画。
                self.collectionView.performBatchUpdates({ () -> Void in
                    self.collectionView.deleteItems(at: [indexPath])
                    self.images.remove(at: indexPath.row)
                }, completion: nil)
                 
            }
            //点击的是空白位置
            else{
                //新元素插入的位置(开头)
                let index = 0
                images.insert("xcode.png", at: index)
                self.collectionView.insertItems(at: [IndexPath(item: index, section: 0)])
            }
        }
    }
     
    //切换布局样式
    @IBAction func changeLayout(_ sender: Any) {
        self.collectionView.collectionViewLayout.invalidateLayout()
        //交替切换新布局
        let newLayout = collectionView.collectionViewLayout
            .isKind(of: LinearCollectionViewLayout.self) ? flowLayout : linearLayput
        collectionView.setCollectionViewLayout(newLayout!, animated: true)
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
 
//Collection View数据源协议相关方法
extension ViewController: UICollectionViewDataSource {
    //获取分区数
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
     
    //获取每个分区里单元格数量
    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int {
        return images.count
    }
     
    //返回每个单元格视图
    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        //获取重用的单元格
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier:
            CellIdentifier, for: indexPath) as! MyCollectionViewCell
        //设置内部显示的图片
        cell.imageView.image = UIImage(named: images[indexPath.item])
        return cell
    }
}
 
//Collection View样式布局协议相关方法
extension ViewController: UICollectionViewDelegate {
     
}

源码下载:hangge_1602.zip
原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1602.html

Swift - 使用CollectionView实现图片Gallery画廊效果(左右滑动浏览图片)的更多相关文章

  1. Android BaseAdapter Gallery 画廊视图 (左右拖动图片列表拖至中间时图片放大显示)

    画廊视图使用Gallery表示,能够按水平方向显示内容,并且可以手指直接拖动图片和移动,一般用来浏览图片,,被选中的选项位于中间,并且可以响应事件显示信息.在使用画廊视图时,首先在屏幕上添加Galle ...

  2. wpf 模拟3D效果(和手机浏览图片效果相似)(附源码)

    原文 wpf 模拟3D效果(和手机浏览图片效果相似)(附源码) pf的3D是一个很有意思的东西,类似于ps的效果,类似于电影动画的效果,因为动画的效果,(对于3D基础的摄像机,光源,之类不介绍,对于依 ...

  3. RecyclerView实现Gallery画廊效果

    使用RecyclerView实现一个画廊效果,主要是使用support库中最新加入的PagerSnapHelper类,通过计算滑动偏移来计算scale的值. 基本实现 首先需要为RecyclerVie ...

  4. RecyclerView 实现gallery画廊效果

    1.RecyclerView的基本用法 首先主Activity的布局文件: [html] view plaincopy <RelativeLayout xmlns:android="h ...

  5. Android 自定义View 之利用ViewPager 实现画廊效果(滑动放大缩小)

    http://www.2cto.com/kf/201608/542107.html

  6. UI组件之AdapterView及其子类(四)Gallery画廊控件使用

    听说 Gallery如今已经不使用了,API使用ViewPaper取代了,以后再学专研ViewPaper吧如今说说Gallery画廊,就是不停显示图片的意思 Gallery是用来水平滚动的显示一系列项 ...

  7. js 实现图片瀑布流效果,可更改配置参数 带完整版解析代码[waterFall.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS实现图片瀑布流效果 页面需求 1 ...

  8. Swift 使用CollectionView 实现图片轮播封装就是这样简单

    前言: 这篇你可以学会自定义视图,创建collectionView,协议的使用,定时器; 自制图片 先上Demo:Github上封装好的下载即用, 好用请Star Thanks首先新建一个继承于UIV ...

  9. 第31讲 UI组件之 Gallery画廊控件

    第31讲 UI组件之 Gallery画廊控件 1.Gallery的简介 Gallery(画廊)是一个锁定中心条目并且拥有水平滚动列表的视图,一般用来浏览图片,并且可以响应事件显示信息.Gallery只 ...

随机推荐

  1. HTML网页做成ASP.NET后台的方法以及.NET后台控制前台样式的方法

    之前一直不知道,写好的纯HTML网页怎么做成ASP.NET后台的呢,因为之前使用别人的HTML模板写过一个自己的个人博客 果冻栋吖个人博客 当时用的PHP写的.一直在考虑怎么做成.NET的. 今天自己 ...

  2. WPF下DataGrid的简单应用

    Dim dt As New DataTable() '------------------- dt.Columns.Add(New DataColumn("名称")) dt.Col ...

  3. [Offer收割]编程练习赛34

    共同富裕 显然每次选最大的数字,其余的加一.也可以理解为每次选一个最大的数字减一,直到所有数字都变成最小的数字为止. #include<stdio.h> #include<strin ...

  4. express jade ejs 为什么要用这些?

    express是快速构建web应用的一个框架   线上文档 http://www.expressjs.com.cn/ 不用express行不行呢?    看了网上的回答:不用express直接搭,等你 ...

  5. java加密解密算法位运算

    一.实例说明 本实例通过位运算的异或运算符 “ ^ ” 把字符串与一个指定的值进行异或运算,从而改变每个字符串中字符的值,这样就可以得到一个加密后的字符串.当把加密后的字符串作为程序输入内容,异或运算 ...

  6. hiho1804 - 整数分解、组合数、乘法逆元

    题目链接 题目叙述很啰嗦,可以简化为:n个球[1-1e5],放到m个不同的桶里,一共多少种不同的放法.[桶里可以不放] ---------------------------------------- ...

  7. Approximate Nearest Neighbors.接近最近邻搜索

    (一):次优最近邻:http://en.wikipedia.org/wiki/Nearest_neighbor_search 有少量修改:如有疑问,请看链接原文.....1.Survey:Neares ...

  8. C# HttpWebRequest post 请求传参数

    Dictionary<string, string> parameters = new Dictionary<string, string>(); //参数列表 paramet ...

  9. mongodb报错一例

    开发程序报错信息: Caused by: com.mongodb.MongoException: Executor error: OperationFailed: Sort operation use ...

  10. WPF创建自定义控件并运用

    此项目源码:https://github.com/lizhiqiang0204/WpfCustomControlLibrary1 首先创建自定义控件库项目 项目名称命名为:WpfCustomContr ...