I have a segmented control with two segments as shown in the image see image here. I want to enable the user to switch between the two segments by swiping right or left, and display the corresponding views accordingly. I have looked through several articles, but I have not found a solution to implement this functionality.
I used buttons to implement this feature, but I'm unsure how to enable swipe functionality.
struct ScheduleView: View {
// MARK: - Variables
@State private var playerName: String = ""
@State private var pastShown: Bool = false
@State private var presentAlert: Bool = false
@State private var goToGroupChat: Bool = false
private var items = ["Upcoming", "Past"]
var body: some View {
NavigationView {
ZStack {
VStack {
HStack {
VStack(alignment: .leading) {
Text("Hello!,")
.robotoRegularFont(size: 14)
.foregroundColor(Color.custom64B054Color)
Text("John Andrew")
.robotoRegularFont(size: 14)
.foregroundColor(Color.custom333333Color)
}
Spacer()
Image("appIcon_icon")
.resizable()
.frame(width: 32, height: 32)
}
.padding(.top, 24)
// MARK: - Upcoming and Past Views
VStack(spacing: 8) {
HStack(alignment: .center, spacing: 0) {
Spacer()
// MARK: - Button Upcoming
ZStack {
Button {
print("Upcoming action")
self.pastShown = false
} label: {
Text("Upcoming")
.font(.custom(self.pastShown ? "Roboto-Light" : "Roboto-Medium", size: 15))
.foregroundColor(self.pastShown ? .black.opacity(0.7) : .black)
}
}
.frame(width: UIScreen.main.bounds.width / 2 - 18)
Spacer()
Spacer()
// MARK: - Button Past
ZStack {
Button {
print("Past action")
self.pastShown = true
} label: {
Text("Past")
.font(.custom(self.pastShown ? "Roboto-Medium" : "Roboto-Light", size: 15))
.foregroundColor(self.pastShown ? .black : .black.opacity(0.7))
}
}
.frame(width: UIScreen.main.bounds.width / 2 - 18)
Spacer()
}
let upcoming = Capsule()
.fill(Color.black.opacity(self.pastShown ? 0.2 : 1))
.frame(maxWidth: .infinity, maxHeight: 1.5)
let past = Capsule()
.fill(Color.black.opacity(self.pastShown ? 1 : 0.2))
.frame(maxWidth: .infinity, maxHeight: 1.5)
HStack(alignment: .center, spacing: 0) {
upcoming
past
}
}
.padding(.top, 8)
.padding(.horizontal, -18)
ScrollView(.vertical, showsIndicators: false) {
// MARK: - Past View
if self.pastShown {
Text("past")
} else {
// MARK: - Upcoming View
LazyVStack(spacing: 20) {
MatchInfoCell(playerType: "Doubles", skillRange: "minimum", noOfParticipants: "2/4", date: "Tues, Oct 25", time: "04:00 PM", court: "Tour Greens Houston Serves The Houston Area", isForSchedule: true, lookingButtonAction: {
print("lookingButtonAction")
}, gameOnButtonAction: {
print("gameOnButtonAction")
}, noGameButtonAction: {
print("noGameButtonAction")
}, messageButtonAction: {
print("messageButtonAction")
self.goToGroupChat.toggle()
}, addGuestButtonAction: {
print("addGuestButtonAction")
withAnimation {
self.presentAlert.toggle()
}
})
}
}
}
.padding(.top)
.background(Color.clear)
}
.padding(.horizontal, 18)
// MARK: - Custom Alert for Add Guest
if self.presentAlert {
VStack {
CustomAlertView(alertTitle: "Are you sure you want to add a Guest in this match?", nonFilledButtonTitle: "No", filledButtonTitle: "Yes", nonFilledButtonAction: {
withAnimation {
self.presentAlert.toggle()
}
}, filledButtonAction: {
withAnimation {
self.presentAlert.toggle()
}
}, presentAlert: $presentAlert)
}
}
// MARK: - Navigate to Group Chat
NavigationLink("", destination: GroupChatView().navigationBarHidden(true).navigationBarBackButtonHidden(true), isActive: $goToGroupChat)
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
If someone could provide me with guidance on how to achieve this, I would greatly appreciate it!
Thank You!
So, to make it work I used almost of all of your code. The important part is the gesture modifier:
.gesture(
DragGesture()
.onChanged({ value in
let translationX = value.translation.width
let velocity = abs(value.velocity.width)
let predictedEnd = value.predictedEndTranslation.width
print("Entered \(translationX), velocity \(velocity)")
if (translationX > 200 || predictedEnd > 200) && velocity > 500 {
withAnimation(.smooth) {
self.pastShown = false
}
} else if (translationX < -200 || predictedEnd > -200) && velocity > 500 {
withAnimation(.smooth) {
self.pastShown = true
}
}
})
)
Play around and tune those values as you need. You can add it at the end of your ZStack which embeds your two views (Upcoming and past). Also, you need to add this line to your ScrollView enclosing the "past" tab:
.frame(maxWidth: .infinity, maxHeight: .infinity)
Making it look like this:
ScrollView(.vertical, showsIndicators: false) {
// MARK: - Past View
if self.pastShown {
Text("past")
} else {
// MARK: - Upcoming View
LazyVStack(spacing: 20) {
// Your code here
}
}
}
.padding(.top)
.background(Color.clear)
.frame(maxWidth: .infinity, maxHeight: .infinity) // <-- The new line
Otherwise the drag gesture would only be detected on a really narrow strip of the screen making it impossible for the swipe to work properly. Let me know if it works for you
Note: I used a red background to let you better see the swipeable area.