Ice
Ice

Reputation: 757

How to create effect of stack of view for collection view?

I am try create effect like few view stack over other for collection view, I am not build effect like this in the past and can't understand how I can create this effect programmatically without images?

Can somebody explain me how I can repeat this effect or post link to code of tutorial?

Example of this effect below:

stack effect

Upvotes: 0

Views: 278

Answers (1)

Duncan C
Duncan C

Reputation: 131481

To get that effect you'd need to add layers under the collection view cells' layers that are smaller and shifted down a little from the cell's layers. You'd use the same background color as the cell on each layer that had a lower alpha than the cell's layer.

I created a sample project that demonstrates how to get the effect:

https://github.com/DuncanMC/CustomCollectionViewCell.git

The cells look like this:

enter image description here

The heavy lifting is in a custom subclass of UICollectionViewCell that I called MyCollectionViewCell

Here is that class:

class MyCollectionViewCell: UICollectionViewCell {

    public var contentCornerRadius: CGFloat = 0 {
        didSet {
            contentView.layer.cornerRadius = contentCornerRadius
            sizeLayerFrames()
        }

    }

    public var fraction: CGFloat = 0.075 { // What percent to shrink & shift the faded layers down (0.075 = 7.5%)
        didSet {
            sizeLayerFrames()
        }
    }
    private var layerMask =   CAShapeLayer()
    private var layer1 = CALayer()
    private var layer2 = CALayer()


    // Use this function to set the cell's background color.
    // (You can't set the view's background color, since we Don't clip the view to it's bounds.)

    // Be sure to set the background color explicitly, since by default it sets a random color that will persist
    // as the cells are recylced, causing your cell colors to move around as the user scrolls
    public func setBackgroundColor(_ color: UIColor)  {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        color.getRed(&red, green: &green, blue: &blue, alpha: nil)
        contentView.layer.backgroundColor = color.cgColor

        //Make the first extra layer have the same color as the cell's layer, but with alpha 0.25
        layer1.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 0.25).cgColor

        //Make the second extra layer have the same color as the cell's layer, but with alpha 0.125
        layer2.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 0.125).cgColor
    }

    @IBOutlet weak var customLabel: UILabel!

    //Do The initial setup once the cell is loaded.
    //Note that t
    override func awakeFromNib() {
        contentView.layer.masksToBounds = false

        // Color each cell's layer some random hue (change to set whatever color you desire.)
        //For testing, use a color based on a random hue and fairly high random brightness.
        let hue = CGFloat.random(in: 0...360)
        let brightness = CGFloat.random(in: 0.8...1.0)
        layer.masksToBounds = false
        setBackgroundColor(UIColor(hue: hue, saturation: 1, brightness: brightness, alpha: 1))


        // Make the inside of the shape layer white (opaque), The color doesn't matter - just that the alpha value is 1
        // and the outside clear (transparent)
        layerMask.fillColor = UIColor.white.cgColor
        layerMask.backgroundColor = UIColor.clear.cgColor

        //With the even/odd rule, the inner shape will not be filled (we'll only fill the part NOT in the inner shape)
        layerMask.fillRule = .evenOdd
        contentCornerRadius = 30


        sizeLayerFrames()

        contentView.layer.addSublayer(layer1)
        layer1.addSublayer(layer2)
        layer1.mask = layerMask
    }

    private func sizeLayerFrames() {
        layer1.cornerRadius = contentCornerRadius
        layer2.cornerRadius = contentCornerRadius


        let viewBounds = bounds //Use the layer's bounds as the starting point for the extra layers.
        var frame1 = viewBounds
        frame1.origin.y += viewBounds.size.height * fraction
        frame1.origin.x += viewBounds.size.width * fraction

        frame1.size.width *= CGFloat(1 - 2 * fraction)
        layer1.frame = frame1
        var frame2 = viewBounds
        frame2.origin.y += viewBounds.size.height * 0.75 * fraction
        frame2.origin.x += viewBounds.size.width  * fraction

        frame2.size.width *= CGFloat(1 - 4 * fraction)
        layer2.frame = frame2

        //Create a mask layer to clip the extra layers.
        var maskFrame = viewBounds

        //We are going to install the mask on layer1, so offeset the frame to cover the whole view contents
        maskFrame.origin.y -= viewBounds.size.height * fraction
        maskFrame.origin.x -= viewBounds.size.width * fraction
        maskFrame.size.height += viewBounds.size.height * fraction * 1.75
        layerMask.frame = maskFrame
        maskFrame = viewBounds
        let innerPath = UIBezierPath(roundedRect: maskFrame, cornerRadius: 30)
        maskFrame.size.height += viewBounds.size.height * fraction * 1.75
        let combinedPath = UIBezierPath(rect: maskFrame)
        combinedPath.append(innerPath)
        layerMask.path = combinedPath.cgPath
    }

    override var bounds: CGRect {
        didSet {
            sizeLayerFrames()
        }
    }
}

Upvotes: 1

Related Questions