Changing between colors based on UIScrollView's contentOffset
Asked Answered
B

3

5

I have a horizontal UIScrollView that has a width of 960, enough to hold 3 UIViewController view's.

Each view simply has a background color. The first is pink, the second is blue, and the third is green.

I want to blend/mix/fade the visible colors together as the user scrolls.

So if you were scrolling from page 1 (pink) to page 2 (blue), there would be some type of mix/blend/fade of pink and blue before finally landing on the final blue color when the user fully swiped to the 2nd page.

I found a question about exactly what I'm trying to do, and I have implemented the answer which can be found here: https://mcmap.net/q/1600310/-uiscrollview-animation-depending-on-content-offset

This answer does work, my only problem is this answer was only created to use 2 screens/colors, and I have 3 screens/colors.

I understand the basics of this answer and that it depends on the UIScrollView's contentOffset.x to calculate the current color, but other than that I am terrible at math which is holding me back from figuring out how to modify this to use a third color.

Bishop answered 27/8, 2015 at 22:33 Comment(1)
Answered here https://mcmap.net/q/1922238/-make-background-color-change-during-scrollApotropaic
R
5

Yes you can definitely use this for three colours (or more).

For three colours (say red, green, blue) you will have red at 0.0, green at 0.5 and blue at 1.0. So you just have to split off the method between the two fades (red-green and green-blue will be calculated separately).

Using my code from before you get the method...

// this just gets the percentage offset.
// 0,0 = no scroll
// 1,1 = maximum scroll
- (void)scrollView:(UIScrollView *)scrollView didScrollToPercentageOffset:(CGPoint)percentageOffset
{
    // get your colours to fade between
    NSArray *colours = @[[UIColor redColor], [UIColor yellowColor], [UIColor purpleColor]];

    // choose the colours to fade between based on the percentage.
    if (percentageOffset.x < 0.5) {
        // multiply the offset by 2 because we want 0.5 to be 100%
        self.backgroundColor = [self fadeFromColor:colours[0] toColor:colours[1] withPercentage:percentageOffset.x*2];
    } else {
        // minus 0.5 because we want 0.5 to be 0%
        self.backgroundColor = [self fadeFromColor:colours[1] toColor:colours[2] withPercentage:(percentageOffset.x-0.5)*2];
    }
}

// this is a more generic method to fade between two colours
// it allows the colours to be passed in as parameters
- (UIColor *)fadeFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor withPercentage:(CGFloat)percentage
{
    // get the RGBA values from the colours
    CGFloat fromRed, fromGreen, fromBlue, fromAlpha;
    [fromColor getRed:&fromRed green:&fromGreen blue:&fromBlue alpha:&fromAlpha];

    CGFloat toRed, toGreen, toBlue, toAlpha;
    [toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha];

    //calculate the actual RGBA values of the fade colour
    CGFloat red = (toRed - fromRed) * percentage + fromRed;
    CGFloat green = (toGreen - fromGreen) * percentage + fromGreen;
    CGFloat blue = (toBlue - fromBlue) * percentage + fromBlue;
    CGFloat alpha = (toAlpha - fromAlpha) * percentage + fromAlpha;

    // return the fade colour
    return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}

This will fade nicely between all three colours.

You can add more colours to this and change how it splits up the percentages.

Radiculitis answered 27/8, 2015 at 23:7 Comment(0)
B
7

The answer Fogmeister posted works perfectly!

Here's the same answer translated to Swift:

// this just gets the percentage offset.
// 0,0 = no scroll
// 1,1 = maximum scroll

func scrollViewdidScrollToPercentageOffset(scrollView: UIScrollView, percentageOffset: CGPoint) {
    // get your colours to fade between
    var colors = [UIColor.redColor(), UIColor.blueColor(), UIColor.greenColor()]

    // choose the colours to fade between based on the percentage.
    if (percentageOffset.x < 0.5) {
        // multiply the offset by 2 because we want 0.5 to be 100%
        scrollView.backgroundColor = fadeFromColor(colors[0], colors[1], percentageOffset.x*2)
    } else {
        // minus 0.5 because we want 0.5 to be 0%
        scrollView.backgroundColor = fadeFromColor(colors[1], colors[2], (percentageOffset.x - 0.5)*2)
    }
}

func fadeFromColor(fromColor: UIColor, toColor: UIColor, withPercentage: CGFloat) -> UIColor {
    var fromRed: CGFloat = 0.0
    var fromGreen: CGFloat = 0.0
    var fromBlue: CGFloat = 0.0
    var fromAlpha: CGFloat = 0.0

    fromColor.getRed(&fromRed, green: &fromGreen, blue: &fromBlue, alpha: &fromAlpha)

    var toRed: CGFloat = 0.0
    var toGreen: CGFloat = 0.0
    var toBlue: CGFloat = 0.0
    var toAlpha: CGFloat = 0.0

    toColor.getRed(&toRed, green: &toGreen, blue: &toBlue, alpha: &toAlpha)

    //calculate the actual RGBA values of the fade colour
    var red = (toRed - fromRed) * withPercentage + fromRed
    var green = (toGreen - fromGreen) * withPercentage + fromGreen
    var blue = (toBlue - fromBlue) * withPercentage + fromBlue
    var alpha = (toAlpha - fromAlpha) * withPercentage + fromAlpha

    // return the fade colour
    return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}
Bishop answered 27/8, 2015 at 23:33 Comment(2)
I'm using this code right now in my app, but having trouble when there's more then 2 screens as you mentioned. How did you manage to resolve it because isn't the offset 0.5 meant for only 2 screens?Promiscuity
Thanks for converting to Swift. Saved several minutes :)Dilley
R
5

Yes you can definitely use this for three colours (or more).

For three colours (say red, green, blue) you will have red at 0.0, green at 0.5 and blue at 1.0. So you just have to split off the method between the two fades (red-green and green-blue will be calculated separately).

Using my code from before you get the method...

// this just gets the percentage offset.
// 0,0 = no scroll
// 1,1 = maximum scroll
- (void)scrollView:(UIScrollView *)scrollView didScrollToPercentageOffset:(CGPoint)percentageOffset
{
    // get your colours to fade between
    NSArray *colours = @[[UIColor redColor], [UIColor yellowColor], [UIColor purpleColor]];

    // choose the colours to fade between based on the percentage.
    if (percentageOffset.x < 0.5) {
        // multiply the offset by 2 because we want 0.5 to be 100%
        self.backgroundColor = [self fadeFromColor:colours[0] toColor:colours[1] withPercentage:percentageOffset.x*2];
    } else {
        // minus 0.5 because we want 0.5 to be 0%
        self.backgroundColor = [self fadeFromColor:colours[1] toColor:colours[2] withPercentage:(percentageOffset.x-0.5)*2];
    }
}

// this is a more generic method to fade between two colours
// it allows the colours to be passed in as parameters
- (UIColor *)fadeFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor withPercentage:(CGFloat)percentage
{
    // get the RGBA values from the colours
    CGFloat fromRed, fromGreen, fromBlue, fromAlpha;
    [fromColor getRed:&fromRed green:&fromGreen blue:&fromBlue alpha:&fromAlpha];

    CGFloat toRed, toGreen, toBlue, toAlpha;
    [toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha];

    //calculate the actual RGBA values of the fade colour
    CGFloat red = (toRed - fromRed) * percentage + fromRed;
    CGFloat green = (toGreen - fromGreen) * percentage + fromGreen;
    CGFloat blue = (toBlue - fromBlue) * percentage + fromBlue;
    CGFloat alpha = (toAlpha - fromAlpha) * percentage + fromAlpha;

    // return the fade colour
    return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}

This will fade nicely between all three colours.

You can add more colours to this and change how it splits up the percentages.

Radiculitis answered 27/8, 2015 at 23:7 Comment(0)
T
3

Here's the answer for any quantity of colors (or pages in UICollectionView in my case) with Swift 4.2:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let maximumOffset = scrollView.contentSize.width - scrollView.frame.width
    let currentOffset = scrollView.contentOffset.x
    let offsetPercentage = currentOffset / maximumOffset
    let sharesCount = CGFloat(colorsArray.count - 1)

    var currentShareIndex: CGFloat = 0
    switch offsetPercentage {
    case  ..<0:
        currentShareIndex = 0
    case 0..<1:
        currentShareIndex = floor(offsetPercentage * sharesCount)
    case 1... :
        currentShareIndex = sharesCount - 1
    default: break
    }

    let firstColorIndex = Int(currentShareIndex)
    let secondColorIndex = Int(currentShareIndex) + 1
    let colorPercentage = (offsetPercentage - currentShareIndex / sharesCount) * sharesCount

    scrollView.backgroundColor = UIColor.fade(from: colorsArray[firstColorIndex], to: colorsArray[secondColorIndex], with: colorPercentage)
}
Thelma answered 13/2, 2019 at 8:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.