セグメントメニューを配置して、メニューごとに表示するリストを変えるサンプルです。今回はフィルター用のViewを作成してみます。

struct Schedule {
let title: String
let type: String
}
// 今回使用するサンプルデータ
struct ScheduleList {
var schedules: [Schedule] = []
init() {
for num in 1...5 {
schedules.append(Schedule(title: "予定\(num)", type: ["A", "B", "C"].randomElement()!))
}
}
}
// リストにフィルターをかけるView
struct FilteredList<Content: View, Schedule>: View {
let content: (Schedule) -> Content
let currentTab: String
let scheduleList: ScheduleList
init(list: ScheduleList, currentTab: String, @ViewBuilder content: @escaping (Schedule) -> Content) {
self.scheduleList = list
self.currentTab = currentTab
self.content = content
}
var body: some View {
VStack {
// タブで選択されているタイプの予定を抽出します
let schedules = scheduleList.schedules.filter { $0.type == currentTab }
if schedules.isEmpty {
Text("予定はありません")
} else {
ForEach(schedules, id: \.title) { schedule in
self.content(schedule as! Schedule)
}
}
}
}
}
struct ContentView: View {
@State var currentTab: String = "B"
@Namespace var animation
let scheduleList = ScheduleList()
var body: some View {
VStack {
HStack(alignment: .top) {
let tabs = ["A", "B", "C"]
ForEach(tabs, id:\.self) { tab in
Text(tab)
.padding(.vertical,6)
.frame(maxWidth: .infinity)
.foregroundColor(currentTab == tab ? .white : .black)
.background{
if currentTab == tab {
Capsule()
.fill(.black)
.matchedGeometryEffect(id: "TAB", in: animation)
}
}
.onTapGesture {
currentTab = tab
}
}
}
.padding()
FilteredList(list: scheduleList, currentTab: currentTab) { (schedule: Schedule) in
ScheduleView(schedule: schedule)
}
}
.frame(maxHeight: .infinity, alignment: .top)
}
func ScheduleView(schedule: Schedule) -> some View {
VStack(alignment: .leading, spacing: 10) {
HStack {
Text(schedule.title)
Text(schedule.type)
}
}
.padding()
}
}