From 38f8f69a70e3267c83fdf9fb9ec767e1c67897af Mon Sep 17 00:00:00 2001
From: David Roman <2538074+davdroman@users.noreply.github.com>
Date: Thu, 10 Nov 2022 02:46:44 +0000
Subject: [PATCH] macCatalyst, tvOS support (#30)
![image](https://user-images.githubusercontent.com/2538074/200985122-0d317808-171e-4d05-8acb-685818a08174.png)
Closes #12.
---
.github/workflows/ci.yml | 2 ++
.spi.yml | 4 +++
Demo/Demo.xcodeproj/project.pbxproj | 18 ++++++++++---
Demo/Demo/AppDelegate.swift | 6 +++--
Demo/Demo/Demo.entitlements | 10 ++++++++
Demo/Demo/PageView.swift | 25 +++++++++++++------
Demo/Demo/Pages.swift | 10 ++++++--
Demo/Demo/RootView.swift | 6 +++--
Demo/Demo/SettingsView.swift | 2 ++
Package.swift | 2 ++
.../NavigationTransition+UIKit.swift | 24 ++++++++++++------
11 files changed, 85 insertions(+), 24 deletions(-)
create mode 100644 Demo/Demo/Demo.entitlements
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c88c8f2f..c04f8ca2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,6 +25,8 @@ jobs:
- macos-12
platform:
- iOS
+ - mac-catalyst
+ - tvOS
swift:
- 5.5
- 5.6
diff --git a/.spi.yml b/.spi.yml
index 1487742d..2e30d857 100644
--- a/.spi.yml
+++ b/.spi.yml
@@ -4,3 +4,7 @@ builder:
- platform: ios
scheme: NavigationTransitions
documentation_targets: [NavigationTransitions, NavigationTransition, AtomicTransition, Animator, Animation]
+ - platform: macos-xcodebuild
+ scheme: NavigationTransitions
+ - platform: tvos
+ scheme: NavigationTransitions
diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj
index 48c2ce54..29515664 100644
--- a/Demo/Demo.xcodeproj/project.pbxproj
+++ b/Demo/Demo.xcodeproj/project.pbxproj
@@ -12,7 +12,7 @@
D5535834290E9718009E5D72 /* Swing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553582C290E9718009E5D72 /* Swing.swift */; };
D5535835290E9718009E5D72 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553582D290E9718009E5D72 /* RootView.swift */; };
D5535836290E9718009E5D72 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553582E290E9718009E5D72 /* SceneDelegate.swift */; };
- D5535837290E9718009E5D72 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D553582F290E9718009E5D72 /* LaunchScreen.storyboard */; };
+ D5535837290E9718009E5D72 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D553582F290E9718009E5D72 /* LaunchScreen.storyboard */; platformFilter = ios; };
D5535839290E9718009E5D72 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5535831290E9718009E5D72 /* AppDelegate.swift */; };
D553583F290E97C5009E5D72 /* NavigationTransitions in Frameworks */ = {isa = PBXBuildFile; productRef = D553583E290E97C5009E5D72 /* NavigationTransitions */; };
D5535843290F4BEA009E5D72 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5535842290F4BEA009E5D72 /* AppView.swift */; };
@@ -36,6 +36,7 @@
D5535842290F4BEA009E5D72 /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; };
D5535844290F52F7009E5D72 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
D5535846290F5E6F009E5D72 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; };
+ D571826B291C9426003672F5 /* Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Demo.entitlements; sourceTree = ""; };
D5755A78291ADC00007F2201 /* Zoom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zoom.swift; sourceTree = ""; };
D5AAF4042911C59E009743D3 /* PageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageView.swift; sourceTree = ""; };
D5AAF4062911C621009743D3 /* Pages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pages.swift; sourceTree = ""; };
@@ -73,6 +74,7 @@
D553581D290E9691009E5D72 /* Demo */ = {
isa = PBXGroup;
children = (
+ D571826B291C9426003672F5 /* Demo.entitlements */,
D553583C290E978C009E5D72 /* Info.plist */,
D553582F290E9718009E5D72 /* LaunchScreen.storyboard */,
D5535831290E9718009E5D72 /* AppDelegate.swift */,
@@ -317,6 +319,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Demo/Demo.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Demo/Preview Content\"";
@@ -337,10 +340,14 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = mn.dro.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = YES;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = "1,2,3";
+ TVOS_DEPLOYMENT_TARGET = 13.0;
};
name = Debug;
};
@@ -350,6 +357,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Demo/Demo.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Demo/Preview Content\"";
@@ -370,9 +378,13 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = mn.dro.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = YES;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = "1,2,3";
+ TVOS_DEPLOYMENT_TARGET = 13.0;
};
name = Release;
};
diff --git a/Demo/Demo/AppDelegate.swift b/Demo/Demo/AppDelegate.swift
index 85da7d1c..6cd13707 100644
--- a/Demo/Demo/AppDelegate.swift
+++ b/Demo/Demo/AppDelegate.swift
@@ -2,6 +2,7 @@ import SwiftUI
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
+ #if !os(tvOS)
func applicationDidFinishLaunching(_ application: UIApplication) {
customizeNavigationBarAppearance()
customizeTabBarAppearance()
@@ -18,7 +19,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
proxy.scrollEdgeAppearance = customAppearance
proxy.compactAppearance = customAppearance
proxy.standardAppearance = customAppearance
- if #available(iOS 15.0, *) {
+ if #available(iOS 15.0, tvOS 15, *) {
proxy.compactScrollEdgeAppearance = customAppearance
}
}
@@ -31,10 +32,11 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
let proxy = UITabBar.appearance()
proxy.standardAppearance = customAppearance
- if #available(iOS 15, *) {
+ if #available(iOS 15, tvOS 15, *) {
proxy.scrollEdgeAppearance = customAppearance
}
}
+ #endif
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
diff --git a/Demo/Demo/Demo.entitlements b/Demo/Demo/Demo.entitlements
new file mode 100644
index 00000000..ee95ab7e
--- /dev/null
+++ b/Demo/Demo/Demo.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+
+
diff --git a/Demo/Demo/PageView.swift b/Demo/Demo/PageView.swift
index 34488634..01617c41 100644
--- a/Demo/Demo/PageView.swift
+++ b/Demo/Demo/PageView.swift
@@ -14,7 +14,7 @@ struct PageView: View {
ZStack {
Rectangle()
.do {
- if #available(iOS 16, *) {
+ if #available(iOS 16, tvOS 16, *) {
$0.fill(color.gradient)
} else {
$0.fill(color)
@@ -33,23 +33,34 @@ struct PageView: View {
.shadow(color: .white.opacity(0.25), radius: 1, x: 0, y: 1)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.foregroundColor(Color(white: 0.14))
- if let link = link, let destination = destination {
- if #available(iOS 16, *) {
- NavigationLink(value: number + 1) { link }
- } else {
- NavigationLink(destination: destination) { link }
+ .frame(maxWidth: 1200)
+
+ Group {
+ if let link = link, let destination = destination {
+ if #available(iOS 16, tvOS 16, *) {
+ NavigationLink(value: number + 1) { link }
+ } else {
+ NavigationLink(destination: destination) { link }
+ }
}
}
+ #if os(tvOS)
+ .frame(maxWidth: 600)
+ #else
+ .frame(maxWidth: 300)
+ #endif
}
.multilineTextAlignment(.center)
.padding(.horizontal)
.padding(.bottom, 30)
}
+ #if !os(tvOS)
.navigationBarTitle(Text(title), displayMode: .inline)
+ #endif
.navigationBarItems(
trailing: Button(action: { appState.isPresentingSettings = true }) {
Group {
- if #available(iOS 14, *) {
+ if #available(iOS 14, tvOS 16, *) {
Image(systemName: "gearshape")
} else {
Image(systemName: "gear")
diff --git a/Demo/Demo/Pages.swift b/Demo/Demo/Pages.swift
index e69dce7a..b76355cc 100644
--- a/Demo/Demo/Pages.swift
+++ b/Demo/Demo/Pages.swift
@@ -14,7 +14,7 @@ struct PageOne: View {
PageTwo()
}
.do {
- if #available(iOS 16, *) {
+ if #available(iOS 16, tvOS 16, *) {
$0.navigationDestination(for: Int.self) { number in
switch number {
case 1: PageOne()
@@ -125,10 +125,14 @@ struct PageLink: View {
var body: some View {
ZStack {
+ #if !os(tvOS)
RoundedRectangle(cornerRadius: 6, style: .continuous)
.fill(Color.blue.opacity(0.8))
+ #endif
Text(title)
+ #if !os(tvOS)
.foregroundColor(.white)
+ #endif
.font(.system(size: 18, weight: .medium, design: .rounded))
}
.frame(maxHeight: 50)
@@ -150,7 +154,7 @@ struct Code: View {
let shape = RoundedRectangle(cornerRadius: 4, style: .circular)
Text(content)
- .frame(maxWidth: .infinity, alignment: .leading)
+ .frame(maxWidth: 500, alignment: .leading)
.padding(10)
.lineLimit(lineLimit)
.multilineTextAlignment(.leading)
@@ -158,6 +162,7 @@ struct Code: View {
.font(.system(size: 14, design: .monospaced))
.background(shape.stroke(Color(white: 0.1).opacity(0.35), lineWidth: 1))
.background(Color(white: 0.94).opacity(0.6).clipShape(shape))
+ #if !os(tvOS)
.do {
if #available(iOS 15, *) {
$0.textSelection(.enabled)
@@ -165,5 +170,6 @@ struct Code: View {
$0
}
}
+ #endif
}
}
diff --git a/Demo/Demo/RootView.swift b/Demo/Demo/RootView.swift
index 9cb47d3b..84e69b8d 100644
--- a/Demo/Demo/RootView.swift
+++ b/Demo/Demo/RootView.swift
@@ -6,7 +6,7 @@ struct RootView: View {
var body: some View {
Group {
- if #available(iOS 16, *) {
+ if #available(iOS 16, tvOS 16, *) {
NavigationStack {
PageOne()
}
@@ -21,6 +21,8 @@ struct RootView: View {
appState.transition().animation(appState.animation()),
interactivity: appState.interactivity()
)
- .sheet(isPresented: $appState.isPresentingSettings, content: SettingsView.init)
+ .sheet(isPresented: $appState.isPresentingSettings) {
+ SettingsView().environmentObject(appState)
+ }
}
}
diff --git a/Demo/Demo/SettingsView.swift b/Demo/Demo/SettingsView.swift
index 3cc21dd1..87deb38a 100644
--- a/Demo/Demo/SettingsView.swift
+++ b/Demo/Demo/SettingsView.swift
@@ -20,7 +20,9 @@ struct SettingsView: View {
picker("Interactivity", $appState.interactivity)
}
}
+ #if !os(tvOS)
.navigationBarTitle("Settings", displayMode: .inline)
+ #endif
.navigationBarItems(
leading: Button("Shuffle", action: shuffle),
trailing: Button(action: dismiss) { Text("Done").bold() }
diff --git a/Package.swift b/Package.swift
index 1f85d2d4..261281ad 100644
--- a/Package.swift
+++ b/Package.swift
@@ -7,6 +7,8 @@ let package = Package(
name: "swiftui-navigation-transitions",
platforms: [
.iOS(.v13),
+ .macCatalyst(.v13),
+ .tvOS(.v13),
]
)
diff --git a/Sources/NavigationTransitions/NavigationTransition+UIKit.swift b/Sources/NavigationTransitions/NavigationTransition+UIKit.swift
index 1105999e..4b962559 100644
--- a/Sources/NavigationTransitions/NavigationTransition+UIKit.swift
+++ b/Sources/NavigationTransitions/NavigationTransition+UIKit.swift
@@ -53,7 +53,7 @@ extension UISplitViewController {
extension UISplitViewController {
var compactViewController: UIViewController? {
- if #available(iOS 14, *) {
+ if #available(iOS 14, tvOS 14, *) {
return viewController(for: .compact)
} else {
if isCollapsed {
@@ -65,7 +65,7 @@ extension UISplitViewController {
}
var primaryViewController: UIViewController? {
- if #available(iOS 14, *) {
+ if #available(iOS 14, tvOS 14, *) {
return viewController(for: .primary)
} else {
if !isCollapsed {
@@ -77,7 +77,7 @@ extension UISplitViewController {
}
var supplementaryViewController: UIViewController? {
- if #available(iOS 14, *) {
+ if #available(iOS 14, tvOS 14, *) {
return viewController(for: .supplementary)
} else {
if !isCollapsed {
@@ -93,7 +93,7 @@ extension UISplitViewController {
}
var secondaryViewController: UIViewController? {
- if #available(iOS 14, *) {
+ if #available(iOS 14, tvOS 14, *) {
return viewController(for: .secondary)
} else {
if !isCollapsed {
@@ -142,6 +142,13 @@ extension UINavigationController {
defaultDelegate = delegate
}
+ if transition.type == Default.self {
+ delegate = defaultDelegate
+ } else {
+ customDelegate = NavigationTransitionDelegate(transition: transition, baseDelegate: defaultDelegate)
+ }
+
+ #if !os(tvOS)
if defaultPanRecognizer == nil {
defaultPanRecognizer = UIPanGestureRecognizer()
defaultPanRecognizer.targets = defaultEdgePanRecognizer?.targets // https://stackoverflow.com/a/60526328/1922543
@@ -165,8 +172,6 @@ extension UINavigationController {
}
if transition.type == Default.self {
- delegate = defaultDelegate
-
switch interactivity {
case .disabled:
exclusivelyEnableGestureRecognizer(.none)
@@ -176,8 +181,6 @@ extension UINavigationController {
exclusivelyEnableGestureRecognizer(defaultPanRecognizer)
}
} else {
- customDelegate = NavigationTransitionDelegate(transition: transition, baseDelegate: defaultDelegate)
-
switch interactivity {
case .disabled:
exclusivelyEnableGestureRecognizer(.none)
@@ -187,8 +190,10 @@ extension UINavigationController {
exclusivelyEnableGestureRecognizer(panRecognizer)
}
}
+ #endif
}
+ @available(tvOS, unavailable)
private func exclusivelyEnableGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer?) {
for recognizer in [defaultEdgePanRecognizer!, defaultPanRecognizer!, edgePanRecognizer!, panRecognizer!] {
if let gestureRecognizer = gestureRecognizer, recognizer === gestureRecognizer {
@@ -200,6 +205,7 @@ extension UINavigationController {
}
}
+@available(tvOS, unavailable)
extension UINavigationController {
var defaultEdgePanRecognizer: UIScreenEdgePanGestureRecognizer! {
interactivePopGestureRecognizer as? UIScreenEdgePanGestureRecognizer
@@ -225,6 +231,7 @@ extension UINavigationController {
}
}
+@available(tvOS, unavailable)
extension UIGestureRecognizer {
private static var strongDelegateKey = "strongDelegateKey"
@@ -252,6 +259,7 @@ extension UIGestureRecognizer {
}
}
+@available(tvOS, unavailable)
final class NavigationGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate {
private unowned let navigationController: UINavigationController