Last active
October 9, 2024 12:46
-
-
Save iandreyshev/47b55d4c7cf3036dacb54b4ea41bd9af to your computer and use it in GitHub Desktop.
Schedule list example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ScrollToItem.swift | |
// SwiftUIArchitectureSample | |
// | |
// Created by Ivan Andreyshev on 04.10.2024. | |
// | |
import SwiftUI | |
struct ScheduleView: View { | |
@StateObject | |
private var viewModel = ScheduleViewModel() | |
@State | |
private var scrolledID: Int? | |
var body: some View { | |
VStack(spacing: 0) { | |
HStack(alignment: .top) { | |
ForEach(0..<viewModel.state.days.count, id: \.self) { index in | |
let day = viewModel.state.days[index] | |
Button(day.date.titleForDaysList()) { | |
withAnimation { | |
scrolledID = day.itemPosToScroll | |
} | |
} | |
.font(.title) | |
.frame(maxWidth: .infinity) | |
} | |
} | |
.padding(.vertical, 20) | |
.background(.red) | |
ScrollView { | |
LazyVStack(spacing: 20) { | |
ForEach(0..<viewModel.state.meetings.count, id: \.self) { index in | |
let item = viewModel.state.meetings[index] | |
VStack(spacing: 0) { | |
switch item { | |
case .emptyDay(let date): | |
Text(date.formatted(date: .complete, time: .omitted)) | |
.padding(.vertical, 20) | |
.id(index) | |
Text("В этот день нет пар, радуйтесь") | |
case .meeting(let meeting): | |
if meeting.isFirst { | |
Text(meeting.startTime.formatted(date: .complete, time: .omitted)) | |
.padding(.vertical, 20) | |
.id(index) | |
} | |
HStack { | |
VStack { | |
Text(meeting.title) | |
.font(.title) | |
Text(meeting.startTime.formatted(date: .long, time: .shortened)) | |
} | |
.padding(.vertical, 80) | |
.frame(maxWidth: .infinity) | |
.background(.blue) | |
} | |
.clipShape( | |
RoundedRectangle(cornerRadius: 12) | |
) | |
.overlay( | |
RoundedRectangle(cornerRadius: 12) | |
.stroke(.red, lineWidth: 2) | |
) | |
.padding(.horizontal, 20) | |
} | |
} | |
} | |
} | |
} | |
.scrollPosition(id: $scrolledID) | |
} | |
.frame(maxWidth: .infinity) | |
} | |
} | |
struct ScheduleState { | |
var days: [Day] = [] | |
var meetings: [MeetingItem] = [] | |
} | |
struct Day { | |
var date: Date | |
var itemPosToScroll: Int | |
} | |
struct Meeting { | |
var title: String | |
var startTime: Date | |
var isFirst: Bool = false | |
} | |
enum MeetingItem { | |
case emptyDay(Date) | |
case meeting(Meeting) | |
} | |
class ScheduleViewModel: ObservableObject { | |
@Published | |
var state: ScheduleState = createState() | |
} | |
private func createState() -> ScheduleState { | |
let meetings: [MeetingItem] = [ | |
// Position 0 | |
.meeting(Meeting(title: "Русский язык", startTime: Date(timeIntervalSince1970: 1727769600), isFirst: true)), | |
.meeting(Meeting(title: "Литература", startTime: Date(timeIntervalSince1970: 1727773200))), | |
.meeting(Meeting(title: "Программирование", startTime: Date(timeIntervalSince1970: 1727776800))), | |
.meeting(Meeting(title: "Ужин", startTime: Date(timeIntervalSince1970: 1727780400))), | |
.meeting(Meeting(title: "Вечернее ООП", startTime: Date(timeIntervalSince1970: 1727784000))), | |
// Position 5 | |
.emptyDay(Date(timeIntervalSince1970: 1727856000)), | |
// Position 6 | |
.meeting(Meeting(title: "Поход в честер", startTime: Date(timeIntervalSince1970: 1727942400), isFirst: true)), | |
.meeting(Meeting(title: "Сангрита", startTime: Date(timeIntervalSince1970: 1727960400))), | |
.meeting(Meeting(title: "Шави", startTime: Date(timeIntervalSince1970: 1727967600))), | |
.meeting(Meeting(title: "Кальянная", startTime: Date(timeIntervalSince1970: 1727974800))), | |
.meeting(Meeting(title: "Вечерний Ассемблер", startTime: Date(timeIntervalSince1970: 1727982000))), | |
// Position 11 | |
.meeting(Meeting(title: "Зарядка", startTime: Date(timeIntervalSince1970: 1728028800), isFirst: true)), | |
.meeting(Meeting(title: "Растяжка", startTime: Date(timeIntervalSince1970: 1728043200))), | |
.meeting(Meeting(title: "Велогонки", startTime: Date(timeIntervalSince1970: 1728050400))), | |
.meeting(Meeting(title: "Автогонки", startTime: Date(timeIntervalSince1970: 1728064800))), | |
.meeting(Meeting(title: "Полёт в космос", startTime: Date(timeIntervalSince1970: 1728075600))) | |
] | |
return .init( | |
days: [ | |
Day(date: Date(timeIntervalSince1970: 1727769600), itemPosToScroll: 0), | |
Day(date: Date(timeIntervalSince1970: 1727856000), itemPosToScroll: 5), | |
Day(date: Date(timeIntervalSince1970: 1727942400), itemPosToScroll: 6), | |
Day(date: Date(timeIntervalSince1970: 1728028800), itemPosToScroll: 11) | |
], | |
meetings: meetings | |
) | |
} | |
extension Date { | |
func titleForDaysList() -> String { | |
let components = Calendar.current.dateComponents([.day, .weekday], from: self) | |
let weekDayStr = DateFormatter().shortWeekdaySymbols[components.weekday ?? 0] | |
return "\(components.day ?? 0)\n\(weekDayStr)" | |
} | |
} | |
#Preview { | |
ScheduleView() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment