4

I have a scrollview>ContentView>TextLabel setup where the gradient is showing up but not working how I want it to. It's a clear background scrollView, so all I'm looking for is a clear gradient mask over the text. I found something similar to I'm looking for in Objective-C but that thread doesn't have a Swift solution.

My end goal seems like something most people might use, so I'm surprised there's not a Swift version yet. The functionality I'm trying to code is:

  1. Sometimes the text will fit perfectly in the scrollView's fixed size so there should be no gradient.
  2. When the text is longer than can fit and so some of it is below the scrollView's bottom cutoff, the bottom of the view should fade to clear.
  3. Once the user scrolls down and the top should fade, indicating that there's more above.

I tried this code to handle bullet #2:

        let gradient = CAGradientLayer()
        gradient.frame = self.bio_ScrollView.superview!.bounds ?? CGRectNull
        gradient.colors = [UIColor.whiteColor().CGColor, UIColor.clearColor().CGColor]
        //gradient.locations = [0, 0.15, 0.25, 0.75, 0.85, 1.0]
        gradient.locations = [0.6, 1.0]
        self.bio_ScrollView.superview!.layer.mask = gradient

But it fades everything, including the button below it, which is clearly not in the scrollview:

scrollView with gradient

If I remove the .superview and apply it directly to the scrollView, it just fades all the text below the initial part that was visible:

scrollview gradient not working

Does anyone know anything about how to implement this correctly?

Community
  • 1
  • 1
Dave G
  • 12,042
  • 7
  • 57
  • 83

1 Answers1

2

Figured it out. First, make sure you've added the right view hierarchy. The scroll view needs to be embedded in a container view (which is what the gradient will be applied to):

Gradient View Hierarchy

  1. Top/container view: Set this up however you want
  2. Scrollview: Pin all edges to container (4 total constraints)
  3. Scrollview content view: Pin edges + Center X (5 total constraints)
  4. Label: Pin edges (4 total constraints)

Add "scrollViewDelegate" to the ViewController class:

class ViewController_WithScrollView: UIViewController, UIScrollViewDelegate {
   ....

Connect the four views listed above with IBOutlets. Then, set your scrollView delegate within the viewDidLoad:

override func viewDidLoad() {
    super.viewDidLoad()

    yourScrollView.delegate = self
    //+All your other viewDidLoad stuff
}

Then implement the scrollViewDidScroll func, which will run automatically thanks to the work you did above.

func scrollViewDidScroll (scrollView: UIScrollView) {
    if scrollView.isAtTop {
        self.applyGradient_To("Bot")
    } else if scrollView.isAtBottom {
        self.applyGradient_To("Top")
    } else {
        self.applyGradient_To("Both")
    }
} 

Then add this magical gradient code:

func applyGradient_To (state: String) {
    let gradient = CAGradientLayer()
    gradient.frame = self.yourScrollView.superview!.bounds ?? CGRectNull

    switch state {
    case "Top":
        gradient.colors = [UIColor.clearColor().CGColor,UIColor.whiteColor().CGColor]
        gradient.locations = [0.0,0.2]
    case "Bot":
        gradient.colors = [UIColor.whiteColor().CGColor, UIColor.clearColor().CGColor]
        gradient.locations = [0.8,1.0]
    default:
        gradient.colors = [UIColor.clearColor().CGColor,UIColor.whiteColor().CGColor,UIColor.whiteColor().CGColor, UIColor.clearColor().CGColor]
        gradient.locations = [0.0,0.2,0.8,1.0]
    }
    self.yourScrollView.superview!.layer.mask = nil
    self.yourScrollView.superview!.layer.mask = gradient
}

That should do it!

Dave G
  • 12,042
  • 7
  • 57
  • 83
  • Worked for me once I added an extension to UIScrollView to use the isAtTop property. http://stackoverflow.com/a/14745900/4228969 – toddg Feb 28 '17 at 21:35