article

Wednesday, November 24, 2021

SwiftUI Horizontal Carousel Parallax Animation

SwiftUI Horizontal Carousel Parallax Animation

ContentView.swift
 
//
//  ContentView.swift
//  Swiftuitest
//
//  Created by Cairocoders
//

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        NavigationView {
            ScrollView {
                MoviesCarousel(categoryName: "Top Movies")
            }.navigationBarTitle("Browse", displayMode: .large)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
MoviesCarousel.swift
 
//
//  MoviesCarousel.swift
//  Swiftuitest
//
//  Created by Cairocoders
//

import SwiftUI

struct MoviesCarousel: View {
    
    let categoryName: String
    
    var body: some View {
        VStack {
            HStack {
                Text(categoryName)
                    .font(.system(size: 14, weight: .heavy))
                    .padding(.vertical, 6)
                    .padding(.horizontal, 12)
                    .background(Color.green)
                    .foregroundColor(.white)
                    .cornerRadius(2)
                Spacer()
            }.padding(.horizontal)
            .padding(.top)
            
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .top, spacing: 16) {
                    ForEach(latestmovie) { num in
                        GeometryReader { proxy in
                            let scale = getScale(proxy: proxy)
                            NavigationLink(
                                destination: MovieDetailsView(movie: num),
                                label: {
                                    VStack(spacing: 8) {
                                        Image(num.imageName)
                                            .resizable()
                                            .scaledToFill()
                                            .frame(width: 180)
                                            .clipped()
                                            .cornerRadius(8)
                                            .overlay(
                                                RoundedRectangle(cornerRadius: 8)
                                                    .stroke(Color(white: 0.4))
                                            )
                                            .shadow(radius: 3)
                                        Text(num.title)
                                            .font(.system(size: 16, weight: .semibold))
                                            .multilineTextAlignment(.center)
                                            .foregroundColor(.black)
                                        HStack(spacing: 0) {
                                            ForEach(0..<5) { num in
                                                Image(systemName: "star.fill")
                                                    .foregroundColor(.orange)
                                                    .font(.system(size: 14))
                                            }
                                        }.padding(.top, -4)
                                    }
                                })
                            
                                .scaleEffect(.init(width: scale, height: scale))
                                //.animation(.spring(), value: 1)
                                .animation(.easeOut(duration: 1))
                                
                                .padding(.vertical)
                        } //End Geometry
                        .frame(width: 125, height: 400)
                        .padding(.horizontal, 32)
                        .padding(.vertical, 32)
                    } //End ForEach
                    Spacer()
                        .frame(width: 16)
                } //End HStack
            }// End ScrollView
        }//End VStack
    }
    
    func getScale(proxy: GeometryProxy) -> CGFloat {
        let midPoint: CGFloat = 125
        
        let viewFrame = proxy.frame(in: CoordinateSpace.global)
        
        var scale: CGFloat = 1.0
        let deltaXAnimationThreshold: CGFloat = 125
        
        let diffFromCenter = abs(midPoint - viewFrame.origin.x - deltaXAnimationThreshold / 2)
        if diffFromCenter < deltaXAnimationThreshold {
            scale = 1 + (deltaXAnimationThreshold - diffFromCenter) / 500
        }
        
        return scale
    }
}


struct MoviesCarousel_Previews: PreviewProvider {
    static var previews: some View {
        MoviesCarousel(categoryName: "Top Movie")
    }
}
MovieDetailsView.swift
 
//
//  MovieDetailsView.swift
//  Swiftuitest
//
//  Created by Cairocoders
//

import SwiftUI

struct MovieDetailsView: View {
    
    let movie: Movie
    
    var body: some View {
        Image(movie.imageName)
            .resizable()
            .scaledToFill()
            .navigationTitle(movie.title)
    }
}

struct MovieDetailsView_Previews: PreviewProvider {
    static var previews: some View {
        MovieDetailsView(movie: Movie(id: 1, title: "Avengers", imageName: "1"))
    }
}
Model.swift
 
//
//  Model.swift
//  Swiftuitest
//
//  Created by Cairocoders
//

import SwiftUI

struct Movie : Identifiable {
    var id : Int
    var title : String
    var imageName : String
}

var latestmovie = [Movie(id: 0, title: "The Avengers", imageName: "1"),
             Movie(id: 1, title: "Onward", imageName: "2"),
             Movie(id: 2, title: "Soul", imageName: "3"),
             Movie(id: 3, title: "Cruella", imageName: "4"),
             Movie(id: 4, title: "Jungle Cruise", imageName: "5"),
             Movie(id: 5, title: "The Call of the wild", imageName: "6"),
             Movie(id: 6, title: "Joker", imageName: "7"),
             Movie(id: 7, title: "Mulan", imageName: "8"),
             Movie(id: 8, title: "Mortal Kombat", imageName: "9")]

Related Post