Fancy picker: story of one background

I am currently working on an app that doesn’t have a lot of features, but is full of custom user interactions and visual effects.

Sometimes when you get something like this from a designer, you don’t know where to start. In this case I started with a horizontal picker, but that’s too boring for a blog post, so let’s skip it. Next thing that looked complicated, but turned out to be pretty easy to implement, is this nice gradient on the background.

So I had this design as a starting point:

And a horizontal picker (or for now just a plain collection view, to simplify the example). You can check the code in the playground.

UICollectionView with horizontal flow layout

And my task is to add a gradient background that will update when the collection view scrolls. Below is a GradientView I started with. Nothing magic in it, just overriding the draw(_ rect:) — what can be easier?

Simple gradient

Here is my model by the way:

Model: Color and Gradient

Now we can combine the gradient and the collection view in a Picker view and add it to the playground:

Swift playground
GradientView in action

Nice! All we need to do is to update the gradient colors depending on the current contentOffset of the collection view.

First of all we want to be notified when scroll happens. Then we need to know what part of the content is visible right now. I added a visibleRange property and some delegate calls to the collection view:

Notify delegate that visible range has changed

Now we need to catch and handle scroll events and update the gradient. We can do it in the Picker class.

Handle scroll events in the Picker

Clipping a gradient is pretty easy if you are using an RGB color model.

Clip gradient to a visible range
Gradient is updated

Hey, it is working! But looks a bit boring, design was better. Here you don’t even see the gradient anymore and the more items we have, the less noticeable it will be. What can we do with it? May be make the color range bigger?

Yahoo!

OMG. May be not. May be some kind of a semi transparent black gradient on top?

Try the gradient

Nah. It just looks like a boring semi transparent black gradient on top...

My solution was to use a blend mode. If you’ve never tried them before, you should! You define how two layers are combined and the effect can be very interesting. I used CGBlendMode.overlay, but there are many more.

Updated draw(_ rect:) method in GradientView

What it does is: it puts a gradient from white to black on top of the gradient we already had. Thanks to the blend mode, the image beneath becomes lighter where the white is and darker where the black is. I made this overlay semi transparent to reduce the effect a bit.

With blend mode

Tadaa!

It took some time to figure out, but once done it is super easy and adds a nice special touch to the app.

You can check the playground on github or try out the app!