fix: widget iOS 17 deployment target + register FloatingTabState
- Widget extension: 16.0 → 17.0 (containerBackground/widget API require 17) - xcodegen re-run picks up FloatingTabState.swift (was on disk but not registered in project.pbxproj after previous xcodegen run)
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44530A77DF12A17E52AAF34 /* MatrixView.swift */; };
|
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44530A77DF12A17E52AAF34 /* MatrixView.swift */; };
|
||||||
C1B3FC29820FCAF069D2C6FE /* KisaniCalWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */; };
|
C1B3FC29820FCAF069D2C6FE /* KisaniCalWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */; };
|
||||||
C79C33BE2802E81AA00175CC /* TodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C786EBC7DF879D64EB28165E /* TodayView.swift */; };
|
C79C33BE2802E81AA00175CC /* TodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C786EBC7DF879D64EB28165E /* TodayView.swift */; };
|
||||||
|
CBE7295BF5ADE08FE93AFAAF /* FloatingTabState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9100804DB1E61EA882CC54DA /* FloatingTabState.swift */; };
|
||||||
CF130E143FC6FFE2FE853FB6 /* WidgetDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */; };
|
CF130E143FC6FFE2FE853FB6 /* WidgetDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */; };
|
||||||
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */; };
|
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */; };
|
||||||
EF03EC9F974B14D100DD5528 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D045FE3DEB1D22D908A29F /* NotificationManager.swift */; };
|
EF03EC9F974B14D100DD5528 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D045FE3DEB1D22D908A29F /* NotificationManager.swift */; };
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
768D4E314252E2A90A06CCF2 /* AddExerciseSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExerciseSheet.swift; sourceTree = "<group>"; };
|
768D4E314252E2A90A06CCF2 /* AddExerciseSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExerciseSheet.swift; sourceTree = "<group>"; };
|
||||||
825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KisaniCalWidget.swift; sourceTree = "<group>"; };
|
825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KisaniCalWidget.swift; sourceTree = "<group>"; };
|
||||||
8934E4F21B90C9650F71D0A2 /* WidgetBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBridge.swift; sourceTree = "<group>"; };
|
8934E4F21B90C9650F71D0A2 /* WidgetBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBridge.swift; sourceTree = "<group>"; };
|
||||||
|
9100804DB1E61EA882CC54DA /* FloatingTabState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingTabState.swift; sourceTree = "<group>"; };
|
||||||
93D045FE3DEB1D22D908A29F /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
93D045FE3DEB1D22D908A29F /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDataStore.swift; sourceTree = "<group>"; };
|
A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDataStore.swift; sourceTree = "<group>"; };
|
||||||
@@ -95,6 +97,7 @@
|
|||||||
21B93C269F283F11B415B18C /* Components */ = {
|
21B93C269F283F11B415B18C /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9100804DB1E61EA882CC54DA /* FloatingTabState.swift */,
|
||||||
23A4491BFA50721082024756 /* SharedComponents.swift */,
|
23A4491BFA50721082024756 /* SharedComponents.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
@@ -292,6 +295,7 @@
|
|||||||
13E4B9854F595394FC9D5912 /* ContentView.swift in Sources */,
|
13E4B9854F595394FC9D5912 /* ContentView.swift in Sources */,
|
||||||
A9FF93259AE8FF0ABF69D71A /* DesignTokens.swift in Sources */,
|
A9FF93259AE8FF0ABF69D71A /* DesignTokens.swift in Sources */,
|
||||||
1A22EE21460821170E44B1DF /* ExerciseModels.swift in Sources */,
|
1A22EE21460821170E44B1DF /* ExerciseModels.swift in Sources */,
|
||||||
|
CBE7295BF5ADE08FE93AFAAF /* FloatingTabState.swift in Sources */,
|
||||||
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */,
|
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */,
|
||||||
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */,
|
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */,
|
||||||
EF03EC9F974B14D100DD5528 /* NotificationManager.swift in Sources */,
|
EF03EC9F974B14D100DD5528 /* NotificationManager.swift in Sources */,
|
||||||
@@ -420,7 +424,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_NSExtensionPointIdentifier = "com.apple.widgetkit-extension";
|
INFOPLIST_KEY_NSExtensionPointIdentifier = "com.apple.widgetkit-extension";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.widget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.widget;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -437,7 +441,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_NSExtensionPointIdentifier = "com.apple.widgetkit-extension";
|
INFOPLIST_KEY_NSExtensionPointIdentifier = "com.apple.widgetkit-extension";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.widget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.widget;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|||||||
BIN
KisaniCal.xcodeproj/project.xcworkspace/xcuserdata/kutesir.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
KisaniCal.xcodeproj/project.xcworkspace/xcuserdata/kutesir.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
52
KisaniCal/Components/FloatingTabState.swift
Normal file
52
KisaniCal/Components/FloatingTabState.swift
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class FloatingTabState: ObservableObject {
|
||||||
|
@Published private(set) var isCollapsed = false
|
||||||
|
private var lastY: CGFloat = 0
|
||||||
|
|
||||||
|
func report(scrollY: CGFloat) {
|
||||||
|
let delta = scrollY - lastY
|
||||||
|
guard abs(delta) > 1.5 else { return }
|
||||||
|
let shouldCollapse = delta < 0 // content Y decreases → user scrolling down
|
||||||
|
lastY = scrollY
|
||||||
|
guard shouldCollapse != isCollapsed else { return }
|
||||||
|
withAnimation(KisaniSpring.snappy) {
|
||||||
|
isCollapsed = shouldCollapse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetOffset() {
|
||||||
|
lastY = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: — Scroll tracker
|
||||||
|
|
||||||
|
private struct ScrollYKey: PreferenceKey {
|
||||||
|
static let defaultValue: CGFloat = 0
|
||||||
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TabBarScrollModifier: ViewModifier {
|
||||||
|
@EnvironmentObject private var tabState: FloatingTabState
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.background(
|
||||||
|
GeometryReader { geo in
|
||||||
|
Color.clear
|
||||||
|
.preference(key: ScrollYKey.self, value: geo.frame(in: .global).minY)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onPreferenceChange(ScrollYKey.self) { y in
|
||||||
|
tabState.report(scrollY: y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func trackScrollForTabBar() -> some View {
|
||||||
|
modifier(TabBarScrollModifier())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ struct ContentView: View {
|
|||||||
@AppStorage("hasOnboarded") private var hasOnboarded = false
|
@AppStorage("hasOnboarded") private var hasOnboarded = false
|
||||||
@State private var showOnboarding = false
|
@State private var showOnboarding = false
|
||||||
@State private var selectedTab = 0
|
@State private var selectedTab = 0
|
||||||
|
@StateObject private var tabState = FloatingTabState()
|
||||||
@State private var iPadSelection: Int? = 0
|
@State private var iPadSelection: Int? = 0
|
||||||
@State private var calIconImage: Image = ContentView.buildCalIcon()
|
@State private var calIconImage: Image = ContentView.buildCalIcon()
|
||||||
|
|
||||||
@@ -135,16 +136,19 @@ struct ContentView: View {
|
|||||||
.tabBarInset()
|
.tabBarInset()
|
||||||
}
|
}
|
||||||
.tint(AppColors.accent)
|
.tint(AppColors.accent)
|
||||||
|
.onChange(of: selectedTab) { _ in tabState.resetOffset() }
|
||||||
|
|
||||||
KisaniTabBar(
|
KisaniTabBar(
|
||||||
selection: $selectedTab,
|
selection: $selectedTab,
|
||||||
calIcon: calIconImage,
|
calIcon: calIconImage,
|
||||||
showMatrix: showMatrixTab,
|
showMatrix: showMatrixTab,
|
||||||
showWorkout: showWorkoutTab
|
showWorkout: showWorkoutTab,
|
||||||
|
isCollapsed: tabState.isCollapsed
|
||||||
)
|
)
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
}
|
}
|
||||||
|
.environmentObject(tabState)
|
||||||
.onAppear { UITabBar.appearance().isHidden = true }
|
.onAppear { UITabBar.appearance().isHidden = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +195,7 @@ struct KisaniTabBar: View {
|
|||||||
let calIcon: Image
|
let calIcon: Image
|
||||||
var showMatrix: Bool
|
var showMatrix: Bool
|
||||||
var showWorkout: Bool
|
var showWorkout: Bool
|
||||||
|
var isCollapsed: Bool = false
|
||||||
@Environment(\.colorScheme) private var cs
|
@Environment(\.colorScheme) private var cs
|
||||||
|
|
||||||
private struct Entry: Identifiable {
|
private struct Entry: Identifiable {
|
||||||
@@ -224,15 +229,15 @@ struct KisaniTabBar: View {
|
|||||||
}
|
}
|
||||||
if let img = entry.custom {
|
if let img = entry.custom {
|
||||||
img
|
img
|
||||||
.font(.system(size: 20))
|
.font(.system(size: isCollapsed ? 17 : 20))
|
||||||
.foregroundColor(selection == entry.id ? AppColors.accent : AppColors.text(cs))
|
.foregroundColor(selection == entry.id ? AppColors.accent : AppColors.text(cs))
|
||||||
} else if let icon = entry.sfIcon {
|
} else if let icon = entry.sfIcon {
|
||||||
Image(systemName: icon)
|
Image(systemName: icon)
|
||||||
.font(.system(size: 17, weight: selection == entry.id ? .semibold : .regular))
|
.font(.system(size: isCollapsed ? 15 : 17, weight: selection == entry.id ? .semibold : .regular))
|
||||||
.foregroundColor(selection == entry.id ? AppColors.accent : AppColors.text(cs))
|
.foregroundColor(selection == entry.id ? AppColors.accent : AppColors.text(cs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity).frame(height: 50)
|
.frame(maxWidth: .infinity).frame(height: isCollapsed ? 38 : 50)
|
||||||
}
|
}
|
||||||
.buttonStyle(PressButtonStyle(scale: 0.88))
|
.buttonStyle(PressButtonStyle(scale: 0.88))
|
||||||
}
|
}
|
||||||
@@ -241,8 +246,9 @@ struct KisaniTabBar: View {
|
|||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 28)
|
RoundedRectangle(cornerRadius: 28)
|
||||||
.fill(AppColors.surface(cs))
|
.fill(AppColors.surface(cs))
|
||||||
.shadow(color: .black.opacity(0.10), radius: 20, x: 0, y: 6)
|
.shadow(color: .black.opacity(isCollapsed ? 0.06 : 0.10), radius: 20, x: 0, y: 6)
|
||||||
.shadow(color: .black.opacity(0.04), radius: 4, x: 0, y: 1)
|
.shadow(color: .black.opacity(0.04), radius: 4, x: 0, y: 1)
|
||||||
)
|
)
|
||||||
|
.animation(KisaniSpring.snappy, value: isCollapsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ struct QuadrantCard: View {
|
|||||||
// ── Scrollable task list ──
|
// ── Scrollable task list ──
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
Color.clear.frame(height: 0).trackScrollForTabBar()
|
||||||
ForEach(tasks) { task in
|
ForEach(tasks) { task in
|
||||||
HStack(alignment: .top, spacing: 6) {
|
HStack(alignment: .top, spacing: 6) {
|
||||||
RoundedRectangle(cornerRadius: 3)
|
RoundedRectangle(cornerRadius: 3)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ struct SettingsView: View {
|
|||||||
AppColors.background(cs).ignoresSafeArea()
|
AppColors.background(cs).ignoresSafeArea()
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
Color.clear.frame(height: 0).trackScrollForTabBar()
|
||||||
|
|
||||||
Text("Settings")
|
Text("Settings")
|
||||||
.font(AppFonts.sans(15, weight: .semibold))
|
.font(AppFonts.sans(15, weight: .semibold))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ struct TodayView: View {
|
|||||||
|
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
Color.clear.frame(height: 0).trackScrollForTabBar()
|
||||||
|
|
||||||
// ── Header ──
|
// ── Header ──
|
||||||
HStack {
|
HStack {
|
||||||
|
|||||||
@@ -710,6 +710,7 @@ struct ManageWorkoutsSheet: View {
|
|||||||
|
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(alignment: .leading, spacing: 20) {
|
VStack(alignment: .leading, spacing: 20) {
|
||||||
|
Color.clear.frame(height: 0).trackScrollForTabBar()
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text("PROGRAMS").font(AppFonts.mono(9, weight: .bold)).foregroundColor(AppColors.text3(cs))
|
Text("PROGRAMS").font(AppFonts.mono(9, weight: .bold)).foregroundColor(AppColors.text3(cs))
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ targets:
|
|||||||
KisaniCalWidget:
|
KisaniCalWidget:
|
||||||
type: app-extension
|
type: app-extension
|
||||||
platform: iOS
|
platform: iOS
|
||||||
deploymentTarget: "16.0"
|
deploymentTarget: "17.0"
|
||||||
sources:
|
sources:
|
||||||
- path: KisaniCalWidget
|
- path: KisaniCalWidget
|
||||||
- path: Shared
|
- path: Shared
|
||||||
|
|||||||
Reference in New Issue
Block a user