SWIFT 3中的UICOLLECTIONVIEW,没有界面构建器。

集合视图类似于表视图,它们显示由自定义布局定义的单元格的集合,并且像表视图一样,它们必须符合协议才能显示数据和执行操作。 在本教程中,我们将创建一个看起来像网格的集合视图。

好吧,让我们潜入……

让我们开始创建一个新项目,这次选择Swift。 现在,让我开始向您介绍将处理collectionView布局的类UICollectionViewFlowLayout。

创建一个新的Cocoa Touch文件并使其成为UICollectionViewFlowLayout的子类,添加此完整的实现,我将对其进行解释……

let innerSpace: CGFloat = 1.0 
let numberOfCellsOnRow: CGFloat = 3
 override init() { 
super.init()
self.minimumLineSpacing = innerSpace
self.minimumInteritemSpacing = innerSpace
self.scrollDirection = .vertical
}
 required init?(coder aDecoder: NSCoder) { 
//fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
}
 func itemWidth() -> CGFloat { 
return (collectionView!.frame.size.width/self.numberOfCellsOnRow)-self.innerSpace
}
 override var itemSize: CGSize { 
set {
self.itemSize = CGSize(width:itemWidth(), height:itemWidth())
}
get {
return CGSize(width:itemWidth(),height:itemWidth())
}
}

我们想要创建一个在单元格之间水平和垂直方向上有1个点的网格布局,这就是为什么我们有一个具有该值的常量并将其分配给ninimumLineSpacing和minimumInteritemSpacing属性的原因。

现在,我敢打赌,您所有的注意力都在“致命错误”行上,看起来确实很吓人,但是声明此“怪异”行的方法只是一个失败的初始化程序,我不会深入探讨此主题,但是您可以在这里查看更多信息,也不会触发aDecoder:NSCoder不会触发,因为我们将在代码中完成所有操作,并且只有在接口构建器上实现collectionView时才会触发。

itemWidth函数返回除以的完整宽度,但是要在“行”中减去多少单元格,减去内部空间的数量。

最后,我们通过返回新的CGSize覆盖itemSize属性。

这就是布局的全部内容,现在让我们跳到ViewController文件,添加这两个变量…

 var gridCollectionView: UICollectionView! 
var gridLayout: GridLayout!

在viewDidLoad方法中,让我们使用新的GridLayout类来实现collectionView,如下所示:

 gridLayout = GridLayout() 
gridCollectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: gridLayout)
gridCollectionView.backgroundColor = UIColor.orange
gridCollectionView.showsVerticalScrollIndicator = false
gridCollectionView.showsHorizontalScrollIndicator = false
self.view.addSubview(gridCollectionView)

现在,我们需要为collectionView设置大小和位置,复制并粘贴…

 override func viewWillLayoutSubviews() { 
super.viewWillLayoutSubviews()
var frame = gridCollectionView.frame
frame.size.height = self.view.frame.size.height
frame.size.width = self.view.frame.size.width
frame.origin.x = 0
frame.origin.y = 0
gridCollectionView.frame = frame
}

是时候创建一个自定义Cell,创建UICollectionviewCell的新可可触摸文件子类,并将其命名为ImageCell,复制并粘贴了……

 class ImageCell: UICollectionViewCell { 

var imageView: UIImageView!

override init(frame: CGRect) {
super.init(frame: frame)
imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.isUserInteractionEnabled = false
contentView.addSubview(imageView)
}

override func layoutSubviews() {
super.layoutSubviews()
var frame = imageView.frame
frame.size.height = self.frame.size.height
frame.size.width = self.frame.size.width
frame.origin.x = 0
frame.origin.y = 0
imageView.frame = frame
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

在这里,我们向其中添加一个ImageView,这就是该单元格了。
在viewDidLoad中,我们为我们的collectionView注册类单元,并设置委托和数据源,将这些行添加到gridCollectionView的实现中。

 gridCollectionView!.register(ImageCell.self, forCellWithReuseIdentifier: "cell") 
gridCollectionView.dataSource = self
gridCollectionView.delegate = self

现在,您的编译器在抱怨,这是因为我们需要遵循协议,通常我们将其添加到类名的旁边,但是Swift为我们提供了一种更好的方法,并且扩展在哪里发光。 扩展使我们能够在不破坏类实现的情况下放置协议的所有逻辑,Apple似乎非常重视可读性,这对我们来说真是太棒了。 在课堂外,复制并粘贴此内容…

 extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate { 
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ImageCell
cell.imageView.image = UIImage.init(named: "puppy")
return cell
}
}

当然,您可以更改逻辑,例如,显示来自restApi的图像集合,但为简单起见,在我的情况下,我们仅使用一个图像,我向资产中添加了Puppy图像,请继续并寻找一个不错的选择。图像,运行该应用程序,您将看到图像的完整网格布局!

让我们更进一步,添加一些基本的动画,使用alpha值来创建溶解效果,并使用UIGesturRecoginzer。

开始将UIImageView添加到viewDidLoad中的项目并将其alpha值设置为0

 fullImageView = UIImageView() 
fullImageView.contentMode = .scaleAspectFit
fullImageView.backgroundColor = UIColor.lightGray
fullImageView.isUserInteractionEnabled = true
fullImageView.alpha = 0
self.view.addSubview(fullImageView)

使用具有相同尺寸和collectionView位置的fullImageView布局,将这行添加到ViewWillLayoutSubViews方法中…

 fullImageView.frame = gridCollectionView.frame 

如果立即运行该应用程序,将看不到fullImageView,这是因为alpha值为0,alpha属性值从0变为1,O不可见,直到1完全可见。
让我们创建一个将alpha更改回完全可见的方法,并使用CGAffineTransform制作很酷的动画,复制和粘贴…

 func showFullImage(of image:UIImage) { 
fullImageView.transform = CGAffineTransform(scaleX: 0, y: 0)
fullImageView.contentMode = .scaleAspectFit
UIView.animate(withDuration: 0.5, delay: 0, options: [], animations:{
self.fullImageView.image = image
self.fullImageView.alpha = 1
self.fullImageView.transform = CGAffineTransform(scaleX: 1, y: 1)
}, completion: nil)
}

转到您的扩展程序,然后添加…

 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 
let cell = collectionView.cellForItem(at: indexPath) as! ImageCell
if let image = cell.imageView.image {
self.showFullImage(of: image)
} else {
print("no photo")
}
}

现在,如果您运行该应用程序,则在您点击任何单元格时,它将向您显示fullImageView,但是我们需要关闭该视图,并要完成此操作,我们将向其中添加一个UITapGestureRecognizer; 在viewDidLoad中添加此行…

 let dismissWihtTap = UITapGestureRecognizer(target: self, action: #selector(hideFullImage)) 
fullImageView.addGestureRecognizer(dismissWihtTap)

最后,让我们实现该方法,以关闭编译器错误,让我们通过创建“溶解效果”来再次使用Alpha,在0.5秒内将Alpha更改回0,当然,您可以根据需要自定义其持续时间,这是功能…

 func hideFullImage() { 
UIView.animate(withDuration: 0.5, delay: 0, options: [], animations:{[unowned self] in
self.fullImageView.alpha = 0
}, completion: nil)
}

运行该应用程序,点击一个单元格以显示完整图像,然后点击该图像将其关闭。 现在,您可以采用第一种方法来使用不同的有用工具,例如UICollectionViews,扩展,CGAffineTransform,使用视图属性,例如在动画块中更改Alpha以创建炫酷效果并使用UITapGestureRecognizer来执行操作。

希望这对您有所帮助,这是Swift中的完整项目以及Objective-C中的整个项目。

这是Swift 4项目的新版本,并以编程方式使用Autolayout。

和平!