iOS Development

ios – Scroll to merchandise with animation difficulty

Spread the love


In a scrollview with LazyHStack, I am working into a difficulty with easily scrolling a tapped merchandise into view. That is the animation Im attempting to realize:

enter image description here

Be aware that the merchandise hovered on above easily expands into view with objects shifting on both aspect.

My scrollview merchandise is structured such that it expands with animation on faucet. Nonetheless for a scroll view merchandise that’s partially seen, I am unable to have it increase in addition to scroll utterly into view.

My code is as follows:

struct CarouselItem: Identifiable, Equatable {
    non-public(set) var id: UUID = .init()
    var title: String
    var 12 months: String
    var runtime: String
    var certification: String
    var posterImage: String
    var stillImage: String
}

struct CarousalItemView: View {
    @State var merchandise: CarouselItem
    @Binding var selectedItem: CarouselItem?
    @Namespace non-public var namespace
    non-public var expanded: Bool {
        merchandise.id == selectedItem?.id
    }
    
    var physique: some View {
        VStack(alignment: .main) {
            ZStack {
                Picture(merchandise.posterImage)
                    .resizable()
                    .scaledToFill()
                    .scaleEffect(expanded ?  1.5 : 1, anchor: .topLeading)
                    .body(width: expanded ? 250 : 150, top: 200)
                    .matchedGeometryEffect(id: "picture", in: namespace, anchor: .topLeading, isSource: true)
                    .opacity(expanded ? 0 : 1)
                    .animation(.easeOut(period: 0.3), worth: expanded)
                
                Picture(merchandise.stillImage)
                    .resizable()
                    .scaledToFill()
                    .body(width: expanded ? 250 : 150, top: 200)
                    .matchedGeometryEffect(id: "picture", in: namespace, anchor: .topLeading, isSource: false)
                    .opacity(expanded ? 1 : 0)
                    .animation(.easeInOut(period: 0.2).delay(expanded ? 0.1 : 0), worth: expanded)
                    .overlay(alignment: .main) {
                        if expanded {
                            itemDetailsView()
                        }
                    }
            }
            .clipShape(RoundedRectangle(cornerRadius: 10))

            Textual content(merchandise.title)
                .body(top: 50)
        }
    }
    
    @ViewBuilder func itemDetailsView() -> some View {
        VStack(alignment: .main, spacing: 15) {
            Spacer()
            Textual content(merchandise.title)
                .font(.title2)
            HStack {
                Textual content("CBFC: " + merchandise.certification)
                    .padding(.horizontal, 5)
                    .overlay {
                        Rectangle()
                            .stroke(.white, lineWidth: 1)
                    }
                Textual content(merchandise.runtime)
                Textual content(merchandise.12 months)
            }
            .shadow(colour: .grey, radius: 5, y: 5)
            Button {

            } label: {
                Textual content("Trailer")
            }
            .buttonStyle(.borderedProminent)
            .tint(.grey)
            .padding(.backside, 15)
        }
        .padding(.horizontal, 10)
    }
}

struct ContentView: View {
    var objects: [CarouselItem] = [
        .init(title: "Oppenheimer", year: "2023", runtime: "3h 1m", certification: "UA", posterImage: "poster1", stillImage: "still1"),
        .init(title: "Barbie", year: "2023", runtime: "1h 54m", certification: "PG", posterImage: "poster2", stillImage: "still2"),
        .init(title: "The Martian", year: "2015", runtime: "2h 24m", certification: "UA", posterImage: "poster3", stillImage: "still3"),
        .init(title: "The Shawshank Redemption", year: "1994", runtime: "2h 22m", certification: "U", posterImage: "poster4", stillImage: "still4"),
        .init(title: "The Godfather", year: "1972", runtime: "2h 55m", certification: "UA", posterImage: "poster5", stillImage: "still5"),
        .init(title: "The Dark Knight", year: "2008", runtime: "2h 32m", certification: "UA", posterImage: "poster6", stillImage: "still6")
    ]
    @State var selectedItem: CarouselItem?
    var physique: some View {
        VStack {
            HStack {
                Textual content("Standard movies")
                    .font(.title2.daring())
                Spacer()
            }
            
            // MARK: Carousal
            ScrollViewReader { proxy in
                ScrollView(.horizontal) {
                    LazyHStack(spacing: 20) {
                        ForEach(objects, id: .id) { merchandise in
                            CarousalItemView(merchandise: merchandise, selectedItem: $selectedItem)
                                .body(width: selectedItem?.id != merchandise.id ? 150 : 250, top: 250)
                                .clipShape(RoundedRectangle(cornerRadius: 10))
                                .contentShape(Rectangle())
                                .onTapGesture {
                                    DispatchQueue.principal.async {
                                        withAnimation {
                                            if selectedItem?.id == merchandise.id {
                                                selectedItem = nil
                                            } else {
                                                selectedItem = merchandise
                                                guard let merchandise = selectedItem else { return }
                                                proxy.scrollTo(merchandise.id, anchor: .heart)
                                            }
                                        }
                                    }
                                }
                        }
                    }
                }
                .body(top: 250)
                .scrollIndicators(.hidden)
                .clipped()
            }
            Spacer()
        }
        .padding()
        .background {
            Coloration(.darkGray).ignoresSafeArea()
        }
        .foregroundStyle(.white)
    }
}

Within the code above, the animation is type of jerky particularly if an merchandise is expanded and is partially seen on the left & proper edges.
Any assistance is appreciated.

Leave a Reply

Your email address will not be published. Required fields are marked *