将视图动画化为全屏(详细显示卡片)

时间:2020-06-11 18:54:31

标签: swift swiftui

我正在尝试从Appstore的“今日”标签复制卡片:

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。但是动画看起来很遥远。我还尝试遵循这些视频教程:

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

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

但是这些仅适用于静态内容(不能使用ScrollView。在重叠的PostView中使用一个结果)或外观不正确。

所以我的问题是我如何才能改善我的代码以使其尽可能接近Appstore中的“今日”标签?我的方法可行吗?

预先感谢

1 个答案:

答案 0 :(得分:1)

请在下面找到修改后的代码以适合您的需求

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
}

如果需要任何解释,请告诉我。

注意:我向您的showDetails模型添加了Post属性,这是控制单个单元格所必需的。请记住,最佳做法是将其分成不同的数组,以照顾可见的内容和不可见的内容,但是目前为止。

还要注意,我们正在遍历数组的索引而不是对象的索引,这样我们可以灵活地选择要显示的内容。