diff --git a/Build.xcconfig b/Build.xcconfig index 98c21cf..1f49873 100644 --- a/Build.xcconfig +++ b/Build.xcconfig @@ -5,7 +5,7 @@ MARKETING_VERSION = 1.1.1 CURRENT_PROJECT_VERSION = 1 // Vars to be overwritten by `CodeSigning.xcconfig` if exists -DEVELOPMENT_TEAM = S32Z3HMYVQ +DEVELOPMENT_TEAM = 95J8WZ4TN8 ORG_IDENTIFIER = com.stossy11 // Codesigning settings defined optionally, see `CodeSigning.xcconfig.example` diff --git a/StosVPN.xcodeproj/project.pbxproj b/StosVPN.xcodeproj/project.pbxproj index 187b0ec..4b43228 100644 --- a/StosVPN.xcodeproj/project.pbxproj +++ b/StosVPN.xcodeproj/project.pbxproj @@ -417,9 +417,13 @@ "$(inherited)", "@executable_path/Frameworks", ); + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; }; name = Debug; }; @@ -440,8 +444,12 @@ "$(inherited)", "@executable_path/Frameworks", ); + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; }; name = Release; }; @@ -455,8 +463,12 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(TUNNEL_BUNDLE_IDENTIFIER)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; }; name = Debug; }; @@ -470,8 +482,12 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(TUNNEL_BUNDLE_IDENTIFIER)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; }; name = Release; }; diff --git a/StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index c3edd96..af9fc13 100644 Binary files a/StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/StosVPN/ContentView.swift b/StosVPN/ContentView.swift index 914e9cb..62aef5e 100644 --- a/StosVPN/ContentView.swift +++ b/StosVPN/ContentView.swift @@ -533,7 +533,7 @@ struct ContentView: View { } .padding() .navigationTitle("StosVPN") - .navigationBarTitleDisplayMode(.inline) + .tvOSNavigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button { @@ -559,6 +559,17 @@ struct ContentView: View { } } +extension View { + @ViewBuilder + func tvOSNavigationBarTitleDisplayMode(_ displayMode: NavigationBarItem.TitleDisplayMode) -> some View { + #if os(iOS) + self.navigationBarTitleDisplayMode(displayMode) + #else + self + #endif + } +} + struct StatusIndicatorView: View { @StateObject private var tunnelManager = TunnelManager.shared @@ -776,6 +787,7 @@ struct SettingsView: View { @AppStorage("autoConnect") private var autoConnect = false @AppStorage("shownTunnelAlert") private var shownTunnelAlert = false @StateObject private var tunnelManager = TunnelManager.shared + @AppStorage("hasNotCompletedSetup") private var hasNotCompletedSetup = true @State private var showNetworkWarning = false @@ -843,7 +855,7 @@ struct SettingsView: View { ) } .navigationTitle(Text("settings")) - .navigationBarTitleDisplayMode(.inline) + .tvOSNavigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("done") { @@ -920,7 +932,7 @@ struct DataCollectionInfoView: View { .padding() } .navigationTitle(Text("data_collection_policy_nav")) - .navigationBarTitleDisplayMode(.inline) + .tvOSNavigationBarTitleDisplayMode(.inline) } } @@ -932,7 +944,7 @@ struct ConnectionLogView: View { .font(.system(.body, design: .monospaced)) } .navigationTitle(Text("logs_nav")) - .navigationBarTitleDisplayMode(.inline) + .tvOSNavigationBarTitleDisplayMode(.inline) } } @@ -1023,7 +1035,7 @@ struct HelpView: View { } } .navigationTitle(Text("help_and_support_nav")) - .navigationBarTitleDisplayMode(.inline) + .tvOSNavigationBarTitleDisplayMode(.inline) } } @@ -1078,6 +1090,8 @@ struct SetupView: View { .fontWeight(.semibold) .frame(height: 50) .frame(maxWidth: .infinity) + .background(Color.blue) + .foregroundColor(.white) .cornerRadius(10) .padding(.horizontal) } @@ -1091,14 +1105,17 @@ struct SetupView: View { .fontWeight(.semibold) .frame(height: 50) .frame(maxWidth: .infinity) + .background(Color.blue) + .foregroundColor(.white) .cornerRadius(10) .padding(.horizontal) } .padding(.bottom) } + } .navigationTitle(Text("setup_nav")) - .navigationBarTitleDisplayMode(.inline) + .tvOSNavigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("setup_skip") { hasNotCompletedSetup = false; dismiss() } @@ -1123,25 +1140,25 @@ struct SetupPageView: View { let page: SetupPage var body: some View { - VStack(spacing: 30) { + VStack(spacing: tvOSSpacing) { Image(systemName: page.imageName) - .font(.system(size: 80)) + .font(.system(size: tvOSImageSize)) .foregroundColor(.blue) - .padding(.top, 50) + .padding(.top, tvOSTopPadding) Text(page.title) - .font(.title) + .font(tvOSTitleFont) .fontWeight(.bold) .multilineTextAlignment(.center) Text(page.description) - .font(.headline) + .font(tvOSDescriptionFont) .foregroundColor(.secondary) .multilineTextAlignment(.center) ScrollView { Text(page.details) - .font(.body) + .font(tvOSBodyFont) .multilineTextAlignment(.leading) .padding(.horizontal) } @@ -1150,8 +1167,58 @@ struct SetupPageView: View { } .padding() } + + // MARK: - Conditional sizes for tvOS + private var tvOSImageSize: CGFloat { + #if os(tvOS) + return 60 + #else + return 80 + #endif + } + + private var tvOSTopPadding: CGFloat { + #if os(tvOS) + return 30 + #else + return 50 + #endif + } + + private var tvOSSpacing: CGFloat { + #if os(tvOS) + return 20 + #else + return 30 + #endif + } + + private var tvOSTitleFont: Font { + #if os(tvOS) + return .headline //.system(size: 35).bold() + #else + return .title + #endif + } + + private var tvOSDescriptionFont: Font { + #if os(tvOS) + return .subheadline + #else + return .headline + #endif + } + + private var tvOSBodyFont: Font { + #if os(tvOS) + return .system(size: 18).bold() + #else + return .body + #endif + } } + class LanguageManager: ObservableObject { static let shared = LanguageManager() @@ -1172,6 +1239,54 @@ class LanguageManager: ObservableObject { } } +#if os(tvOS) +@ViewBuilder +func GroupBox( + label: some View, + @ViewBuilder content: @escaping () -> Content +) -> some View { + #if os(tvOS) + tvOSGroupBox(label: { + label + }, content: content) + #else + SwiftUI.GroupBox(label: label, content: content) + #endif +} + +struct tvOSGroupBox: View { + @ViewBuilder let label: () -> Label + @ViewBuilder let content: () -> Content + + init( + @ViewBuilder label: @escaping () -> Label, + @ViewBuilder content: @escaping () -> Content + ) { + self.label = label + self.content = content + } + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + label() + .font(.headline) + + content() + .padding(.top, 4) + } + .padding() + .background( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .fill(.secondary) + ) + .overlay( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(.separator, lineWidth: 0.5) + ) + } +} +#endif + #Preview { ContentView() }