40

Given an arbitrary UIView on iOS, is there a way using Core Graphics (CAGradientLayer comes to mind) to apply a "foreground-transparent" gradient to it?

I can't use a standard CAGradientLayer because the background is more complex than a UIColor. I also can't overlay a PNG because the background will change as my subview is scrolled along its parent vertical scrollview (see image).

I have a non-elegant fallback: have my uiview clip its subviews and move a pre-rendered gradient png of the background as the parent scrollview is scrolled.

transparent uiview gradient problem

jpsim
  • 14,329
  • 6
  • 51
  • 68
  • What is the background? How does it "change" as the scroll view scrolls? – rob mayoff Jun 01 '12 at 17:38
  • As @jab mentioned in their answer below, it's probably more useful to think of this problem generally, as applying a gradient mask to any view, rather than a gradient mask to a _scroll_ view (just like the OP requests). Creating such a container view (using one of the methods described below), then adding the scroll view as a subview, can create the desired effect. It looks like there's some confusion from people trying to apply the gradient to a scroll view itself and not getting quite what they want. – SlimeBaron Dec 21 '21 at 16:14

5 Answers5

100

This was an embarrassingly easy fix: apply a CAGradientLayer as my subview's mask.

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = _fileTypeScrollView.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[UIColor whiteColor].CGColor, (id)[UIColor clearColor].CGColor, nil];
gradientLayer.startPoint = CGPointMake(0.8f, 1.0f);
gradientLayer.endPoint = CGPointMake(1.0f, 1.0f);
_fileTypeScrollView.layer.mask = gradientLayer;

Thanks to Cocoanetics for pointing me in the right direction!

Leena
  • 2,678
  • 1
  • 30
  • 43
jpsim
  • 14,329
  • 6
  • 51
  • 68
  • 7
    I used this code to add a gradient to a very similar horizontal UIScrollView. It looks all good, but when you actually scroll- the views coming in from the right are transparent. It is as if the gradient applied to the actual views sitting on the scrollview and not as a layer above them. What am I doing wrong? – David Ben Ari Dec 26 '12 at 14:09
  • 1
    How to create a vertical gradient suing this code ? – Sebastian Boldt Jun 04 '15 at 08:11
  • Future readers: here's another helpful thread - http://stackoverflow.com/questions/23074539/programmatically-create-a-uiview-with-color-gradient – kbpontius Mar 11 '16 at 23:27
  • 1
    As noted in https://stackoverflow.com/a/53307483/592454, the gradient layer should probably be applied to the scroll view superview, not the scroll view itself. – elitalon Sep 19 '19 at 07:08
10

This is how I'll do.

Step 1 Define a custom gradient view (Swift 4):

import UIKit

class GradientView: UIView {
    override open class var layerClass: AnyClass {
        return CAGradientLayer.classForCoder()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let gradientLayer = self.layer as! CAGradientLayer
        gradientLayer.colors = [
            UIColor.white.cgColor,
            UIColor.init(white: 1, alpha: 0).cgColor
        ]
        backgroundColor = UIColor.clear
    }
}

Step 2 - Drag and drop a UIView in your storyboard and set its custom class to GradientView

enter image description here

As an example, this is how the above gradient view looks like:


https://github.com/yzhong52/GradientViewDemo

Yuchen
  • 30,852
  • 26
  • 164
  • 234
  • @Elsammak I think it is because you didn't set the background color of the UIview to clear. See Smaily Carrilho's edits for detail. – Yuchen Nov 12 '17 at 13:29
  • mention `mapView.addSubview(mapGradientView)` additionally. – Codetard Feb 24 '18 at 12:00
  • @SatnamSync Thanks for the edit. But step 2 is equivalent to `mapView.addSubview(mapGradientView)`. Essentially, if we have the view in the storyboard, we should not have to call this again. But if you want to do things in code, that'll be the right way to do. – Yuchen Feb 24 '18 at 12:51
  • @YuchenZhong I just tried this, programmatically and storyboard both ways it is required. I don't know if it's required with Google maps, but UIView(Class - MKMapView, that's how I'm using it) It does need to have that mentioned. – Codetard Feb 24 '18 at 13:07
  • 1
    @SatnamSync you probably have a bug in your code. I put tgt a demo, see if that helps. https://github.com/yzhong52/GradientViewDemo – Yuchen Feb 24 '18 at 16:36
4

I used the accepted (OP's) answer above and ran into the same issue noted in an upvoted comment - when the view scrolls, everything that started offscreen is now transparent, covered by the mask.

The solution was to add the gradient layer as the superview's mask, not the scroll view's mask. In my case, I'm using a text view, which is contained inside a view called contentView.

I added a third color and used locations instead of startPoint and endPoint, so that items below the text view are still visible.

let gradientLayer = CAGradientLayer()
gradientLayer.frame = self.contentView!.bounds
gradientLayer.colors = [UIColor.white.cgColor, UIColor.clear.cgColor, UIColor.white.cgColor]

// choose position for gradient, aligned to bottom of text view
let bottomOffset = (self.textView!.frame.size.height + self.textView!.frame.origin.y + 5)/self.contentView!.bounds.size.height
let topOffset = bottomOffset - 0.1
let bottomCoordinate = NSNumber(value: Double(bottomOffset))
let topCoordinate = NSNumber(value: Double(topOffset))
gradientLayer.locations = [topCoordinate, bottomCoordinate, bottomCoordinate]

self.contentView!.layer.mask = gradientLayer

Before, the text that started offscreen was permanently invisible. With my modifications, scrolling works as expected, and the "Close" button is not covered by the mask.

screenshot

jab
  • 4,053
  • 3
  • 33
  • 40
2

Gradient Example

I just ran into the same issue and wound up writing my own class. It seems like serious overkill, but it was the only way I could find to do gradients with transparency. You can see my writeup and code example here

It basically comes down to a custom UIView that creates two images. One is a solid color, the other is a gradient that is used as an image mask. From there I applied the resulting image to the uiview.layer.content.

I hope it helps, Joe

Joe Andolina
  • 648
  • 1
  • 11
  • 23
0

I hate to say it, but I think that you are into the CUSTOM UIView land. I think that I would try to implement this in a custom UIView overiding the drawRect routine.

With this, you could have that view, place on top of your actual scrollview, and have your gradient view (if you will) "pass-on" all touch events (i.e. relinquish first responder).

trumpetlicks
  • 7,033
  • 2
  • 19
  • 33