I'm trying to replicate eBay's listing menu on their iOS app in which users can scroll through different images of a listing. I noticed that the background color is a solid color that replicates the surround colors of the background in each image.
As I scroll through a different image (different page) with a different surround background, the actual background of the UICollectionView
changes to somehow reflect it.
Here's what I mean:
As you can see in the first picture, the background is a light color, somewhat resembling the image's background. As I scroll halfway to the 2nd picture, the background turns darker.
Finally:
My setup is similar:
Using DominantColor, I was able to set the UICollectionView
's background color with each UIImage
's dominant color. As the user scrolls half-way between the first and second page, the UICollectionView
background color is set the 2nd page UIImage
's dominant color.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as UICollectionViewCell
let frame: CGRect = CGRect(x: 0, y: 0, width: imagesView.frame.size.width, height: imagesView.frame.size.height)
let subView: UIImageView = UIImageView(frame: frame)
subView.image = imagesArray[indexPath.row].image
subView.contentMode = UIViewContentMode.scaleAspectFit
if colorsArray.count > 0
{
// Set background dominant color of first image
collectionView.backgroundColor = colorsArray[0]
}
cell.addSubview(subView)
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let scrollValue = (scrollView.contentOffset.x / UIScreen.main.bounds.width)
let pageWidth: CGFloat = imagesCollectionView.bounds.size.width
let currentPage: Int = Int( floor( (imagesCollectionView.contentOffset.x - pageWidth / 2) / pageWidth) + 1)
if scrollValue != 1
{
UIView.animate(withDuration: 1, animations: {
self.imagesCollectionView.backgroundColor = self.colorsArray[currentPage]
})
}
else
{
UIView.animate(withDuration: 1, animations: {
self.imagesCollectionView.backgroundColor = self.colorsArray[currentPage]
})
}
}
However, I'm having a difficult time on how to slowly transition from the current background color to the next background color as soon as the user starts scrolling, as seen in the 2nd picture above.
The way it is currently implemented as above in scrollViewDidScroll
, it starts to fade into the next color when the page is half-way scrolled between the next page, and with a 1 second animation, it will become that color within 1 second, regardless of if the next page is half-way or fully shown.
How can I achieve this?
Using Fogmeister's answer in obj-C and user3344977's nicely conversion of it to Swift, as well as Tonton's guidance, I came up with the solution:
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let pageWidth: CGFloat = imagesCollectionView.bounds.size.width
let currentPage: Int = Int( floor( (imagesCollectionView.contentOffset.x - pageWidth / 2) / pageWidth) + 1)
let maximumHorizontalOffset: CGFloat = scrollView.contentSize.width - scrollView.frame.size.width
let currentHorizontalOffset: CGFloat = scrollView.contentOffset.x
let percentageHorizontalOffset: CGFloat = currentHorizontalOffset / maximumHorizontalOffset
if percentageHorizontalOffset < 0.5
{
imagesCollectionView.backgroundColor = fadeFromColor(fromColor: colorsArray[currentPage], toColor: colorsArray[currentPage + 1], withPercentage: percentageHorizontalOffset)
}
else
{
imagesCollectionView.backgroundColor = fadeFromColor(fromColor: colorsArray[currentPage - 1], toColor: colorsArray[currentPage], withPercentage: percentageHorizontalOffset)
}
}
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
// Get the RGBA values from the colours
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
let red = (toRed - fromRed) * withPercentage + fromRed;
let green = (toGreen - fromGreen) * withPercentage + fromGreen;
let blue = (toBlue - fromBlue) * withPercentage + fromBlue;
let alpha = (toAlpha - fromAlpha) * withPercentage + fromAlpha;
// Return the fade colour
return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}
Now, when the user starts scrolling to the next/prev page, the background color will slowly change due to color alpha change in percentageHorizontalOffset
.
Thanks to all mentioned!
If the change of colour needs to be in sync with the scroll, ie, gradually changing the colour, you would probably want to the changes in scrollViewDidScroll using the scrollview from your collectionView
By getting the 2 background image colour (perhaps using Mike JS Choi's answer), you can compare their differences. Using the scroll value from 0 to 1, you can change the colour gradually.
Use some local variable to let the method know what is your current image
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let scrollValue = (scrollView.contentOffset.x / UIScreen.main.bounds.width) - 1
guard let nextColor: UIColor? = getBackgroundColor(from: nextImage) else { return }
let currentImage = getBackgroundColor(from: currentImage)
let currentIndex = pageControl.currentPage
if scrollValue != 0 {
backgroundColor = colorDifference(current: currentColor, next: nextColor, transitionValue: abs(scrollValue))
} else {
backgroundColor = nextColor
}
}
func colorDifference(current: UIColor, next: UIColor, transitionValue: Int) -> UIColor {
var currentRed: Float = 0
var currentGreen: Float = 0
var currentBlue: Float = 0
var currentAlpha: Float = 0
current.getRed(¤tRed, green: ¤tGreen, blue: ¤tBlue, alpha: ¤tAlpha)
var nextRed: Float = 0
var nextGreen: Float = 0
var nextBlue: Float = 0
var nextAlpha: Float = 0
next.getRed(&nextRed, green: &nextGreen, blue: &nextBlue, alpha: &nextAlpha)
let finalRed = (nextRed - currentRed) * transitionValue
let finalGreen = (nextGreen - currentGreen) * transitionValue
let finalBlue = (nextBlue - currentBlue) * transitionValue
let finalAlpha = (nextAlpha - currentAlpha) * transitionValue
let finalColor = UIColor(colorLiteralRed: finalRed, green: finalGreen, blue: finalBlue, alpha: finalAlpha)
return finalColor
}
So you will have to figure out how to store your current image is and getting the next image (which you could be scrolling forwards and back), then use those within the scrollViewDidScroll method.
glhf fam
The tricky part is to find a color corresponding to each picture. I once used TDImageColors which worked just fine : https://www.cocoacontrols.com/controls/tdimagecolors
Then all you have to do is transition from one color to another. As in @Tonton answer.