I am playing around with collectionViews,
trying to create a custom design for image layout.
See below:
I have achieved this.
I know the code isn't the most beautiful or regular, however I am intrigued to see if this is possible.
When I scroll, the cell are pushed to the bottom of the screen, to where they are no longer visible.
Is there a fix for this?
Results after scrolling vvv
Here is the code: It can be copied to a blank project and run by itself for testing.
Any input would be greatly appreciated.
Thanks
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var spiralNumber = 0
var collectionView: UICollectionView!
let colors = [UIColor.yellow, UIColor.red, UIColor.blue, UIColor.green, UIColor.purple, UIColor.systemRed, UIColor.systemMint, UIColor.systemBlue, UIColor.cyan, UIColor.gray, UIColor.systemTeal, UIColor.systemYellow]///testing purposes
func generateRandomNumber(min: Int, max: Int) -> Int {
return Int.random(in: min...max)
}//testing purposes
func subtractionFive(number: Int) -> Int {
let largestMultipleOfFive = (number / 5) * 5
return number - largestMultipleOfFive
}
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = -(view.frame.width * 2/3) / 2
layout.sectionInset = .zero
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionView.backgroundColor = .white
view.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 300
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let randomNumber = generateRandomNumber(min: 0, max: colors.count-1)
let randomColor = colors[randomNumber]
cell.backgroundColor = randomColor
let spiralIndex = subtractionFive(number: indexPath.row)
spiralNumber = spiralIndex
let rowHeight = collectionView.frame.width
let rowNumber = CGFloat((indexPath.row/5))
let rowOffset = rowNumber*rowHeight
print("rowNumber-->", Int(rowNumber), rowOffset, rowOffset/rowNumber, spiralNumber)
if spiralNumber == 0 {
cell.frame = CGRect(x: 0, y: 0+rowOffset, width: cell.frame.width, height: cell.frame.height)
}else if spiralNumber == 1{
cell.frame = CGRect(x: cell.frame.width*2, y: 0+rowOffset, width: cell.frame.width, height: cell.frame.height)
}else if spiralNumber == 2{
cell.frame = CGRect(x: 0, y: (cell.frame.height/2)+rowOffset, width: cell.frame.width, height: cell.frame.height)
} else if spiralNumber == 3 {
cell.frame = CGRect(x: cell.frame.width, y: cell.frame.height+rowOffset, width: cell.frame.width, height: cell.frame.height)
} else if spiralNumber == 4{
cell.frame = CGRect(x: cell.frame.width/2, y: (cell.frame.height*2)+rowOffset, width: cell.frame.width, height: cell.frame.height)
}else{
cell.frame = CGRect(x: cell.frame.width, y: (cell.frame.height*2)+rowOffset, width: cell.frame.width, height: cell.frame.height)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width
let spiralIndex = subtractionFive(number: indexPath.row)
spiralNumber = spiralIndex
if spiralNumber == 0 || spiralNumber == 4{
return CGSize(width: width * 2/3, height: width * 1/3)
}else if spiralNumber == 1 || spiralNumber == 2{
return CGSize(width: width * 1/3, height: width * 2/3)
}else if spiralNumber == 3{
return CGSize(width: width * 1/3, height: width * 1/3)
}else{
return CGSize(width: width * 2/3, height: width * 1/3)
}
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("test->", indexPath.row/5)
}
}
I was expecting the cells to remain in place as I scroll down. I.e. Normal behaviour of a collectionView.
First be careful don't do any resizing in this function
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
all resizing should be in here
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)
Second Collection view does not know how to draw cells inside of it so it needs a layout so apple provide us with a default layout called UICollectionViewFlowLayout. which you implement sizeForItemAt function to tell the layout the size you want for each cell but it's limited so when you want to do something like your example you need to ignore the default layout and create your own custom layout here is the final code
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var spiralNumber = 0
var collectionView: UICollectionView!
let colors = [UIColor.yellow, UIColor.red, UIColor.blue, UIColor.green, UIColor.purple, UIColor.systemRed, UIColor.systemMint, UIColor.systemBlue, UIColor.cyan, UIColor.gray, UIColor.systemTeal, UIColor.systemYellow]///testing purposes
func generateRandomNumber(min: Int, max: Int) -> Int {
return Int.random(in: min...max)
}//testing purposes
func subtractionFive(number: Int) -> Int {
let largestMultipleOfFive = (number / 5) * 5
return number - largestMultipleOfFive
}
override func viewDidLoad() {
super.viewDidLoad()
let layout = CustomCollectionViewLayout()
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionView.backgroundColor = .white
view.addSubview(collectionView)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 300
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let randomNumber = generateRandomNumber(min: 0, max: colors.count-1)
let randomColor = colors[randomNumber]
cell.backgroundColor = randomColor
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// print("test->", indexPath.row/5)
}
}
class CustomCollectionViewLayout: UICollectionViewLayout {
private var cachedAttributes: [UICollectionViewLayoutAttributes] = []
private var contentHeight:CGFloat = 0
var spiralNumber = 0
private var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
func subtractionFive(number: Int) -> Int {
let largestMultipleOfFive = (number / 5) * 5
return number - largestMultipleOfFive
}
override func prepare() {
guard cachedAttributes.isEmpty, let collectionView else {return}
for index in 0..<collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: index, section: 0)
let spiralIndex = subtractionFive(number: indexPath.row)
spiralNumber = spiralIndex
let rowHeight = contentWidth
let rowNumber = CGFloat((indexPath.row/5))
let rowOffset = rowNumber*rowHeight
let width = contentWidth
spiralNumber = spiralIndex
var size: CGSize = .zero
if spiralNumber == 0 || spiralNumber == 4{
size = CGSize(width: width * 2/3, height: width * 1/3)
}else if spiralNumber == 1 || spiralNumber == 2{
size = CGSize(width: width * 1/3, height: width * 2/3)
}else if spiralNumber == 3{
size = CGSize(width: width * 1/3, height: width * 1/3)
}else{
size = CGSize(width: width * 2/3, height: width * 1/3)
}
var frame:CGRect = .zero
if spiralNumber == 0 {
frame = CGRect(x: 0, y: 0+rowOffset, width: size.width, height: size.height)
}else if spiralNumber == 1{
frame = CGRect(x: size.width*2, y: 0+rowOffset, width: size.width, height: size.height)
}else if spiralNumber == 2{
frame = CGRect(x: 0, y: (size.height/2)+rowOffset, width: size.width, height: size.height)
} else if spiralNumber == 3 {
frame = CGRect(x: size.width, y: size.height+rowOffset, width: size.width, height: size.height)
} else if spiralNumber == 4{
frame = CGRect(x: size.width/2, y: (size.height*2)+rowOffset, width: size.width, height: size.height)
}else{
frame = CGRect(x: size.width, y: (size.height*2)+rowOffset, width: size.width, height: size.height)
}
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
cachedAttributes.append(attributes)
contentHeight = max(contentHeight, frame.maxY)
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in cachedAttributes {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
}
Finally if you want to learn more about collection view custom layout check this helpful blog https://www.kodeco.com/4829472-uicollectionview-custom-layout-tutorial-pinterest