Creating a Scrolling Parallax Effect with SwiftUI

Prathamesh Bhuravane
3 min readDec 20, 2023

--

SwiftUI, renowned for its elegance and simplicity, offers an exciting canvas for crafting visually compelling user interfaces. In this tutorial, we’ll embark on a journey to master the art of creating a mesmerizing parallax effect using SwiftUI. Our canvas will be adorned with city cards, represented by the CityCard model.

Introducing the CityCard Model

struct CityCard: Identifiable, Hashable {
var id: UUID = .init()
var title: String
var subTitle: String
var image: String
}
// Sample data for trip cards
var tripCards: [CityCard] = [
.init(title: "Mumbai", subTitle: "India", image: "Pic 3"),
.init(title: "New York", subTitle: "USA", image: "Pic 2"),
.init(title: "London", subTitle: "England", image: "Pic 1"),
.init(title: "Alaska", subTitle: "USA", image: "Pic 5"),
.init(title: "Paris", subTitle: "France", image: "Pic 4")
]

Our journey begins with defining the CityCard model, encapsulating the essence of each city card – title, subtitle, and a captivating image.

Crafting the Parallax Symphony in ContentView

Part 1: Grasping the dimensions with GeometryReader

struct ContentView: View {
var body: some View {
VStack {
GeometryReader(content: { geometry in
let size = geometry.size

Part 2: Horizontal ScrollView, the stage for the parallax effect

GeometryReader(content: { geometry in
let size = geometry.size

ScrollView(.horizontal) {
HStack(spacing: 5) {

Part 3: Embarking on a journey through tripCards

ScrollView(.horizontal) {
HStack(spacing: 5) {
ForEach(tripCards) { card in

Part 4: Grasping individual card dimensions

HStack(spacing: 5) {
ForEach(tripCards) { card in
GeometryReader(content: { geo in
let cardSize = geo.size
let minX = min(geo.frame(in: .scrollView).minX * 1.4, geo.size.width * 1.4)

Part 5: Elevating the Parallax Image

GeometryReader(content: { geo in
let cardSize = geo.size
let minX = min(geo.frame(in: .scrollView).minX * 1.4, geo.size.width * 1.4)

Image(card.image)
.resizable()
.aspectRatio(contentMode: .fill)
.scaleEffect(1.2)
.offset(x: -minX)
.frame(width: cardSize.width, height: cardSize.height)

Part 6: Enchanting overlay with city name

Image(card.image)
.resizable()
.aspectRatio(contentMode: .fill)
.scaleEffect(1.2)
.offset(x: -minX)
.frame(width: cardSize.width, height: cardSize.height)
.overlay {
addCityName(card: card)
}
.clipShape(.rect(cornerRadius: 15))
.shadow(color: .black.opacity(0.25), radius: 8, x: 5, y: 10)
})

Part 7: Adding finesse with size and scale transition

                                    .overlay {

addCityName(card: card)
}
.clipShape(.rect(cornerRadius: 15))
.shadow(color: .black.opacity(0.25), radius: 8, x: 5, y: 10)
})

.frame(width: size.width - 60, height: size.height - 50)
.scrollTransition(.interactive, axis: .horizontal) { view, phase in
view.scaleEffect(phase.isIdentity ? 1 : 0.95)
}
}
}
.padding(.horizontal, 30)
.scrollTargetLayout()
.frame(height: size.height, alignment: .top)
}
.scrollTargetBehavior(.viewAligned)
.scrollIndicators(.hidden)
})
.frame(height: 550)
.padding(.horizontal, -15)
.padding(.top, 10)
}
.padding(15)
}
}

In this symphony of SwiftUI, we gracefully introduce the ContentView structure, harmonizing the elements to create an immersive parallax effect. Each part plays a crucial role, contributing to the overall masterpiece.

Enchanting the Overlay: addCityName Function

    @ViewBuilder
func addCityName(card: CityCard) -> some View {
let gardientColors : [Color] = [.clear,.clear,.clear,.clear,.clear,.black.opacity(0.1),.black.opacity (0.5),.black]

ZStack(alignment:
.bottomLeading, content: {
LinearGradient (colors: gardientColors,
startPoint: .top, endPoint: .bottom)
VStack(alignment: .leading, spacing: 4,content:{
Text (card.title)
.font(.title2)
.fontWeight(.black)
.foregroundStyle(.white)
Text (card.subTitle)
.font(.callout)
.foregroundStyle(.white.opacity(0.8))
})
.padding(20)
})
}

The addCityName function enchants our city cards with an elegant overlay, showcasing the city's title and subtitle in a beautifully stylized format.

Open Source Goodness: GitHub Repository

Explore and experiment with the code by accessing the GitHub repository:

SwiftUI Parallax Demo — https://github.com/PrathameshBhuravane/Parallax.git

Clone the repository to dive into the details and contribute to the magic.

Conclusion

With SwiftUI’s elegance and our orchestration skills, we’ve created a mesmerising parallax effect that breathes life into city cards. The CityCard model lays the foundation, and the ContentView structure orchestrates a symphony of visuals. As you continue your SwiftUI journey, let this tutorial serve as a guide to mastering the art of parallax effects in your applications. Happy coding!

--

--