Would you please give more explanation of the code? like why should we calculate maxY or yOffset this way in the video?
Otherwise, from 2:40, you are reading the code which I can download and checkout out myself without much explanation.
This is a general suggestion. As I found the topic is pretty complicated so more details we require.
Let’s say on the initial screen after running the app got 5 cells visible including the first featured cell.
When scrolling down, I get a strange behaviour. The initial featured cell and the next cell got the behaviour expected. However, I have the cell 6 appearing over the other cells.
How can I fix it?
Here is the code I have replicated from the lesson:
//
// UltravisualLayout.swift
// RWDevCon
//
// Created by Mic Pringle on 27/02/2015.
// Copyright (c) 2015 Ray Wenderlich. All rights reserved.
//
import UIKit
/* The heights are declared as constants outside of the class so they can be easily referenced elsewhere */
struct UltravisualLayoutConstants {
struct Cell {
/* The height of the non-featured cell */
static let standardHeight: CGFloat = 100
/* The height of the first visible cell */
static let featuredHeight: CGFloat = 280
}
}
class UltravisualLayout: UICollectionViewLayout {
// MARK: Properties and Variables
/* The amount the user needs to scroll before the featured cell changes */
let dragOffset: CGFloat = 180.0
var cache = [UICollectionViewLayoutAttributes]()
/* Returns the item index of the currently featured cell */
var featuredItemIndex: Int {
get {
/* Use max to make sure the featureItemIndex is never < 0 */
return max(0, Int(collectionView!.contentOffset.y / dragOffset))
}
}
/* Returns a value between 0 and 1 that represents how close the next cell is to becoming the featured cell */
var nextItemPercentageOffset: CGFloat {
get {
return (collectionView!.contentOffset.y / dragOffset) - CGFloat(featuredItemIndex)
}
}
/* Returns the width of the collection view */
var width: CGFloat {
get {
return collectionView!.bounds.width
}
}
/* Returns the height of the collection view */
var height: CGFloat {
get {
return collectionView!.bounds.height
}
}
/* Returns the number of items in the collection view */
var numberOfItems: Int {
get {
return collectionView!.numberOfItems(inSection: 0)
}
}
// MARK: UICollectionViewLayout
/* Return the size of all the content in the collection view */
override var collectionViewContentSize : CGSize {
let contentHeight = (CGFloat(numberOfItems) * dragOffset) + (height - dragOffset)
return CGSize(width: width, height: contentHeight)
}
override func prepare() {
cache.removeAll(keepingCapacity: false)
let standardHeight = UltravisualLayoutConstants.Cell.standardHeight
let featuredHeight = UltravisualLayoutConstants.Cell.featuredHeight
var frame = CGRect.zero
var y:CGFloat = 0
for item in 0 ... (numberOfItems - 1) {
let indexPath = NSIndexPath(item: item, section: 0)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
attributes.zIndex = item
var height = standardHeight
if indexPath.item == featuredItemIndex {
let yOffset = standardHeight * nextItemPercentageOffset
y = self.collectionView!.contentOffset.y - yOffset
height = featuredHeight
} else if (indexPath as NSIndexPath).item == (featuredItemIndex + 1) && (indexPath as NSIndexPath).item != numberOfItems {
let maxY = y + standardHeight
height = standardHeight + max((featuredHeight - standardHeight) * nextItemPercentageOffset, 0)
y = maxY - height
}
frame = CGRect(x: 0, y: y, width: width, height: height)
attributes.frame = frame
cache.append(attributes)
y = frame.maxY
print("y value is: \(y)")
}
}
/* Return all attributes in the cache whose frame intersects with the rect passed to the method */
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if attributes.frame.intersects(rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return self.cache[indexPath.row]
}
/* Return true so that the layout is continuously invalidated as the user scrolls */
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
This layout does not work with iOS 10 prefetching. If you are running this tutorial code on an iOS 10 device or simulator, put the following code in InspirationsViewController.viewDidLoad():
if #available(iOS 10.0, *) {
collectionView!.prefetchingEnabled = false
}
I’m not sure why this layout doesn’t work with iOS 10 prefetching, but the code above fixes it for me.
Thanks that sorted me too, i think cells that are being prefetched are given a value which is on the screen, when if fact they shouldn’t be visible. does anyone know how to cater for these
Good video tutorial. I was wondering if it would be possible to provide a version of the finished project without a story board file. So basically everything created programatically. Being able to compare both a storyboard version and non storyboard version would help me understand everything even better.
@ys_xs I agree, this course overall has felt very dense with little explanation. Thank you Michael Briscoe for making this course. Next time, could you please explain what is happening and why?
This is the worst video ever, where did MosaicViewLayout come from? He only shows how to set the delegate methods. In general this video course for collectionview feels like the shortest and the one in which I’ve spent more time in, trying to read code that the instructor didn’t explain.