fix(layout): fix rotation crash and improve iPad split view

iPadLayout was rendering TodayView without workoutVM, crashing with
a fatal EnvironmentObject error on any device where hSizeClass goes
.regular (iPad, iPhone Plus/Max in landscape). Rewrote to use proper
NavigationSplitView with List(selection:) driving the detail column.

Also caches calendarIcon as a static func + @State instead of
recomputing UIGraphicsImageRenderer on every body evaluation.
This commit is contained in:
kutesir
2026-05-27 14:01:34 +03:00
parent f67429de0b
commit d7ec351dd4

View File

@@ -11,8 +11,10 @@ struct ContentView: View {
@AppStorage("hasOnboarded") private var hasOnboarded = false
@State private var showOnboarding = false
@State private var selectedTab = 0
@State private var iPadSelection: Int? = 0
@State private var calIconImage: Image = ContentView.buildCalIcon()
private var calendarIcon: Image {
private static func buildCalIcon() -> Image {
let day = Calendar.current.component(.day, from: Date())
let size = CGSize(width: 26, height: 26)
let renderer = UIGraphicsImageRenderer(size: size)
@@ -21,14 +23,12 @@ struct ContentView: View {
let lw: CGFloat = 1.5
let cr: CGFloat = 4.5
// Outer rounded rectangle body
let body = CGRect(x: lw / 2, y: 3, width: size.width - lw, height: size.height - 3 - lw / 2)
let bodyPath = UIBezierPath(roundedRect: body, cornerRadius: cr)
color.setStroke()
bodyPath.lineWidth = lw
bodyPath.stroke()
// Header divider line
let divY = body.minY + 7.5
let divPath = UIBezierPath()
divPath.move(to: CGPoint(x: body.minX + lw / 2, y: divY))
@@ -37,13 +37,11 @@ struct ContentView: View {
divPath.lineWidth = 1
divPath.stroke()
// Ring bumps at top
color.setFill()
for rx in [size.width * 0.3, size.width * 0.7] {
UIBezierPath(roundedRect: CGRect(x: rx - 1.5, y: 0.5, width: 3, height: 5.5), cornerRadius: 1.5).fill()
}
// Date number centred in body below header
let s = "\(day)"
let font = UIFont.systemFont(ofSize: floor(size.height * 0.38), weight: .bold)
let attrs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: color]
@@ -133,7 +131,7 @@ struct ContentView: View {
KisaniTabBar(
selection: $selectedTab,
calIcon: calendarIcon,
calIcon: calIconImage,
showMatrix: showMatrixTab,
showWorkout: showWorkoutTab
)
@@ -146,42 +144,27 @@ struct ContentView: View {
// MARK: - iPad
@ViewBuilder
private func iPadLayout() -> some View {
NavigationSplitView(columnVisibility: .constant(.all)) {
List {
iPadSidebarItem("Tasks", icon: "checkmark", tag: 0)
iPadSidebarItem("Calendar", icon: "calendar", tag: 1)
if showMatrixTab {
iPadSidebarItem("Matrix", icon: "square.grid.2x2", tag: 2)
}
if showWorkoutTab {
iPadSidebarItem("Workout", icon: "dumbbell", tag: 3)
}
iPadSidebarItem("Settings", icon: "gearshape", tag: 4)
NavigationSplitView {
List(selection: $iPadSelection) {
Label("Tasks", systemImage: "checkmark").tag(0)
Label("Calendar", systemImage: "calendar").tag(1)
if showMatrixTab { Label("Matrix", systemImage: "square.grid.2x2").tag(2) }
if showWorkoutTab { Label("Workout", systemImage: "dumbbell").tag(3) }
Label("Settings", systemImage: "gearshape").tag(4)
}
.navigationTitle("Kisani Cal")
.listStyle(.sidebar)
} detail: {
TodayView().environmentObject(taskVM)
}
.tint(AppColors.accent)
}
private func iPadSidebarItem(_ label: String, icon: String, tag: Int) -> some View {
NavigationLink(destination: iPadDetail(tag: tag)) {
Label(label, systemImage: icon)
}
}
@ViewBuilder
private func iPadDetail(tag: Int) -> some View {
switch tag {
case 0: TodayView().environmentObject(taskVM).environmentObject(workoutVM)
switch iPadSelection ?? 0 {
case 1: CalendarView().environmentObject(taskVM).environmentObject(workoutVM)
case 2: MatrixView().environmentObject(taskVM)
case 3: WorkoutView().environmentObject(workoutVM)
default: SettingsView().environmentObject(workoutVM)
case 4: SettingsView().environmentObject(workoutVM)
default: TodayView().environmentObject(taskVM).environmentObject(workoutVM)
}
}
.tint(AppColors.accent)
}
}
// MARK: - Custom Tab Bar