如何使用SwiftUI创建可滑动查看的视图

时间:2019-10-16 11:13:33

标签: swift swiftui

我试图制作一个SWIFTUI View,该SWIFTUI View可以通过使用gesture()方法来进行类似卡片刷卡的动作。但是我想不出一种方法来使视图一个一个地滑动。目前,当我滑动所有视图时,

import SwiftUI

struct EventView: View {
    @State private var offset: CGSize = .zero
    @ObservedObject var randomView: EventViewModel
    var body: some View {
        ZStack{
            ForEach(randomView.randomViews,id:\.id){ view in
            view
                .background(Color.randomColor)
                .cornerRadius(8)
                .shadow(radius: 10)
                .padding()
                .offset(x: self.offset.width, y: self.offset.height)
                .gesture(
                    DragGesture()
                        .onChanged { self.offset = $0.translation }
                        .onEnded {
                            if $0.translation.width < -100 {

                                self.offset = .init(width: -1000, height: 0)
                            } else if $0.translation.width > 100 {
                                self.offset = .init(width: 1000, height: 0)

                            } else {
                                self.offset = .zero
                            }
                    }
                )
                .animation(.spring())
            }
        }
    }
}

struct EventView_Previews: PreviewProvider {
    static var previews: some View {
        EventView(randomView: EventViewModel())
    }
}

struct PersonView: View {
    var id:Int = Int.random(in: 1...1000)
    var body: some View {
        VStack(alignment: .center) {
            Image("testBtn")
                .clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/)

            Text("Majid Jabrayilov")
                .font(.title)
                .accentColor(.white)

            Text("iOS Developer")
                .font(.body)
                .accentColor(.white)
        }.padding()
    }
}

使用这段代码,当我轻扫整个内容时

1 个答案:

答案 0 :(得分:1)

基本上,您的代码告诉每个视图都遵循偏移量,而实际上您只希望前一个动作。因此,首先我要添加一个变量,该变量将保存卡的当前索引,并提供一种计算其偏移量的方法:

@State private var currentCard = 0

func offset(for i: Int) -> CGSize {
   return i == currentCard ? offset : .zero
}

第二,我发现,如果我们只是这样保留它,则在下一个触摸视图上将获得上一个(-1000,0)的偏移量,然后才跳转到正确的位置,因此它看起来就像上一个卡决定返回,而不是新的卡。为了解决这个问题,我添加了一个标记来标记该卡已用完,因此当我们再次触摸它时,它最初会定位在正确的位置。通常,我们会在手势的.began状态下执行此操作,但是在swiftUI中没有类似的操作,因此唯一的执行位置是在.onChanged中:

@State private var didJustSwipe = false

DragGesture()
    .onChanged {
        if self.didJustSwipe {
            self.didJustSwipe = false
            self.currentCard += 1
            self.offset = .zero
        } else {
            self.offset = $0.translation
        }
    }

如果成功,我们在.onEnded中分配didJustSwipe = true

因此,现在它可以完美运行了。另外,我建议您将代码分成更小的部分。它不仅可以提高可读性,而且可以节省一些编译时间。您没有提供EventViewModel和那些randomViews的实现,所以我改用了矩形。这是您的代码:

struct EventView: View {

    @State private var offset: CGSize = .zero
    @State private var currentCard = 0
    @State private var didJustSwipe = false

    var randomView: some View {
        return Rectangle()
            .foregroundColor(.green)
            .cornerRadius(20)
            .frame(width: 300, height: 400)
            .shadow(radius: 10)
            .padding()
            .opacity(0.3)
    }

    func offset(for i: Int) -> CGSize {
        return i == currentCard ? offset : .zero
    }

    var body: some View {
        ZStack{
            ForEach(currentCard..<5, id: \.self) { i in
                self.randomView
                    .offset(self.offset(for: i))
                    .gesture(self.gesture)
                    .animation(.spring())
            }
        }
    }

    var gesture: some Gesture {
        DragGesture()
            .onChanged {
                if self.didJustSwipe {
                    self.didJustSwipe = false
                    self.currentCard += 1
                    self.offset = .zero
                } else {
                    self.offset = $0.translation
                }
        }
            .onEnded {
                let w = $0.translation.width
                if abs(w) > 100 {
                    self.didJustSwipe = true
                    let x = w > 0 ? 1000 : -1000
                    self.offset = .init(width: x, height: 0)
                } else {
                    self.offset = .zero
                }
        }
    }
}