Animate view to fullscreen (Card to detail)

大憨熊 提交于 2021-02-11 14:46:23

问题


Im trying to replicate the cards from the Appstore's today tab:

https://imgur.com/a/1Jd4bI5

This is what I have so far:

struct ContentView: View {

    @State var showDetail = false
    @State var selectedForDetail : Post?

    var posts = [...] // Just sample data: Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor...") etc.


    var body: some View {
        ZStack {
            ScrollView{
                ForEach(self.posts){ current in
                    PostView(post: current, isDetailed: self.$showDetail).onTapGesture {
                        self.selectedForDetail = current
                        withAnimation(.spring()){
                           self.showDetail.toggle()
                        }
                    }
                }
            }
            if showDetail {
                PostView(post: selectedForDetail!, isDetailed: self.$showDetail).onTapGesture {
                   withAnimation(.spring()){
                        self.showDetail.toggle()
                    }

                }
            }
        }
    }
}

struct PostView : View {

    var post : Post

    @Binding var isDetailed : Bool    

    var body : some View {
       VStack(alignment: .leading){
           HStack(alignment: .top){
               Text(post.subtitle)
               Spacer()
           }.padding([.top, .horizontal])
           Text(post.title).padding([.horizontal, .bottom])
           if isDetailed {
               Text(post.extra).padding([.horizontal, .bottom])
               Spacer()
           }
       }
       .background(isDetailed ? Color.green : Color.white)
       .cornerRadius(isDetailed ? 0 : 16)
       .shadow(radius: isDetailed ? 0 : 12)
       .padding(isDetailed ? [] : [.top, .horizontal])
       .edgesIgnoringSafeArea(.all)
   }
}

struct Post : Identifiable {
   var id = UUID()
   var subtitle : String
   var title : String
   var extra : String
}

It works so far that pressing a PostView shows a detailed PostView in fullscreen. But the animation looks way off. I also tried to follow these video tutorials:

https://www.youtube.com/watch?v=wOQWAzsKi4U

https://www.youtube.com/watch?v=8gDtf22TwW0

But these only worked with static content (and no ScrollView. Using one results in overlapped PostViews) or didn't look right..

So my question is how can i improve my code to get as close as possible to the todays tab in die Appstore? Is my approach even feasible?

Thanks in advance


回答1:


Please find below your code modified to fit your needs

import SwiftUI

struct ContentView: View {
    @State var selectedForDetail : Post?
    @State var showDetails: Bool = false

    // Posts need to be @State so changes can be observed
    @State var posts = [
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor..."),
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor..."),
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor..."),
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor..."),
        Post(subtitle: "test1", title: "title1", extra: "Lorem ipsum dolor...")
    ]

    var body: some View {
        ScrollView {
            VStack {
                ForEach(self.posts.indices) { index in
                    GeometryReader { reader in
                        PostView(post: self.$posts[index], isDetailed: self.$showDetails)
                        .offset(y: self.posts[index].showDetails ? -reader.frame(in: .global).minY : 0)
                        .onTapGesture {
                            if !self.posts[index].showDetails {
                                self.posts[index].showDetails.toggle()
                                self.showDetails.toggle()
                            }
                        }
                        // Change this animation to what you please, or change the numbers around. It's just a preference.
                        .animation(.spring(response: 0.6, dampingFraction: 0.6, blendDuration: 0))
                        // If there is one view expanded then hide all other views that are not
                        .opacity(self.showDetails ? (self.posts[index].showDetails ? 1 : 0) : 1)
                    }
                    .frame(height: self.posts[index].showDetails ? UIScreen.main.bounds.height : 100, alignment: .center)
                    .simultaneousGesture(
                        // 500 will disable ScrollView effect
                        DragGesture(minimumDistance: self.posts[index].showDetails ? 0 : 500)
                    )
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct PostView : View {
    @Binding var post : Post
    @Binding var isDetailed : Bool
    var body : some View {
        VStack(alignment: .leading){
            HStack(alignment: .top){
                Text(post.subtitle)
                Spacer()

                // Only show close button if page is showing in full screen
                if(self.isDetailed) {
                    // Close Button
                    Button(action: {
                        self.post.showDetails.toggle()
                        self.isDetailed.toggle()
                    }) {
                        Text("X")
                            .frame(width: 48, height: 48, alignment: .center)
                            .background(Color.white)
                            .clipShape(Circle())
                    }.buttonStyle(PlainButtonStyle())
                }
            }.padding([.top, .horizontal])

            Text(post.title).padding([.horizontal, .bottom])

            if isDetailed {
                Text(post.extra).padding([.horizontal, .bottom])
                Spacer()
            }
        }
        .background(isDetailed ? Color.green : Color.white)
        .cornerRadius(isDetailed ? 0 : 16)
        .shadow(radius: isDetailed ? 0 : 12)
        .padding(isDetailed ? [] : [.top, .horizontal])
        .edgesIgnoringSafeArea(.all)
    }
}

struct Post : Identifiable {
    var id = UUID()
    var subtitle : String
    var title : String
    var extra : String
    var showDetails: Bool = false // We need this variable to control each cell individually
}

If any explanation is needed please let me know.

Note: I added a showDetails property to your Post model, this is needed to control individual cells. Keep in mind best practice is to separate that into a different array to take care of whats visible and what not but this will do for now.

Also note we are looping through indices of our array and not the objects, this way we have flexibility of choosing what to show.



来源:https://stackoverflow.com/questions/62331530/animate-view-to-fullscreen-card-to-detail

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!