In this tutorial When one is removed, another is added to the bottom of the stack.
ContentView.swift
// // ContentView.swift // swiftuidev // // Created by Cairocoders // import SwiftUI struct User: Hashable, CustomStringConvertible { var id: Int let firstName: String let lastName: String let age: Int let mutualFriends: Int let imageName: String let occupation: String var description: String { return "\(firstName), id: \(id)" } } struct ContentView: View { /// List of users @State var users: [User] = [ User(id: 0, firstName: "Airi", lastName: "Satou", age: 33, mutualFriends: 43, imageName: "photo1", occupation: "Accountant"), User(id: 1, firstName: "Angeleca", lastName: "Ramos", age: 47, mutualFriends: 12, imageName: "photo2", occupation: "Junior Techinical Author"), User(id: 2, firstName: "Aston", lastName: "Cox", age: 20, mutualFriends: 10, imageName: "photo3", occupation: "Scientist"), User(id: 3, firstName: "Bradley", lastName: "Greer", age: 45, mutualFriends: 46, imageName: "photo4", occupation: "Sales Assistant"), User(id: 4, firstName: "Bruno", lastName: "Nash", age: 23, mutualFriends:48, imageName: "photo5", occupation: "Sales Assistant"), User(id: 5, firstName: "Cara", lastName: "Stevens", age: 24, mutualFriends: 37, imageName: "photo6", occupation: "Marketing Manager") ] /// Return the CardViews width for the given offset in the array /// - parameters: - geometry: The geometry proxy of the parent, - id: The ID of the current user private func getCardWidth(_ geometry: GeometryProxy, id: Int) -> CGFloat { let offset: CGFloat = CGFloat(users.count - 1 - id) * 10 return geometry.size.width - offset } /// Return the CardViews frame offset for the given offset in the array /// - Parameters: - geometry: The geometry proxy of the parent, - id: The ID of the current user private func getCardOffset(_ geometry: GeometryProxy, id: Int) -> CGFloat { return CGFloat(users.count - 1 - id) * 10 } private var maxID: Int { return self.users.map { $0.id }.max() ?? 0 } var body: some View { VStack { GeometryReader { geometry in LinearGradient(gradient: Gradient(colors: [Color.init(#colorLiteral(red: 0.8509803922, green: 0.6549019608, blue: 0.7803921569, alpha: 1)), Color.init(#colorLiteral(red: 1, green: 0.9882352941, blue: 0.862745098, alpha: 1))]), startPoint: .bottom, endPoint: .top) .frame(width: geometry.size.width * 1.5, height: geometry.size.height) .background(Color.blue) .clipShape(Circle()) .offset(x: -geometry.size.width / 4, y: -geometry.size.height / 2) VStack(spacing: 24) { DateView() ZStack { ForEach(self.users, id: \.self) { user in // Range Operator if (self.maxID - 3)...self.maxID ~= user.id { CardView(user: user, onRemove: { removedUser in // Remove that user from our array self.users.removeAll { $0.id == removedUser.id } }) .frame(width: self.getCardWidth(geometry, id: user.id), height: 400) .offset(x: 0, y: self.getCardOffset(geometry, id: user.id)) } } } Spacer() } } }.padding() } } struct DateView: View { var body: some View { VStack { HStack { VStack(alignment: .leading) { Text(Date(), style: .date) .font(.title) .bold() Text("Today") .font(.subheadline) .foregroundColor(.gray) } Spacer() }.padding() } .background(Color.white) .cornerRadius(10) .shadow(radius: 5) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }CardView.swift
// // CardView.swift // swiftuidev // // Created by Cairocoders // import SwiftUI struct CardView: View { @State private var translation: CGSize = .zero @State private var swipeStatus: LikeDislike = .none private var user: User private var onRemove: (_ user: User) -> Void private var thresholdPercentage: CGFloat = 0.5 // when the user has draged 50% the width of the screen in either direction private enum LikeDislike: Int { case like, dislike, none } init(user: User, onRemove: @escaping (_ user: User) -> Void) { self.user = user self.onRemove = onRemove } /// What percentage of our own width have we swipped - Parameters: - geometry: The geometry - gesture: The current gesture translation value private func getGesturePercentage(_ geometry: GeometryProxy, from gesture: DragGesture.Value) -> CGFloat { gesture.translation.width / geometry.size.width } var body: some View { GeometryReader { geometry in VStack(alignment: .leading) { ZStack(alignment: self.swipeStatus == .like ? .topLeading : .topTrailing) { Image(self.user.imageName) .resizable() .aspectRatio(contentMode: .fill) .frame(width: geometry.size.width, height: geometry.size.height * 0.75) .clipped() if self.swipeStatus == .like { Text("LIKE") .font(.headline) .padding() .cornerRadius(10) .foregroundColor(Color.green) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(Color.green, lineWidth: 3.0) ).padding(24) .rotationEffect(Angle.degrees(-45)) } else if self.swipeStatus == .dislike { Text("DISLIKE") .font(.headline) .padding() .cornerRadius(10) .foregroundColor(Color.red) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(Color.red, lineWidth: 3.0) ).padding(.top, 45) .rotationEffect(Angle.degrees(45)) } } HStack { VStack(alignment: .leading, spacing: 6) { Text("\(self.user.firstName) \(self.user.lastName), \(self.user.age)") .font(.title) .bold() Text(self.user.occupation) .font(.subheadline) .bold() Text("\(self.user.mutualFriends) Mutual Friends") .font(.subheadline) .foregroundColor(.gray) } Spacer() Image(systemName: "info.circle") .foregroundColor(.gray) } .padding(.horizontal) } .padding(.bottom) .background(Color.white) .cornerRadius(10) .shadow(radius: 5) .animation(.interactiveSpring()) .offset(x: self.translation.width, y: 0) .rotationEffect(.degrees(Double(self.translation.width / geometry.size.width) * 25), anchor: .bottom) .gesture( DragGesture() .onChanged { value in self.translation = value.translation if (self.getGesturePercentage(geometry, from: value)) >= self.thresholdPercentage { self.swipeStatus = .like } else if self.getGesturePercentage(geometry, from: value) <= -self.thresholdPercentage { self.swipeStatus = .dislike } else { self.swipeStatus = .none } }.onEnded { value in // determine snap distance > 0.5 aka half the width of the screen if abs(self.getGesturePercentage(geometry, from: value)) > self.thresholdPercentage { self.onRemove(self.user) } else { self.translation = .zero } } ) } } } struct CardView_Previews: PreviewProvider { static var previews: some View { CardView(user: User(id: 1, firstName: "Cairocoders", lastName: "Tutorial101", age: 27, mutualFriends: 0, imageName: "photo1", occupation: "Coder"), onRemove: { _ in // do nothing }) .frame(height: 400) .padding() } }