本文介绍了动画视图到全屏(卡片到细节)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从 Appstore 的今天"标签中复制卡片:

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

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

这是我目前所拥有的:

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
}

到目前为止,按下 PostView 可以全屏显示详细的 PostView.但动画看起来很遥远.我也尝试遵循这些视频教程:

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

但这些只适用于静态内容(没有 ScrollView.使用一个结果会导致重叠的 PostView)或者看起来不正确..

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

所以我的问题是如何改进我的代码以尽可能接近 Appstore 中的今天标签?我的方法可行吗?

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?

提前致谢

推荐答案

请在下面找到已修改的代码以满足您的需求

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.

注意:我在您的 Post 模型中添加了一个 showDetails 属性,这是控制单个单元格所必需的.请记住,最佳做法是将其分离到不同的数组中,以处理可见的和不可见的,但现在可以这样做.

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.

这篇关于动画视图到全屏(卡片到细节)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-17 13:33