0

我试图从 Appstore 的今日标签复制卡片。

这是我到目前为止所拥有的:

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 重叠)或者看起来不正确..

所以我的问题是如何改进我的代码以尽可能接近应用商店中的今日标签?我的方法是否可行?

提前致谢

4

1 回答 1

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,这是控制单个单元格所必需的。请记住,最佳实践是将其分离到不同的数组中,以处理可见的和不可见的,但现在这样做。

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

于 2020-06-12T00:46:06.797 回答