Search code examples
iosuiscrollviewcalayermaskcagradientlayer

When using UIScrollView, how can I fade the left and right edges if content is outside the visible bounds?


Let's say I have a horizontally scrollable UIScrollView with various types of content. It could be images, UIViews etc. I would like to fade transparent the left and right edges if the content is outside those edges.

I have tried using the following but it doesn't appear to work correctly.

class CustomScrollView: UIScrollView {
    
    override func layoutSubviews() {
        super.layoutSubviews()
        print("layoutSubviews")
        
        if layer.mask == nil {
            let mask = CAGradientLayer()
            mask.locations = [0.0, 0.2, 0.8, 1.0]
            
            mask.startPoint = CGPoint(x:0, y:0)
            mask.endPoint = CGPoint(x:1, y:0)

            layer.mask = mask
        }
        
        if let mask = layer.mask as? CAGradientLayer {
            
            mask.bounds = self.frame
            let fadedColor = UIColor(white: 1.0, alpha: 0.0).cgColor
            let realColor = UIColor(white: 1.0, alpha: 1.0).cgColor

            var colors = [CGColor]()

            colors = [fadedColor, realColor, realColor, fadedColor]
                        
            mask.colors = colors

            CATransaction.begin()
            CATransaction.setDisableActions(true)
            CATransaction.commit()
        }
    }
}

Solution

  • You're close...

    You want to set the mask layer's frame, not its bounds.

    Also, you can do all of the gradient layer setup in the if equals nil block, instead of on every layout pass:

    class CustomScrollView: UIScrollView {
        
        override func layoutSubviews() {
            super.layoutSubviews()
            print("layoutSubviews")
            
            if layer.mask == nil {
                // do all the GradientLayer / Mask setup here
                let mask = CAGradientLayer()
                mask.locations = [0.0, 0.2, 0.8, 1.0]
                mask.startPoint = CGPoint(x:0, y:0)
                mask.endPoint = CGPoint(x:1, y:0)
                
                let fadedColor = UIColor(white: 1.0, alpha: 0.0).cgColor
                let realColor = UIColor(white: 1.0, alpha: 1.0).cgColor
                
                mask.colors = [fadedColor, realColor, realColor, fadedColor]
                
                self.layer.mask = mask
            }
            
            if let mask = layer.mask as? CAGradientLayer {
                CATransaction.begin()
                CATransaction.setDisableActions(true)
                mask.frame = self.bounds
                CATransaction.commit()
            }
        }
        
    }