Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
84196bab1c | |
![]() |
d7b1d71ee3 | |
![]() |
80356a6b96 | |
![]() |
a5a1d7c305 | |
![]() |
c29b50a475 | |
![]() |
01cf328bdb | |
![]() |
930432278c | |
![]() |
67e2a59be1 | |
![]() |
4b81342e51 | |
![]() |
23b08e51fc | |
![]() |
94ab0068fc | |
![]() |
a6ced72772 | |
![]() |
95c4382161 | |
![]() |
1627b93fed | |
![]() |
f327069e9c | |
![]() |
43a8b9cfe8 | |
![]() |
a84dfbfb28 | |
![]() |
f17535beda | |
![]() |
0955cfcec9 | |
![]() |
5b3f3996c7 | |
![]() |
05e13467a0 |
|
@ -8,17 +8,23 @@ on:
|
|||
jobs:
|
||||
deploy:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
podspec:
|
||||
- Introspect.podspec
|
||||
- SwiftUIIntrospect.podspec
|
||||
steps:
|
||||
- name: Git Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # required to be able to find Git tags
|
||||
|
||||
- name: Deploy to CocoaPods Trunk
|
||||
- name: Deploy to CocoaPods Trunk (${{ matrix.podspec }})
|
||||
run: |
|
||||
set -eo pipefail
|
||||
export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
pod lib lint --allow-warnings
|
||||
pod trunk push --allow-warnings
|
||||
pod lib lint ${{ matrix.podspec }} --allow-warnings
|
||||
pod trunk push ${{ matrix.podspec }} --allow-warnings
|
||||
env:
|
||||
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
|
||||
|
|
|
@ -13,8 +13,8 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint-podspec:
|
||||
name: lint podspec
|
||||
lint-podspecs:
|
||||
name: lint podspecs
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Git Checkout
|
||||
|
@ -22,61 +22,77 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0 # required to be able to find Git tags
|
||||
|
||||
- name: Lint Podspec
|
||||
- name: Lint Introspect.podspec
|
||||
run: |
|
||||
set -eo pipefail
|
||||
export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
pod lib lint --allow-warnings
|
||||
pod lib lint Introspect.podspec --allow-warnings
|
||||
|
||||
- name: Lint SwiftUIIntrospect.podspec
|
||||
run: |
|
||||
set -eo pipefail
|
||||
export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
pod lib lint SwiftUIIntrospect.podspec --allow-warnings
|
||||
|
||||
ci:
|
||||
name: ${{ matrix.platform[0] }} ${{ matrix.platform[1] }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os || 'macos-13' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- [ios, 13]
|
||||
- [ios, 14]
|
||||
- [ios, 15]
|
||||
- [ios, 16]
|
||||
|
||||
- [tvos, 13]
|
||||
- [tvos, 14]
|
||||
- [tvos, 15]
|
||||
- [tvos, 16]
|
||||
|
||||
- [macos, 11]
|
||||
- [macos, 12]
|
||||
- [macos, 13]
|
||||
include:
|
||||
- platform: [ios, 13]
|
||||
runtime: iOS 13.7
|
||||
install: true
|
||||
- platform: [ios, 14]
|
||||
os: macos-11
|
||||
xcode_version: 13.2.1
|
||||
sdk: [12.5.1, iPhoneOS, iOS, 14.5]
|
||||
- platform: [tvos, 14]
|
||||
os: macos-11
|
||||
xcode_version: 13.2.1
|
||||
sdk: [12.5.1, AppleTVOS, tvOS, 14.5]
|
||||
|
||||
runtime: iOS 14.5
|
||||
install: true
|
||||
- platform: [ios, 15]
|
||||
os: macos-12
|
||||
xcode_version: 14.2
|
||||
sdk: [13.4.1, iPhoneOS, iOS, 15.5]
|
||||
- platform: [tvos, 15]
|
||||
os: macos-12
|
||||
xcode_version: 14.2
|
||||
sdk: [13.4.1, AppleTVOS, tvOS, 15.5]
|
||||
|
||||
runtime: iOS 15.5
|
||||
install: true
|
||||
- platform: [ios, 16]
|
||||
os: macos-12
|
||||
xcode_version: 14.2
|
||||
runtime: iOS 16.4
|
||||
install: false
|
||||
|
||||
- platform: [tvos, 13]
|
||||
runtime: tvOS 13.4
|
||||
install: true
|
||||
- platform: [tvos, 14]
|
||||
runtime: tvOS 14.5
|
||||
install: true
|
||||
- platform: [tvos, 15]
|
||||
runtime: tvOS 15.4
|
||||
install: true
|
||||
- platform: [tvos, 16]
|
||||
os: macos-12
|
||||
xcode_version: 14.2
|
||||
runtime: tvOS 16.4
|
||||
install: false
|
||||
|
||||
- platform: [macos, 11]
|
||||
os: macos-11
|
||||
xcode_version: 13.2.1
|
||||
xcode: 13.2.1
|
||||
install: false
|
||||
- platform: [macos, 12]
|
||||
os: macos-12
|
||||
xcode_version: 14.2
|
||||
xcode: 14.2
|
||||
install: false
|
||||
- platform: [macos, 13]
|
||||
os: macos-13
|
||||
xcode: 14.3
|
||||
install: false
|
||||
steps:
|
||||
- name: Git Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
@ -84,22 +100,38 @@ jobs:
|
|||
- name: Select Xcode version
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: ${{ matrix.xcode_version }}
|
||||
xcode-version: ${{ matrix.xcode || '14.3' }}
|
||||
|
||||
- if: ${{ matrix.sdk }}
|
||||
name: Symlink SDK
|
||||
run: |
|
||||
echo "Creating Runtimes folder if needed..."
|
||||
sudo mkdir -p /Library/Developer/CoreSimulator/Profiles/Runtimes
|
||||
|
||||
echo "Creating symlink of the ${{ matrix.sdk[2] }} ${{ matrix.sdk[3] }} runtime..."
|
||||
sudo ln -s /Applications/Xcode_${{ matrix.sdk[0] }}.app/Contents/Developer/Platforms/${{ matrix.sdk[1] }}.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/${{ matrix.sdk[2] }}.simruntime /Library/Developer/CoreSimulator/Profiles/Runtimes/${{ matrix.sdk[2] }}\ ${{ matrix.sdk[3] }}.simruntime
|
||||
|
||||
- name: Install Homebrew dependencies
|
||||
- name: Install xcbeautify
|
||||
run: brew install xcbeautify
|
||||
|
||||
- name: Run Tests
|
||||
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }}
|
||||
env:
|
||||
SKIP_SLOW_FASTLANE_WARNINGS: 1
|
||||
FASTLANE_SKIP_UPDATE_CHECK: 1
|
||||
- if: ${{ matrix.install }}
|
||||
name: Install xcodes
|
||||
run: brew install xcodesorg/made/xcodes
|
||||
|
||||
- if: ${{ matrix.install }}
|
||||
name: Install Required Runtime (${{ matrix.runtime }})
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
timeout_minutes: 12
|
||||
max_attempts: 3
|
||||
command: sudo xcodes runtimes install '${{ matrix.runtime }}'
|
||||
|
||||
- name: List Available Simulators
|
||||
run: xcrun simctl list devices available
|
||||
|
||||
- if: ${{ join(matrix.platform, ' ') != 'macos 11' }}
|
||||
name: Build Showcase
|
||||
run: fastlane build platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Showcase
|
||||
|
||||
- if: ${{ join(matrix.platform, ' ') != 'ios 13' && join(matrix.platform, ' ') != 'tvos 13' }}
|
||||
name: Run Tests (Introspect)
|
||||
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Introspect
|
||||
|
||||
- if: ${{ join(matrix.platform, ' ') != 'macos 11' }}
|
||||
name: Run Tests (SwiftUIIntrospect, Debug)
|
||||
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Debug
|
||||
|
||||
- if: ${{ join(matrix.platform, ' ') != 'macos 11' }}
|
||||
name: Run Tests (SwiftUIIntrospect, Release)
|
||||
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Release
|
||||
|
|
46
CHANGELOG.md
46
CHANGELOG.md
|
@ -3,6 +3,52 @@ Changelog
|
|||
|
||||
## master
|
||||
|
||||
## [0.6.1]
|
||||
|
||||
- Improved: optimized receiver lookup algorithm (#246)
|
||||
- Infrastructure: refactored `.introspect` to use `ViewModifier` (#253)
|
||||
- Infrastructure: retry runtime download on timeout or error on CI (#247)
|
||||
|
||||
## [0.6.0]
|
||||
|
||||
### SwiftUIIntrospect
|
||||
|
||||
- Added: iOS 17 / tvOS 17 / macOS 14 compatibility (#243)
|
||||
|
||||
### Introspect
|
||||
|
||||
- Fixed: `UIColorWell` build error on tvOS 13 (#217)
|
||||
|
||||
## [0.5.2]
|
||||
|
||||
### SwiftUIIntrospect
|
||||
|
||||
- Added: selector overrides (#239)
|
||||
- Changed: optimized ancestor controller selectors (#240)
|
||||
|
||||
## [0.5.1]
|
||||
|
||||
### SwiftUIIntrospect
|
||||
|
||||
- Fixed: SwiftUIIntrospect.podspec (#237)
|
||||
|
||||
## [0.5.0]
|
||||
|
||||
### SwiftUIIntrospect
|
||||
|
||||
- Added: support for custom selectors (#233)
|
||||
- Changed: unified introspect modifiers into one (#232)
|
||||
- Fixed: `searchField` introspection (#234)
|
||||
- Documentation: added explicit SPI import (#229)
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
- Added: all-new implementation, API, and module (SwiftUIIntrospect) (#207)
|
||||
|
||||
## [0.3.1]
|
||||
|
||||
- Fixed: wrong Swift version in podspec (#220)
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
- Changed: minimum language version required is now Swift 5.5 (#209)
|
||||
|
|
|
@ -3,24 +3,20 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objectVersion = 55;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
D53071F729983CEF00F1936C /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F629983CEF00F1936C /* App.swift */; };
|
||||
D53071F929983CEF00F1936C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F829983CEF00F1936C /* ContentView.swift */; };
|
||||
D53071FB29983CF000F1936C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53071FA29983CF000F1936C /* Assets.xcassets */; };
|
||||
D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53071FD29983CF000F1936C /* Preview Assets.xcassets */; };
|
||||
D530720729983DCA00F1936C /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = D530720629983DCA00F1936C /* Introspect */; };
|
||||
D5B829752999738200920EBD /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B829742999738200920EBD /* Helpers.swift */; };
|
||||
D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D5E3180229C132B6005847DC /* SwiftUIIntrospect */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
D53071F329983CEF00F1936C /* Showcase.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Showcase.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D53071F629983CEF00F1936C /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||
D53071F829983CEF00F1936C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
D53071FA29983CF000F1936C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
D53071FD29983CF000F1936C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
D530720429983D9300F1936C /* Showcase.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Showcase.entitlements; sourceTree = "<group>"; };
|
||||
D5B829742999738200920EBD /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -30,7 +26,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D530720729983DCA00F1936C /* Introspect in Frameworks */,
|
||||
D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -61,20 +57,10 @@
|
|||
D53071F629983CEF00F1936C /* App.swift */,
|
||||
D53071F829983CEF00F1936C /* ContentView.swift */,
|
||||
D5B829742999738200920EBD /* Helpers.swift */,
|
||||
D53071FA29983CF000F1936C /* Assets.xcassets */,
|
||||
D53071FC29983CF000F1936C /* Preview Content */,
|
||||
);
|
||||
path = Showcase;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D53071FC29983CF000F1936C /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D53071FD29983CF000F1936C /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D530720529983DCA00F1936C /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -99,7 +85,7 @@
|
|||
);
|
||||
name = Showcase;
|
||||
packageProductDependencies = (
|
||||
D530720629983DCA00F1936C /* Introspect */,
|
||||
D5E3180229C132B6005847DC /* SwiftUIIntrospect */,
|
||||
);
|
||||
productName = Showcase;
|
||||
productReference = D53071F329983CEF00F1936C /* Showcase.app */;
|
||||
|
@ -121,7 +107,7 @@
|
|||
};
|
||||
};
|
||||
buildConfigurationList = D53071EE29983CEF00F1936C /* Build configuration list for PBXProject "Showcase" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
compatibilityVersion = "Xcode 13.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
|
@ -143,8 +129,6 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */,
|
||||
D53071FB29983CF000F1936C /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -167,6 +151,7 @@
|
|||
D53071FF29983CF000F1936C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
@ -214,20 +199,24 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TVOS_DEPLOYMENT_TARGET = 14.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 13.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D530720029983CF000F1936C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
@ -269,13 +258,16 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TVOS_DEPLOYMENT_TARGET = 14.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 13.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -283,18 +275,16 @@
|
|||
D530720229983CF000F1936C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Showcase/Showcase.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Showcase/Preview Content\"";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = "";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -304,7 +294,6 @@
|
|||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase;
|
||||
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;
|
||||
|
@ -316,18 +305,16 @@
|
|||
D530720329983CF000F1936C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Showcase/Showcase.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Showcase/Preview Content\"";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = "";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -337,7 +324,6 @@
|
|||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase;
|
||||
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;
|
||||
|
@ -370,9 +356,9 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
D530720629983DCA00F1936C /* Introspect */ = {
|
||||
D5E3180229C132B6005847DC /* SwiftUIIntrospect */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Introspect;
|
||||
productName = SwiftUIIntrospect;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1420"
|
||||
LastUpgradeVersion = "1340"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -14,10 +14,10 @@
|
|||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Introspect-Dynamic"
|
||||
BuildableName = "Introspect-Dynamic"
|
||||
BlueprintName = "Introspect-Dynamic"
|
||||
ReferencedContainer = "container:">
|
||||
BlueprintIdentifier = "D53071F229983CEF00F1936C"
|
||||
BuildableName = "Showcase.app"
|
||||
BlueprintName = "Showcase"
|
||||
ReferencedContainer = "container:Showcase.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
|
@ -40,6 +40,16 @@
|
|||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D53071F229983CEF00F1936C"
|
||||
BuildableName = "Showcase.app"
|
||||
BlueprintName = "Showcase"
|
||||
ReferencedContainer = "container:Showcase.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
@ -47,15 +57,16 @@
|
|||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Introspect-Dynamic"
|
||||
BuildableName = "Introspect-Dynamic"
|
||||
BlueprintName = "Introspect-Dynamic"
|
||||
ReferencedContainer = "container:">
|
||||
BlueprintIdentifier = "D53071F229983CEF00F1936C"
|
||||
BuildableName = "Showcase.app"
|
||||
BlueprintName = "Showcase"
|
||||
ReferencedContainer = "container:Showcase.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
|
@ -1,5 +1,19 @@
|
|||
import SwiftUI
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
@UIApplicationMain
|
||||
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window?.rootViewController = UIHostingController(rootView: ContentView())
|
||||
window?.makeKeyAndVisible()
|
||||
return true
|
||||
}
|
||||
}
|
||||
#elseif os(macOS)
|
||||
@main
|
||||
struct App: SwiftUI.App {
|
||||
var body: some Scene {
|
||||
|
@ -8,3 +22,4 @@ struct App: SwiftUI.App {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +1,50 @@
|
|||
import SwiftUI
|
||||
import Introspect
|
||||
import SwiftUIIntrospect
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var selection = 0
|
||||
@State var selection = 0
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $selection) {
|
||||
ListShowcase()
|
||||
.tabItem { Text("List") }
|
||||
.tag(0)
|
||||
.introspectTabBarController { tabBarController in
|
||||
tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor
|
||||
}
|
||||
ScrollViewShowcase()
|
||||
.tabItem { Text("ScrollView") }
|
||||
.tag(1)
|
||||
#if !os(macOS)
|
||||
NavigationShowcase()
|
||||
.tabItem { Text("Navigation") }
|
||||
.tag(2)
|
||||
ViewControllerShowcase()
|
||||
.tabItem { Text("ViewController") }
|
||||
.tag(3)
|
||||
#endif
|
||||
SimpleElementsShowcase()
|
||||
.tabItem { Text("Simple elements") }
|
||||
.tag(4)
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tabBarController in
|
||||
tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { splitView in
|
||||
splitView.subviews.first?.layer?.backgroundColor = NSColor.green.cgColor
|
||||
}
|
||||
#endif
|
||||
.preferredColorScheme(.light)
|
||||
}
|
||||
}
|
||||
|
||||
struct ListShowcase: View {
|
||||
var body: some View {
|
||||
|
||||
HStack {
|
||||
VStack(spacing: 40) {
|
||||
VStack {
|
||||
Text("Default")
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
List {
|
||||
Text("Item 1")
|
||||
Text("Item 2")
|
||||
|
@ -40,28 +52,54 @@ struct ListShowcase: View {
|
|||
}
|
||||
|
||||
VStack {
|
||||
Text("List.introspectTableView()")
|
||||
Text(".introspect(.list, ...)")
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
.font(.system(.subheadline, design: .monospaced))
|
||||
List {
|
||||
Text("Item 1")
|
||||
Text("Item 2")
|
||||
}
|
||||
.introspectTableView { tableView in
|
||||
#if !os(tvOS)
|
||||
tableView.separatorStyle = .none
|
||||
#endif
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tableView in
|
||||
tableView.backgroundView = UIView()
|
||||
tableView.backgroundColor = .cyan
|
||||
}
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { collectionView in
|
||||
collectionView.backgroundView = UIView()
|
||||
collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { tableView in
|
||||
tableView.backgroundColor = .cyan
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
VStack {
|
||||
Text("child.introspectTableView()")
|
||||
Text(".introspect(.list, ..., scope: .ancestor)")
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
.font(.system(.subheadline, design: .monospaced))
|
||||
List {
|
||||
Text("Item 1")
|
||||
Text("Item 2")
|
||||
.introspectTableView { tableView in
|
||||
#if !os(tvOS)
|
||||
tableView.separatorStyle = .none
|
||||
#endif
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { tableView in
|
||||
tableView.backgroundView = UIView()
|
||||
tableView.backgroundColor = .cyan
|
||||
}
|
||||
.introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { collectionView in
|
||||
collectionView.backgroundView = UIView()
|
||||
collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { tableView in
|
||||
tableView.backgroundColor = .cyan
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,75 +107,114 @@ struct ListShowcase: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct ScrollViewShowcase: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 40) {
|
||||
ScrollView {
|
||||
Text("Default")
|
||||
.frame(maxWidth: .infinity)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Text(".introspect(.scrollView, ...)")
|
||||
.frame(maxWidth: .infinity)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
.font(.system(.subheadline, design: .monospaced))
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
|
||||
scrollView.layer.backgroundColor = UIColor.cyan.cgColor
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { scrollView in
|
||||
scrollView.drawsBackground = true
|
||||
scrollView.backgroundColor = .cyan
|
||||
}
|
||||
#endif
|
||||
|
||||
ScrollView {
|
||||
Text(".introspect(.scrollView, ..., scope: .ancestor)")
|
||||
.frame(maxWidth: .infinity)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
.font(.system(.subheadline, design: .monospaced))
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in
|
||||
scrollView.layer.backgroundColor = UIColor.cyan.cgColor
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { scrollView in
|
||||
scrollView.drawsBackground = true
|
||||
scrollView.backgroundColor = .cyan
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NavigationShowcase: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Text("Customized")
|
||||
.do {
|
||||
if #available(iOS 15, tvOS 15, *) {
|
||||
Text("Content")
|
||||
.modifier {
|
||||
if #available(iOS 15, tvOS 15, macOS 12, *) {
|
||||
$0.searchable(text: .constant(""))
|
||||
} else {
|
||||
$0
|
||||
}
|
||||
}
|
||||
.do {
|
||||
#if os(iOS)
|
||||
if #available(iOS 15, *) {
|
||||
$0.introspectSearchController { searchController in
|
||||
searchController.searchBar.backgroundColor = .purple
|
||||
}
|
||||
}
|
||||
#else
|
||||
$0
|
||||
#endif
|
||||
}
|
||||
#if !os(tvOS)
|
||||
#if os(iOS)
|
||||
.navigationBarTitle(Text("Customized"), displayMode: .inline)
|
||||
#elseif os(macOS)
|
||||
.navigationTitle(Text("Navigation"))
|
||||
#endif
|
||||
.introspectNavigationController { navigationController in
|
||||
navigationController.navigationBar.backgroundColor = .red
|
||||
}
|
||||
}
|
||||
.introspectSplitViewController { splitViewController in
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in
|
||||
navigationController.navigationBar.backgroundColor = .cyan
|
||||
}
|
||||
.introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { splitViewController in
|
||||
splitViewController.preferredDisplayMode = .oneOverSecondary
|
||||
}
|
||||
.introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in
|
||||
navigationController.navigationBar.backgroundColor = .cyan
|
||||
}
|
||||
.introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17)) { searchBar in
|
||||
searchBar.backgroundColor = .red
|
||||
#if os(iOS)
|
||||
searchBar.searchTextField.backgroundColor = .purple
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
struct ViewControllerShowcase: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
VStack {
|
||||
Text("Customized")
|
||||
}
|
||||
.introspectViewController { viewController in
|
||||
viewController.navigationItem.title = "Customized"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ScrollViewShowcase: View {
|
||||
var body: some View {
|
||||
HStack {
|
||||
ScrollView {
|
||||
Text("Default")
|
||||
}
|
||||
ScrollView {
|
||||
Text("ScrollView.introspectScrollView()")
|
||||
}
|
||||
.introspectScrollView { scrollView in
|
||||
scrollView.layer.backgroundColor = UIColor.red.cgColor
|
||||
}
|
||||
ScrollView {
|
||||
Text("child.introspectScrollView()")
|
||||
.introspectScrollView { scrollView in
|
||||
scrollView.layer.backgroundColor = UIColor.green.cgColor
|
||||
}
|
||||
Text(".introspect(.view, ...)")
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 12)
|
||||
.font(.system(.subheadline, design: .monospaced))
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
.introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { viewController in
|
||||
viewController.children.first?.view.backgroundColor = .cyan
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct SimpleElementsShowcase: View {
|
||||
|
||||
|
@ -151,28 +228,49 @@ struct SimpleElementsShowcase: View {
|
|||
VStack {
|
||||
HStack {
|
||||
TextField("Text Field Red", text: $textFieldValue)
|
||||
.introspectTextField { textField in
|
||||
textField.layer.backgroundColor = UIColor.red.cgColor
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in
|
||||
textField.backgroundColor = .red
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { textField in
|
||||
textField.backgroundColor = .red
|
||||
}
|
||||
#endif
|
||||
|
||||
TextField("Text Field Green", text: $textFieldValue)
|
||||
.introspectTextField { textField in
|
||||
textField.layer.backgroundColor = UIColor.green.cgColor
|
||||
.cornerRadius(8)
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in
|
||||
textField.backgroundColor = .green
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { textField in
|
||||
textField.backgroundColor = .green
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HStack {
|
||||
Toggle("Toggle Red", isOn: $toggleValue)
|
||||
#if !os(tvOS)
|
||||
.introspectSwitch { uiSwitch in
|
||||
uiSwitch.layer.backgroundColor = UIColor.red.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in
|
||||
toggle.backgroundColor = .red
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { toggle in
|
||||
toggle.layer?.backgroundColor = NSColor.red.cgColor
|
||||
}
|
||||
#endif
|
||||
|
||||
Toggle("Toggle Green", isOn: $toggleValue)
|
||||
#if !os(tvOS)
|
||||
.introspectSwitch { uiSwitch in
|
||||
uiSwitch.layer.backgroundColor = UIColor.green.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in
|
||||
toggle.backgroundColor = .green
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { toggle in
|
||||
toggle.layer?.backgroundColor = NSColor.green.cgColor
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -180,39 +278,69 @@ struct SimpleElementsShowcase: View {
|
|||
#if !os(tvOS)
|
||||
HStack {
|
||||
Slider(value: $sliderValue, in: 0...100)
|
||||
.introspectSlider { slider in
|
||||
slider.layer.backgroundColor = UIColor.red.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { slider in
|
||||
slider.backgroundColor = .red
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { slider in
|
||||
slider.layer?.backgroundColor = NSColor.red.cgColor
|
||||
}
|
||||
#endif
|
||||
|
||||
Slider(value: $sliderValue, in: 0...100)
|
||||
.introspectSlider { slider in
|
||||
slider.layer.backgroundColor = UIColor.green.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { slider in
|
||||
slider.backgroundColor = .green
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { slider in
|
||||
slider.layer?.backgroundColor = NSColor.green.cgColor
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HStack {
|
||||
Stepper(onIncrement: {}, onDecrement: {}) {
|
||||
Text("Stepper Red")
|
||||
}
|
||||
.introspectStepper { stepper in
|
||||
stepper.layer.backgroundColor = UIColor.red.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { stepper in
|
||||
stepper.backgroundColor = .red
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { stepper in
|
||||
stepper.layer?.backgroundColor = NSColor.red.cgColor
|
||||
}
|
||||
#endif
|
||||
|
||||
Stepper(onIncrement: {}, onDecrement: {}) {
|
||||
Text("Stepper Green")
|
||||
}
|
||||
.introspectStepper { stepper in
|
||||
stepper.layer.backgroundColor = UIColor.green.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { stepper in
|
||||
stepper.backgroundColor = .green
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { stepper in
|
||||
stepper.layer?.backgroundColor = NSColor.green.cgColor
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HStack {
|
||||
DatePicker(selection: $datePickerValue) {
|
||||
Text("DatePicker Red")
|
||||
}
|
||||
.introspectDatePicker { datePicker in
|
||||
datePicker.layer.backgroundColor = UIColor.red.cgColor
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in
|
||||
datePicker.backgroundColor = .red
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { datePicker in
|
||||
datePicker.layer?.backgroundColor = NSColor.red.cgColor
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -223,9 +351,15 @@ struct SimpleElementsShowcase: View {
|
|||
Text("Option 3").tag(2)
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.introspectSegmentedControl { segmentedControl in
|
||||
segmentedControl.layer.backgroundColor = UIColor.red.cgColor
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in
|
||||
datePicker.backgroundColor = .red
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { datePicker in
|
||||
datePicker.layer?.backgroundColor = NSColor.red.cgColor
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,9 +368,6 @@ struct SimpleElementsShowcase: View {
|
|||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
ListShowcase()
|
||||
NavigationShowcase()
|
||||
}
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func `do`<TransformedView: View>(
|
||||
@ViewBuilder
|
||||
func modifier<TransformedView: View>(
|
||||
@ViewBuilder transform: (Self) -> TransformedView
|
||||
) -> TransformedView {
|
||||
transform(self)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ Pod::Spec.new do |spec|
|
|||
|
||||
spec.source_files = 'Introspect/*.swift'
|
||||
|
||||
spec.swift_version = '5.4'
|
||||
spec.swift_version = '5.5'
|
||||
spec.ios.deployment_target = '13.0'
|
||||
spec.tvos.deployment_target = '13.0'
|
||||
spec.osx.deployment_target = '10.15'
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
<FileRef
|
||||
location = "group:Examples/Showcase/Showcase.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Tests/Tests.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:">
|
||||
</FileRef>
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Introspect-Static"
|
||||
BuildableName = "Introspect-Static"
|
||||
BlueprintName = "Introspect-Static"
|
||||
BlueprintIdentifier = "SwiftUIIntrospect"
|
||||
BuildableName = "SwiftUIIntrospect"
|
||||
BlueprintName = "SwiftUIIntrospect"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
|
@ -50,9 +50,9 @@
|
|||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Introspect-Static"
|
||||
BuildableName = "Introspect-Static"
|
||||
BlueprintName = "Introspect-Static"
|
||||
BlueprintIdentifier = "SwiftUIIntrospect"
|
||||
BuildableName = "SwiftUIIntrospect"
|
||||
BlueprintName = "SwiftUIIntrospect"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
|
@ -187,11 +187,13 @@ extension View {
|
|||
}
|
||||
|
||||
/// Finds a `UIColorWell` from a `SwiftUI.ColorPicker`
|
||||
#if os(iOS)
|
||||
@available(iOS 14, *)
|
||||
@available(tvOS, unavailable)
|
||||
public func introspectColorWell(customize: @escaping (UIColorWell) -> ()) -> some View {
|
||||
introspect(selector: TargetViewSelector.siblingContaining, customize: customize)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -367,6 +367,7 @@ private struct SegmentedControlTestView: View {
|
|||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
@available(iOS 14.0, *)
|
||||
@available(tvOS, unavailable)
|
||||
private struct ColorWellTestView: View {
|
||||
|
@ -380,6 +381,7 @@ private struct ColorWellTestView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
import MapKit
|
||||
@available(iOS 14, tvOS 14, *)
|
||||
|
|
|
@ -17,12 +17,12 @@ let package = Package(
|
|||
targets: [
|
||||
.target(
|
||||
name: "Introspect",
|
||||
path: "Introspect" // TODO: rename to Sources for v1.0
|
||||
path: "Introspect"
|
||||
),
|
||||
.testTarget(
|
||||
name: "IntrospectTests",
|
||||
dependencies: ["Introspect"],
|
||||
path: "IntrospectTests" // TODO: rename to Tests for v1.0
|
||||
path: "IntrospectTests"
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// swift-tools-version:5.7
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "swiftui-introspect",
|
||||
platforms: [
|
||||
.iOS(.v13),
|
||||
.macOS(.v10_15),
|
||||
.tvOS(.v13),
|
||||
],
|
||||
products: [
|
||||
// legacy library
|
||||
.library(name: "Introspect", targets: ["Introspect"]),
|
||||
.library(name: "Introspect-Static", type: .static, targets: ["Introspect"]),
|
||||
.library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]),
|
||||
|
||||
// new experimental library
|
||||
.library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]),
|
||||
.library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]),
|
||||
.library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "Introspect",
|
||||
path: "Introspect"
|
||||
),
|
||||
.testTarget(
|
||||
name: "IntrospectTests",
|
||||
dependencies: ["Introspect"],
|
||||
path: "IntrospectTests"
|
||||
),
|
||||
|
||||
.target(
|
||||
name: "SwiftUIIntrospect",
|
||||
path: "Sources"
|
||||
),
|
||||
]
|
||||
)
|
|
@ -1,3 +1,11 @@
|
|||
> **Note**
|
||||
>
|
||||
> [`SwiftUIIntrospect`](Package@swift-5.7.swift#L19) is an all-new module based off the original [`Introspect`](Package.swift#L13) module that improves on stability, predictability, and ergonomics.
|
||||
>
|
||||
> Both modules currently live together under this repo, but the plan is to ultimately obsolete `Introspect` in favor of `SwiftUIIntrospect` as part of a 1.0 release.
|
||||
>
|
||||
> Read the [`SwiftUIIntrospect` documentation](docs/SwiftUIIntrospect.md) to learn more.
|
||||
|
||||
Introspect for SwiftUI
|
||||
======================
|
||||
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
import SwiftUI
|
||||
|
||||
public struct IntrospectionScope: OptionSet {
|
||||
public static let receiver = Self(rawValue: 1 << 0)
|
||||
public static let ancestor = Self(rawValue: 1 << 1)
|
||||
|
||||
@_spi(Private) public let rawValue: UInt
|
||||
|
||||
@_spi(Private) public init(rawValue: UInt) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func introspect<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity>(
|
||||
_ viewType: SwiftUIViewType,
|
||||
on platforms: (PlatformViewVersions<SwiftUIViewType, PlatformSpecificEntity>)...,
|
||||
scope: IntrospectionScope? = nil,
|
||||
customize: @escaping (PlatformSpecificEntity) -> Void
|
||||
) -> some View {
|
||||
self.modifier(IntrospectModifier(viewType, platforms: platforms, scope: scope, customize: customize))
|
||||
}
|
||||
}
|
||||
|
||||
struct IntrospectModifier<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity>: ViewModifier {
|
||||
let id = IntrospectionViewID()
|
||||
let scope: IntrospectionScope
|
||||
let selector: IntrospectionSelector<PlatformSpecificEntity>?
|
||||
let customize: (PlatformSpecificEntity) -> Void
|
||||
|
||||
init(
|
||||
_ viewType: SwiftUIViewType,
|
||||
platforms: [PlatformViewVersions<SwiftUIViewType, PlatformSpecificEntity>],
|
||||
scope: IntrospectionScope?,
|
||||
customize: @escaping (PlatformSpecificEntity) -> Void
|
||||
) {
|
||||
self.scope = scope ?? viewType.scope
|
||||
if let platform = platforms.first(where: \.isCurrent) {
|
||||
self.selector = platform.selector ?? .default
|
||||
} else {
|
||||
self.selector = nil
|
||||
}
|
||||
self.customize = customize
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if let selector {
|
||||
content
|
||||
.background(
|
||||
IntrospectionAnchorView(id: id)
|
||||
.frame(width: 0, height: 0)
|
||||
)
|
||||
.overlay(
|
||||
IntrospectionView(id: id, selector: { selector($0, scope) }, customize: customize)
|
||||
.frame(width: 0, height: 0)
|
||||
)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol PlatformEntity: AnyObject {
|
||||
associatedtype Base: PlatformEntity
|
||||
|
||||
@_spi(Internals)
|
||||
var ancestor: Base? { get }
|
||||
|
||||
@_spi(Internals)
|
||||
var descendants: [Base] { get }
|
||||
|
||||
@_spi(Internals)
|
||||
func isDescendant(of other: Base) -> Bool
|
||||
}
|
||||
|
||||
extension PlatformEntity {
|
||||
@_spi(Internals)
|
||||
public var ancestors: some Sequence<Base> {
|
||||
sequence(first: self~, next: { $0.ancestor~ }).dropFirst()
|
||||
}
|
||||
|
||||
@_spi(Internals)
|
||||
public var allDescendants: some Sequence<Base> {
|
||||
recursiveSequence([self~], children: { $0.descendants~ }).dropFirst()
|
||||
}
|
||||
|
||||
func nearestCommonAncestor(with other: Base) -> Base? {
|
||||
var nearestAncestor: Base? = self~
|
||||
|
||||
while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) {
|
||||
nearestAncestor = currentEntity.ancestor~
|
||||
}
|
||||
|
||||
return nearestAncestor
|
||||
}
|
||||
|
||||
func allDescendants(between bottomEntity: Base, and topEntity: Base) -> some Sequence<Base> {
|
||||
self.allDescendants
|
||||
.lazy
|
||||
.drop(while: { $0 !== bottomEntity })
|
||||
.prefix(while: { $0 !== topEntity })
|
||||
}
|
||||
|
||||
func receiver<PlatformSpecificEntity: PlatformEntity>(
|
||||
ofType type: PlatformSpecificEntity.Type
|
||||
) -> PlatformSpecificEntity? {
|
||||
let frontEntity = self
|
||||
guard
|
||||
let backEntity = frontEntity.introspectionAnchorEntity,
|
||||
let commonAncestor = backEntity.nearestCommonAncestor(with: frontEntity~)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return commonAncestor
|
||||
.allDescendants(between: backEntity~, and: frontEntity~)
|
||||
.compactMap { $0 as? PlatformSpecificEntity }
|
||||
.first
|
||||
}
|
||||
|
||||
func ancestor<PlatformSpecificEntity: PlatformEntity>(
|
||||
ofType type: PlatformSpecificEntity.Type
|
||||
) -> PlatformSpecificEntity? {
|
||||
self.ancestors
|
||||
.lazy
|
||||
.compactMap { $0 as? PlatformSpecificEntity }
|
||||
.first
|
||||
}
|
||||
}
|
||||
|
||||
extension PlatformView: PlatformEntity {
|
||||
@_spi(Internals)
|
||||
public var ancestor: PlatformView? {
|
||||
superview
|
||||
}
|
||||
|
||||
@_spi(Internals)
|
||||
public var descendants: [PlatformView] {
|
||||
subviews
|
||||
}
|
||||
}
|
||||
|
||||
extension PlatformViewController: PlatformEntity {
|
||||
@_spi(Internals)
|
||||
public var ancestor: PlatformViewController? {
|
||||
parent
|
||||
}
|
||||
|
||||
@_spi(Internals)
|
||||
public var descendants: [PlatformViewController] {
|
||||
children
|
||||
}
|
||||
|
||||
@_spi(Internals)
|
||||
public func isDescendant(of other: PlatformViewController) -> Bool {
|
||||
self.ancestors.contains(other)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
public protocol IntrospectableViewType {
|
||||
var scope: IntrospectionScope { get }
|
||||
}
|
||||
|
||||
extension IntrospectableViewType {
|
||||
public var scope: IntrospectionScope { .receiver }
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
@_spi(Internals)
|
||||
public struct IntrospectionSelector<Target: PlatformEntity> {
|
||||
@_spi(Internals)
|
||||
public static var `default`: Self { .from(Target.self, selector: { $0 }) }
|
||||
|
||||
@_spi(Internals)
|
||||
public static func from<Entry: PlatformEntity>(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self {
|
||||
.init(
|
||||
receiverSelector: { controller in
|
||||
controller.as(Entry.Base.self)?.receiver(ofType: Entry.self).flatMap(selector)
|
||||
},
|
||||
ancestorSelector: { controller in
|
||||
controller.as(Entry.Base.self)?.ancestor(ofType: Entry.self).flatMap(selector)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private var receiverSelector: (IntrospectionPlatformViewController) -> Target?
|
||||
private var ancestorSelector: (IntrospectionPlatformViewController) -> Target?
|
||||
|
||||
private init(
|
||||
receiverSelector: @escaping (IntrospectionPlatformViewController) -> Target?,
|
||||
ancestorSelector: @escaping (IntrospectionPlatformViewController) -> Target?
|
||||
) {
|
||||
self.receiverSelector = receiverSelector
|
||||
self.ancestorSelector = ancestorSelector
|
||||
}
|
||||
|
||||
@_spi(Internals)
|
||||
public func withReceiverSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self {
|
||||
var copy = self
|
||||
copy.receiverSelector = selector
|
||||
return copy
|
||||
}
|
||||
|
||||
@_spi(Internals)
|
||||
public func withAncestorSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self {
|
||||
var copy = self
|
||||
copy.ancestorSelector = selector
|
||||
return copy
|
||||
}
|
||||
|
||||
func callAsFunction(_ controller: IntrospectionPlatformViewController, _ scope: IntrospectionScope) -> Target? {
|
||||
if
|
||||
scope.contains(.receiver),
|
||||
let target = receiverSelector(controller)
|
||||
{
|
||||
return target
|
||||
}
|
||||
if
|
||||
scope.contains(.ancestor),
|
||||
let target = ancestorSelector(controller)
|
||||
{
|
||||
return target
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension PlatformViewController {
|
||||
func `as`<Base: PlatformEntity>(_ baseType: Base.Type) -> (any PlatformEntity)? {
|
||||
if Base.self == PlatformView.self {
|
||||
#if canImport(UIKit)
|
||||
return viewIfLoaded
|
||||
#elseif canImport(AppKit)
|
||||
return isViewLoaded ? view : nil
|
||||
#endif
|
||||
} else if Base.self == PlatformViewController.self {
|
||||
return self
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
import SwiftUI
|
||||
|
||||
typealias IntrospectionViewID = UUID
|
||||
|
||||
fileprivate enum IntrospectionStore {
|
||||
static var shared: [IntrospectionViewID: Pair] = [:]
|
||||
|
||||
struct Pair {
|
||||
weak var controller: IntrospectionPlatformViewController?
|
||||
weak var anchor: IntrospectionAnchorPlatformViewController?
|
||||
}
|
||||
}
|
||||
|
||||
extension PlatformEntity {
|
||||
var introspectionAnchorEntity: Base? {
|
||||
if let introspectionController = self as? IntrospectionPlatformViewController {
|
||||
return IntrospectionStore.shared[introspectionController.id]?.anchor~
|
||||
}
|
||||
if
|
||||
let view = self as? PlatformView,
|
||||
let introspectionController = view.introspectionController
|
||||
{
|
||||
return IntrospectionStore.shared[introspectionController.id]?.anchor?.view~
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// ⚓️
|
||||
struct IntrospectionAnchorView: PlatformViewControllerRepresentable {
|
||||
#if canImport(UIKit)
|
||||
typealias UIViewControllerType = IntrospectionAnchorPlatformViewController
|
||||
#elseif canImport(AppKit)
|
||||
typealias NSViewControllerType = IntrospectionAnchorPlatformViewController
|
||||
#endif
|
||||
|
||||
@Binding
|
||||
private var observed: Void // workaround for state changes not triggering view updates
|
||||
|
||||
let id: IntrospectionViewID
|
||||
|
||||
init(id: IntrospectionViewID) {
|
||||
self._observed = .constant(())
|
||||
self.id = id
|
||||
}
|
||||
|
||||
func makePlatformViewController(context: Context) -> IntrospectionAnchorPlatformViewController {
|
||||
IntrospectionAnchorPlatformViewController(id: id)
|
||||
}
|
||||
|
||||
func updatePlatformViewController(_ controller: IntrospectionAnchorPlatformViewController, context: Context) {}
|
||||
|
||||
static func dismantlePlatformViewController(_ controller: IntrospectionAnchorPlatformViewController, coordinator: Coordinator) {}
|
||||
}
|
||||
|
||||
final class IntrospectionAnchorPlatformViewController: PlatformViewController {
|
||||
init(id: IntrospectionViewID) {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
IntrospectionStore.shared[id, default: .init()].anchor = self
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
override func loadView() {
|
||||
view = NSView()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct IntrospectionView<Target: PlatformEntity>: PlatformViewControllerRepresentable {
|
||||
#if canImport(UIKit)
|
||||
typealias UIViewControllerType = IntrospectionPlatformViewController
|
||||
#elseif canImport(AppKit)
|
||||
typealias NSViewControllerType = IntrospectionPlatformViewController
|
||||
#endif
|
||||
|
||||
final class TargetCache {
|
||||
weak var target: Target?
|
||||
}
|
||||
|
||||
@Binding
|
||||
private var observed: Void // workaround for state changes not triggering view updates
|
||||
private let id: IntrospectionViewID
|
||||
private let selector: (IntrospectionPlatformViewController) -> Target?
|
||||
private let customize: (Target) -> Void
|
||||
|
||||
init(
|
||||
id: IntrospectionViewID,
|
||||
selector: @escaping (IntrospectionPlatformViewController) -> Target?,
|
||||
customize: @escaping (Target) -> Void
|
||||
) {
|
||||
self._observed = .constant(())
|
||||
self.id = id
|
||||
self.selector = selector
|
||||
self.customize = customize
|
||||
}
|
||||
|
||||
func makeCoordinator() -> TargetCache {
|
||||
TargetCache()
|
||||
}
|
||||
|
||||
func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController {
|
||||
let controller = IntrospectionPlatformViewController(id: id) { controller in
|
||||
guard let target = selector(controller) else {
|
||||
return
|
||||
}
|
||||
context.coordinator.target = target
|
||||
customize(target)
|
||||
controller.handler = nil
|
||||
}
|
||||
|
||||
// - Workaround -
|
||||
// iOS/tvOS 13 sometimes need a nudge on the next run loop.
|
||||
if #available(iOS 14, tvOS 14, *) {} else {
|
||||
DispatchQueue.main.async { [weak controller] in
|
||||
controller?.handler?()
|
||||
}
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
func updatePlatformViewController(_ controller: IntrospectionPlatformViewController, context: Context) {
|
||||
guard let target = context.coordinator.target ?? selector(controller) else {
|
||||
return
|
||||
}
|
||||
customize(target)
|
||||
}
|
||||
|
||||
static func dismantlePlatformViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) {
|
||||
controller.handler = nil
|
||||
}
|
||||
}
|
||||
|
||||
final class IntrospectionPlatformViewController: PlatformViewController {
|
||||
let id: IntrospectionViewID
|
||||
var handler: (() -> Void)? = nil
|
||||
|
||||
fileprivate init(
|
||||
id: IntrospectionViewID,
|
||||
handler: ((IntrospectionPlatformViewController) -> Void)?
|
||||
) {
|
||||
self.id = id
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
self.handler = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
handler?(self)
|
||||
}
|
||||
IntrospectionStore.shared[id, default: .init()].controller = self
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.introspectionController = self
|
||||
handler?()
|
||||
}
|
||||
|
||||
override func didMove(toParent parent: UIViewController?) {
|
||||
super.didMove(toParent: parent)
|
||||
handler?()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
handler?()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
handler?()
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
override func loadView() {
|
||||
view = NSView()
|
||||
view.introspectionController = self
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
handler?()
|
||||
}
|
||||
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
handler?()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
import ObjectiveC
|
||||
|
||||
extension PlatformView {
|
||||
fileprivate var introspectionController: IntrospectionPlatformViewController? {
|
||||
get {
|
||||
let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self)
|
||||
return objc_getAssociatedObject(self, key) as? IntrospectionPlatformViewController
|
||||
}
|
||||
set {
|
||||
let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self)
|
||||
objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_ASSIGN)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
import Foundation
|
||||
|
||||
public protocol PlatformVersion {
|
||||
var isCurrent: Bool { get }
|
||||
}
|
||||
|
||||
public struct iOSVersion: PlatformVersion {
|
||||
public let isCurrent: Bool
|
||||
|
||||
public init(isCurrent: () -> Bool) {
|
||||
self.isCurrent = isCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
extension iOSVersion {
|
||||
public static let v13 = iOSVersion {
|
||||
if #available(iOS 14, *) {
|
||||
return false
|
||||
}
|
||||
if #available(iOS 13, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v14 = iOSVersion {
|
||||
if #available(iOS 15, *) {
|
||||
return false
|
||||
}
|
||||
if #available(iOS 14, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v15 = iOSVersion {
|
||||
if #available(iOS 16, *) {
|
||||
return false
|
||||
}
|
||||
if #available(iOS 15, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v16 = iOSVersion {
|
||||
if #available(iOS 17, *) {
|
||||
return false
|
||||
}
|
||||
if #available(iOS 16, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v17 = iOSVersion {
|
||||
if #available(iOS 18, *) {
|
||||
return false
|
||||
}
|
||||
if #available(iOS 17, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public struct tvOSVersion: PlatformVersion {
|
||||
public let isCurrent: Bool
|
||||
|
||||
public init(isCurrent: () -> Bool) {
|
||||
self.isCurrent = isCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSVersion {
|
||||
public static let v13 = tvOSVersion {
|
||||
if #available(tvOS 14, *) {
|
||||
return false
|
||||
}
|
||||
if #available(tvOS 13, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v14 = tvOSVersion {
|
||||
if #available(tvOS 15, *) {
|
||||
return false
|
||||
}
|
||||
if #available(tvOS 14, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v15 = tvOSVersion {
|
||||
if #available(tvOS 16, *) {
|
||||
return false
|
||||
}
|
||||
if #available(tvOS 15, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v16 = tvOSVersion {
|
||||
if #available(tvOS 17, *) {
|
||||
return false
|
||||
}
|
||||
if #available(tvOS 16, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v17 = tvOSVersion {
|
||||
if #available(tvOS 18, *) {
|
||||
return false
|
||||
}
|
||||
if #available(tvOS 17, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public struct macOSVersion: PlatformVersion {
|
||||
public let isCurrent: Bool
|
||||
|
||||
public init(isCurrent: () -> Bool) {
|
||||
self.isCurrent = isCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
extension macOSVersion {
|
||||
public static let v10_15 = macOSVersion {
|
||||
if #available(macOS 11, *) {
|
||||
return false
|
||||
}
|
||||
if #available(macOS 10.15, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v10_15_4 = macOSVersion {
|
||||
if #available(macOS 11, *) {
|
||||
return false
|
||||
}
|
||||
if #available(macOS 10.15.4, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v11 = macOSVersion {
|
||||
if #available(macOS 12, *) {
|
||||
return false
|
||||
}
|
||||
if #available(macOS 11, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v12 = macOSVersion {
|
||||
if #available(macOS 13, *) {
|
||||
return false
|
||||
}
|
||||
if #available(macOS 12, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v13 = macOSVersion {
|
||||
if #available(macOS 14, *) {
|
||||
return false
|
||||
}
|
||||
if #available(macOS 13, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static let v14 = macOSVersion {
|
||||
if #available(macOS 15, *) {
|
||||
return false
|
||||
}
|
||||
if #available(macOS 14, *) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import SwiftUI
|
||||
|
||||
#if canImport(UIKit)
|
||||
public typealias PlatformView = UIView
|
||||
#elseif canImport(AppKit)
|
||||
public typealias PlatformView = NSView
|
||||
#endif
|
||||
|
||||
#if canImport(UIKit)
|
||||
public typealias PlatformViewController = UIViewController
|
||||
#elseif canImport(AppKit)
|
||||
public typealias PlatformViewController = NSViewController
|
||||
#endif
|
||||
|
||||
#if canImport(UIKit)
|
||||
typealias _PlatformViewControllerRepresentable = UIViewControllerRepresentable
|
||||
#elseif canImport(AppKit)
|
||||
typealias _PlatformViewControllerRepresentable = NSViewControllerRepresentable
|
||||
#endif
|
||||
|
||||
protocol PlatformViewControllerRepresentable: _PlatformViewControllerRepresentable {
|
||||
#if canImport(UIKit)
|
||||
typealias ViewController = UIViewControllerType
|
||||
#elseif canImport(AppKit)
|
||||
typealias ViewController = NSViewControllerType
|
||||
#endif
|
||||
|
||||
func makePlatformViewController(context: Context) -> ViewController
|
||||
func updatePlatformViewController(_ controller: ViewController, context: Context)
|
||||
static func dismantlePlatformViewController(_ controller: ViewController, coordinator: Coordinator)
|
||||
}
|
||||
|
||||
extension PlatformViewControllerRepresentable {
|
||||
#if canImport(UIKit)
|
||||
func makeUIViewController(context: Context) -> ViewController {
|
||||
makePlatformViewController(context: context)
|
||||
}
|
||||
func updateUIViewController(_ controller: ViewController, context: Context) {
|
||||
updatePlatformViewController(controller, context: context)
|
||||
}
|
||||
static func dismantleUIViewController(_ controller: ViewController, coordinator: Coordinator) {
|
||||
dismantlePlatformViewController(controller, coordinator: coordinator)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
func makeNSViewController(context: Context) -> ViewController {
|
||||
makePlatformViewController(context: context)
|
||||
}
|
||||
func updateNSViewController(_ controller: ViewController, context: Context) {
|
||||
updatePlatformViewController(controller, context: context)
|
||||
}
|
||||
static func dismantleNSViewController(_ controller: ViewController, coordinator: Coordinator) {
|
||||
dismantlePlatformViewController(controller, coordinator: coordinator)
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import SwiftUI
|
||||
|
||||
public struct PlatformViewVersions<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
|
||||
let isCurrent: Bool
|
||||
let selector: IntrospectionSelector<PlatformSpecificEntity>?
|
||||
|
||||
private init<Version: PlatformVersion>(
|
||||
_ versions: [PlatformViewVersion<Version, SwiftUIViewType, PlatformSpecificEntity>]
|
||||
) {
|
||||
if let currentVersion = versions.first(where: \.isCurrent) {
|
||||
self.isCurrent = true
|
||||
self.selector = currentVersion.selector
|
||||
} else {
|
||||
self.isCurrent = false
|
||||
self.selector = nil
|
||||
}
|
||||
}
|
||||
|
||||
public static func iOS(_ versions: (iOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
|
||||
Self(versions)
|
||||
}
|
||||
|
||||
public static func tvOS(_ versions: (tvOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
|
||||
Self(versions)
|
||||
}
|
||||
|
||||
public static func macOS(_ versions: (macOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
|
||||
Self(versions)
|
||||
}
|
||||
}
|
||||
|
||||
public typealias iOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
|
||||
PlatformViewVersion<iOSVersion, SwiftUIViewType, PlatformSpecificEntity>
|
||||
public typealias tvOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
|
||||
PlatformViewVersion<tvOSVersion, SwiftUIViewType, PlatformSpecificEntity>
|
||||
public typealias macOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
|
||||
PlatformViewVersion<macOSVersion, SwiftUIViewType, PlatformSpecificEntity>
|
||||
|
||||
public struct PlatformViewVersion<Version: PlatformVersion, SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
|
||||
let isCurrent: Bool
|
||||
let selector: IntrospectionSelector<PlatformSpecificEntity>?
|
||||
}
|
||||
|
||||
extension PlatformViewVersion {
|
||||
@_spi(Internals) public init(for version: Version, selector: IntrospectionSelector<PlatformSpecificEntity>? = nil) {
|
||||
self.init(isCurrent: version.isCurrent, selector: selector)
|
||||
}
|
||||
|
||||
@_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self {
|
||||
let filePath = file.withUTF8Buffer { String(decoding: $0, as: UTF8.self) }
|
||||
let fileName = URL(fileURLWithPath: filePath).lastPathComponent
|
||||
runtimeWarn(
|
||||
"""
|
||||
If you're seeing this, someone forgot to mark \(fileName):\(line) as unavailable.
|
||||
|
||||
This won't have any effect, but it should be disallowed altogether.
|
||||
|
||||
Please report it upstream so we can properly fix it by using the following link:
|
||||
|
||||
https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable
|
||||
"""
|
||||
)
|
||||
return Self(isCurrent: false, selector: nil)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Point-Free, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
@_transparent
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
func runtimeWarn(
|
||||
_ message: @autoclosure () -> String,
|
||||
category: String? = "SwiftUIIntrospect"
|
||||
) {
|
||||
#if DEBUG
|
||||
let message = message()
|
||||
let category = category ?? "Runtime Warning"
|
||||
#if canImport(os)
|
||||
os_log(
|
||||
.fault,
|
||||
dso: dso,
|
||||
log: OSLog(subsystem: "com.apple.runtime-issues", category: category),
|
||||
"%@",
|
||||
message
|
||||
)
|
||||
#else
|
||||
fputs("\(formatter.string(from: Date())) [\(category)] \(message)\n", stderr)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
#if canImport(os)
|
||||
import os
|
||||
|
||||
// NB: Xcode runtime warnings offer a much better experience than traditional assertions and
|
||||
// breakpoints, but Apple provides no means of creating custom runtime warnings ourselves.
|
||||
// To work around this, we hook into SwiftUI's runtime issue delivery mechanism, instead.
|
||||
//
|
||||
// Feedback filed: https://gist.github.com/stephencelis/a8d06383ed6ccde3e5ef5d1b3ad52bbc
|
||||
@usableFromInline
|
||||
let dso = { () -> UnsafeMutableRawPointer in
|
||||
let count = _dyld_image_count()
|
||||
for i in 0..<count {
|
||||
if let name = _dyld_get_image_name(i) {
|
||||
let swiftString = String(cString: name)
|
||||
if swiftString.hasSuffix("/SwiftUI") {
|
||||
if let header = _dyld_get_image_header(i) {
|
||||
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer(header))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return UnsafeMutableRawPointer(mutating: #dsohandle)
|
||||
}()
|
||||
#else
|
||||
import Foundation
|
||||
|
||||
@usableFromInline
|
||||
let formatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd HH:MM:SS.sssZ"
|
||||
return formatter
|
||||
}()
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
postfix operator ~
|
||||
|
||||
postfix func ~ <LHS, T>(lhs: LHS) -> T {
|
||||
lhs as! T
|
||||
}
|
||||
|
||||
postfix func ~ <LHS, T>(lhs: LHS?) -> T? {
|
||||
lhs as? T
|
||||
}
|
||||
|
||||
func recursiveSequence<S: Sequence>(_ sequence: S, children: @escaping (S.Element) -> S) -> AnySequence<S.Element> {
|
||||
AnySequence {
|
||||
var mainIterator = sequence.makeIterator()
|
||||
// Current iterator, or `nil` if all sequences are exhausted:
|
||||
var iterator: AnyIterator<S.Element>?
|
||||
|
||||
return AnyIterator {
|
||||
guard let iterator, let element = iterator.next() else {
|
||||
if let element = mainIterator.next() {
|
||||
iterator = recursiveSequence(children(element), children: children).makeIterator()
|
||||
return element
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return element
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Button
|
||||
|
||||
public struct ButtonType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == ButtonType {
|
||||
public static var button: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<ButtonType, NSButton> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.ColorPicker
|
||||
|
||||
public struct ColorPickerType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == ColorPickerType {
|
||||
public static var colorPicker: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
@available(iOS 14, *)
|
||||
extension iOSViewVersion<ColorPickerType, UIColorWell> {
|
||||
@available(*, unavailable, message: "ColorPicker isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
@available(macOS 11, *)
|
||||
extension macOSViewVersion<ColorPickerType, NSColorWell> {
|
||||
@available(*, unavailable, message: "ColorPicker isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.DatePicker
|
||||
|
||||
public struct DatePickerType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == DatePickerType {
|
||||
public static var datePicker: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<DatePickerType, UIDatePicker> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<DatePickerType, NSDatePicker> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.compact)
|
||||
|
||||
public struct DatePickerWithCompactStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case compact
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == DatePickerWithCompactStyleType {
|
||||
public static func datePicker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<DatePickerWithCompactStyleType, UIDatePicker> {
|
||||
@available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on iOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<DatePickerWithCompactStyleType, NSDatePicker> {
|
||||
@available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v10_15_4 = Self(for: .v10_15_4)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.field)
|
||||
|
||||
public struct DatePickerWithFieldStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case field
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == DatePickerWithFieldStyleType {
|
||||
public static func datePicker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<DatePickerWithFieldStyleType, NSDatePicker> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.graphical)
|
||||
|
||||
public struct DatePickerWithGraphicalStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case graphical
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == DatePickerWithGraphicalStyleType {
|
||||
public static func datePicker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<DatePickerWithGraphicalStyleType, UIDatePicker> {
|
||||
@available(*, unavailable, message: ".datePickerStyle(.graphical) isn't available on iOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<DatePickerWithGraphicalStyleType, NSDatePicker> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.stepperField)
|
||||
|
||||
public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case stepperField
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType {
|
||||
public static func datePicker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<DatePickerWithStepperFieldStyleType, NSDatePicker> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#if os(iOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.wheel)
|
||||
|
||||
public struct DatePickerWithWheelStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case wheel
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == DatePickerWithWheelStyleType {
|
||||
public static func datePicker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<DatePickerWithWheelStyleType, UIDatePicker> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
#if !os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Form
|
||||
|
||||
public struct FormType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == FormType {
|
||||
public static var form: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<FormType, UITableView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<FormType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<FormType, UITableView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Form { ... }.formStyle(.grouped)
|
||||
|
||||
public struct FormWithGroupedStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case grouped
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == FormWithGroupedStyleType {
|
||||
public static func form(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<FormWithGroupedStyleType, UITableView> {
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
}
|
||||
|
||||
extension iOSViewVersion<FormWithGroupedStyleType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<FormWithGroupedStyleType, UITableView> {
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<FormWithGroupedStyleType, NSScrollView> {
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 11")
|
||||
public static let v11 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 12")
|
||||
public static let v12 = Self.unavailable()
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List
|
||||
|
||||
public struct ListType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case plain
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListType {
|
||||
public static var list: Self { .init() }
|
||||
public static func list(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ListType, UITableView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<ListType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ListType, UITableView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ListType, NSTableView> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List { Cell() }
|
||||
|
||||
public struct ListCellType: IntrospectableViewType {
|
||||
public var scope: IntrospectionScope { .ancestor }
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListCellType {
|
||||
public static var listCell: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ListCellType, UITableViewCell> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<ListCellType, UICollectionViewCell> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ListCellType, UITableViewCell> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ListCellType, NSTableCellView> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List { ... }.listStyle(.bordered)
|
||||
|
||||
public struct ListWithBorderedStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case bordered
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListWithBorderedStyleType {
|
||||
public static func list(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<ListWithBorderedStyleType, NSTableView> {
|
||||
@available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 11")
|
||||
public static let v11 = Self.unavailable()
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
#if os(iOS) || os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List { ... }.listStyle(.grouped)
|
||||
|
||||
public struct ListWithGroupedStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case grouped
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListWithGroupedStyleType {
|
||||
public static func list(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ListWithGroupedStyleType, UITableView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<ListWithGroupedStyleType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ListWithGroupedStyleType, UITableView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#if os(iOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List { ... }.listStyle(.insetGrouped)
|
||||
|
||||
public struct ListWithInsetGroupedStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case insetGrouped
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType {
|
||||
public static func list(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ListWithInsetGroupedStyleType, UITableView> {
|
||||
@available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on iOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<ListWithInsetGroupedStyleType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List { ... }.listStyle(.inset)
|
||||
|
||||
public struct ListWithInsetStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case inset
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListWithInsetStyleType {
|
||||
public static func list(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ListWithInsetStyleType, UITableView> {
|
||||
@available(*, unavailable, message: ".listStyle(.inset) isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<ListWithInsetStyleType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ListWithInsetStyleType, NSTableView> {
|
||||
@available(*, unavailable, message: ".listStyle(.inset) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.List { ... }.listStyle(.sidebar)
|
||||
|
||||
public struct ListWithSidebarStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case sidebar
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ListWithSidebarStyleType {
|
||||
public static func list(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ListWithSidebarStyleType, UITableView> {
|
||||
@available(*, unavailable, message: ".listStyle(.sidebar) isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
}
|
||||
|
||||
extension iOSViewVersion<ListWithSidebarStyleType, UICollectionView> {
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ListWithSidebarStyleType, NSTableView> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.NavigationSplitView
|
||||
|
||||
public struct NavigationSplitViewType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == NavigationSplitViewType {
|
||||
public static var navigationSplitView: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<NavigationSplitViewType, UISplitViewController> {
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on iOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on iOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UISplitViewController> {
|
||||
.default.withAncestorSelector(\.splitViewController)
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<NavigationSplitViewType, UINavigationController> {
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UINavigationController> {
|
||||
.default.withAncestorSelector(\.navigationController)
|
||||
}
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<NavigationSplitViewType, NSSplitView> {
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on macOS 11")
|
||||
public static let v11 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationSplitView isn't available on macOS 12")
|
||||
public static let v12 = Self.unavailable()
|
||||
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.NavigationStack
|
||||
|
||||
public struct NavigationStackType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == NavigationStackType {
|
||||
public static var navigationStack: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<NavigationStackType, UINavigationController> {
|
||||
@available(*, unavailable, message: "NavigationStack isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationStack isn't available on iOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationStack isn't available on iOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UINavigationController> {
|
||||
.default.withAncestorSelector(\.navigationController)
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<NavigationStackType, UINavigationController> {
|
||||
@available(*, unavailable, message: "NavigationStack isn't available on tvOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationStack isn't available on tvOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: "NavigationStack isn't available on tvOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UINavigationController> {
|
||||
.default.withAncestorSelector(\.navigationController)
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.columns)
|
||||
|
||||
public struct NavigationViewWithColumnsStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case columns
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == NavigationViewWithColumnsStyleType {
|
||||
public static func navigationView(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<NavigationViewWithColumnsStyleType, UISplitViewController> {
|
||||
public static let v13 = Self(for: .v13, selector: selector)
|
||||
public static let v14 = Self(for: .v14, selector: selector)
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UISplitViewController> {
|
||||
.default.withAncestorSelector(\.splitViewController)
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<NavigationViewWithColumnsStyleType, UINavigationController> {
|
||||
public static let v13 = Self(for: .v13, selector: selector)
|
||||
public static let v14 = Self(for: .v14, selector: selector)
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UINavigationController> {
|
||||
.default.withAncestorSelector(\.navigationController)
|
||||
}
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<NavigationViewWithColumnsStyleType, NSSplitView> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.stack)
|
||||
|
||||
public struct NavigationViewWithStackStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case stack
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == NavigationViewWithStackStyleType {
|
||||
public static func navigationView(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<NavigationViewWithStackStyleType, UINavigationController> {
|
||||
public static let v13 = Self(for: .v13, selector: selector)
|
||||
public static let v14 = Self(for: .v14, selector: selector)
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UINavigationController> {
|
||||
.default.withAncestorSelector(\.navigationController)
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<NavigationViewWithStackStyleType, UINavigationController> {
|
||||
public static let v13 = Self(for: .v13, selector: selector)
|
||||
public static let v14 = Self(for: .v14, selector: selector)
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UINavigationController> {
|
||||
.default.withAncestorSelector(\.navigationController)
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,26 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Picker { ... }.pickerStyle(.menu)
|
||||
|
||||
public struct PickerWithMenuStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case menu
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == PickerWithMenuStyleType {
|
||||
public static func picker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<PickerWithMenuStyleType, NSPopUpButton> {
|
||||
@available(*, unavailable, message: ".pickerStyle(.menu) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Picker { ... }.pickerStyle(.segmented)
|
||||
|
||||
public struct PickerWithSegmentedStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case segmented
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == PickerWithSegmentedStyleType {
|
||||
public static func picker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<PickerWithSegmentedStyleType, UISegmentedControl> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<PickerWithSegmentedStyleType, UISegmentedControl> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<PickerWithSegmentedStyleType, NSSegmentedControl> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#if os(iOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Picker { ... }.pickerStyle(.wheel)
|
||||
|
||||
public struct PickerWithWheelStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case wheel
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == PickerWithWheelStyleType {
|
||||
public static func picker(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<PickerWithWheelStyleType, UIPickerView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,42 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.ProgressView().progressViewStyle(.circular)
|
||||
|
||||
public struct ProgressViewWithCircularStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case circular
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ProgressViewWithCircularStyleType {
|
||||
public static func progressView(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ProgressViewWithCircularStyleType, UIActivityIndicatorView> {
|
||||
@available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on iOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ProgressViewWithCircularStyleType, UIActivityIndicatorView> {
|
||||
@available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on tvOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ProgressViewWithCircularStyleType, NSProgressIndicator> {
|
||||
@available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,42 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.ProgressView().progressViewStyle(.linear)
|
||||
|
||||
public struct ProgressViewWithLinearStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case linear
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ProgressViewWithLinearStyleType {
|
||||
public static func progressView(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ProgressViewWithLinearStyleType, UIProgressView> {
|
||||
@available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on iOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ProgressViewWithLinearStyleType, UIProgressView> {
|
||||
@available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on tvOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ProgressViewWithLinearStyleType, NSProgressIndicator> {
|
||||
@available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.ScrollView
|
||||
|
||||
public struct ScrollViewType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == ScrollViewType {
|
||||
public static var scrollView: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ScrollViewType, UIScrollView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ScrollViewType, UIScrollView> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ScrollViewType, NSScrollView> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.View.searchable(...)
|
||||
|
||||
public struct SearchFieldType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == SearchFieldType {
|
||||
public static var searchField: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<SearchFieldType, UISearchBar> {
|
||||
@available(*, unavailable, message: ".searchable isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".searchable isn't available on iOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UISearchBar> {
|
||||
.from(UINavigationController.self) {
|
||||
$0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<SearchFieldType, UISearchBar> {
|
||||
@available(*, unavailable, message: ".searchable isn't available on tvOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".searchable isn't available on tvOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UISearchBar> {
|
||||
.from(UINavigationController.self) {
|
||||
$0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Slider
|
||||
|
||||
public struct SliderType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == SliderType {
|
||||
public static var slider: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<SliderType, UISlider> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<SliderType, NSSlider> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Stepper
|
||||
|
||||
public struct StepperType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == StepperType {
|
||||
public static var stepper: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<StepperType, UIStepper> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<StepperType, NSStepper> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.TabView
|
||||
|
||||
public struct TabViewType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == TabViewType {
|
||||
public static var tabView: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<TabViewType, UITabBarController> {
|
||||
public static let v13 = Self(for: .v13, selector: selector)
|
||||
public static let v14 = Self(for: .v14, selector: selector)
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UITabBarController> {
|
||||
.default.withAncestorSelector(\.tabBarController)
|
||||
}
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<TabViewType, UITabBarController> {
|
||||
public static let v13 = Self(for: .v13, selector: selector)
|
||||
public static let v14 = Self(for: .v14, selector: selector)
|
||||
public static let v15 = Self(for: .v15, selector: selector)
|
||||
public static let v16 = Self(for: .v16, selector: selector)
|
||||
public static let v17 = Self(for: .v17, selector: selector)
|
||||
|
||||
private static var selector: IntrospectionSelector<UITabBarController> {
|
||||
.default.withAncestorSelector(\.tabBarController)
|
||||
}
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<TabViewType, NSTabView> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
#if !os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.TabView { ... }.tabViewStyle(.page)
|
||||
|
||||
public struct TabViewWithPageStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case page
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == TabViewWithPageStyleType {
|
||||
public static func tabView(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<TabViewWithPageStyleType, UICollectionView> {
|
||||
@available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<TabViewWithPageStyleType, UICollectionView> {
|
||||
@available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on tvOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Table
|
||||
|
||||
public struct TableType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == TableType {
|
||||
public static var table: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<TableType, UICollectionView> {
|
||||
@available(*, unavailable, message: "Table isn't available on iOS 13")
|
||||
public static let v13 = Self(for: .v13)
|
||||
@available(*, unavailable, message: "Table isn't available on iOS 14")
|
||||
public static let v14 = Self(for: .v14)
|
||||
@available(*, unavailable, message: "Table isn't available on iOS 15")
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<TableType, NSTableView> {
|
||||
@available(*, unavailable, message: "Table isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
@available(*, unavailable, message: "Table isn't available on macOS 11")
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.TextEditor
|
||||
|
||||
public struct TextEditorType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == TextEditorType {
|
||||
public static var textEditor: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<TextEditorType, UITextView> {
|
||||
@available(*, unavailable, message: "TextEditor isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<TextEditorType, NSTextView> {
|
||||
@available(*, unavailable, message: "TextEditor isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.TextField
|
||||
|
||||
public struct TextFieldType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == TextFieldType {
|
||||
public static var textField: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<TextFieldType, UITextField> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<TextFieldType, UITextField> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<TextFieldType, NSTextField> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,53 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.TextField(..., axis: .vertical)
|
||||
|
||||
public struct TextFieldWithVerticalAxisType: IntrospectableViewType {
|
||||
public enum Axis {
|
||||
case vertical
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == TextFieldWithVerticalAxisType {
|
||||
public static func textField(axis: Self.Axis) -> Self { .init() }
|
||||
}
|
||||
|
||||
// MARK: SwiftUI.TextField(..., axis: .vertical) - iOS
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<TextFieldWithVerticalAxisType, UITextView> {
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<TextFieldWithVerticalAxisType, UITextField> {
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 13")
|
||||
public static let v13 = Self.unavailable()
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 14")
|
||||
public static let v14 = Self.unavailable()
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 15")
|
||||
public static let v15 = Self.unavailable()
|
||||
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<TextFieldWithVerticalAxisType, NSTextField> {
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 11")
|
||||
public static let v11 = Self.unavailable()
|
||||
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 12")
|
||||
public static let v12 = Self.unavailable()
|
||||
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Toggle
|
||||
|
||||
public struct ToggleType: IntrospectableViewType {}
|
||||
|
||||
extension IntrospectableViewType where Self == ToggleType {
|
||||
public static var toggle: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ToggleType, UISwitch> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ToggleType, NSButton> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Toggle(...).toggleStyle(.button)
|
||||
|
||||
public struct ToggleWithButtonStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case button
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ToggleWithButtonStyleType {
|
||||
public static func toggle(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<ToggleWithButtonStyleType, NSButton> {
|
||||
@available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 10.15")
|
||||
public static let v10_15 = Self.unavailable()
|
||||
@available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 11")
|
||||
public static let v11 = Self.unavailable()
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Toggle(...).toggleStyle(.checkbox)
|
||||
|
||||
public struct ToggleWithCheckboxStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case checkbox
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType {
|
||||
public static func toggle(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
extension macOSViewVersion<ToggleWithCheckboxStyleType, NSButton> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.Toggle(...).toggleStyle(.switch)
|
||||
|
||||
public struct ToggleWithSwitchStyleType: IntrospectableViewType {
|
||||
public enum Style {
|
||||
case `switch`
|
||||
}
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ToggleWithSwitchStyleType {
|
||||
public static func toggle(style: Self.Style) -> Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ToggleWithSwitchStyleType, UISwitch> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
extension macOSViewVersion<ToggleWithSwitchStyleType, NSSwitch> {
|
||||
public static let v10_15 = Self(for: .v10_15)
|
||||
public static let v11 = Self(for: .v11)
|
||||
public static let v12 = Self(for: .v12)
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: SwiftUI.View
|
||||
|
||||
public struct ViewType: IntrospectableViewType {
|
||||
public var scope: IntrospectionScope { [.receiver, .ancestor] }
|
||||
}
|
||||
|
||||
extension IntrospectableViewType where Self == ViewType {
|
||||
public static var view: Self { .init() }
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
extension iOSViewVersion<ViewType, UIViewController> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
|
||||
extension tvOSViewVersion<ViewType, UIViewController> {
|
||||
public static let v13 = Self(for: .v13)
|
||||
public static let v14 = Self(for: .v14)
|
||||
public static let v15 = Self(for: .v15)
|
||||
public static let v16 = Self(for: .v16)
|
||||
public static let v17 = Self(for: .v17)
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
Pod::Spec.new do |spec|
|
||||
spec.name = 'SwiftUIIntrospect'
|
||||
spec.version = ENV['LIB_VERSION']
|
||||
spec.license = { type: 'MIT' }
|
||||
spec.homepage = 'https://github.com/siteline/swiftui-introspect'
|
||||
spec.author = 'David Roman'
|
||||
spec.summary = 'Introspect underlying UIKit/AppKit components from SwiftUI.'
|
||||
spec.source = {
|
||||
git: 'https://github.com/siteline/swiftui-introspect.git',
|
||||
tag: spec.version
|
||||
}
|
||||
|
||||
spec.source_files = 'Sources/**/*.swift'
|
||||
|
||||
spec.swift_version = '5.7'
|
||||
spec.ios.deployment_target = '13.0'
|
||||
spec.tvos.deployment_target = '13.0'
|
||||
spec.osx.deployment_target = '10.15'
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
import SwiftUI
|
||||
|
||||
@main
|
||||
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window?.rootViewController = UIHostingController(rootView: EmptyView())
|
||||
window?.makeKeyAndVisible()
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// swift-tools-version:5.5
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Tests",
|
||||
products: [],
|
||||
targets: []
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1420"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D50E2F592A2B9F6600BAFB03"
|
||||
BuildableName = "LegacyTests.xctest"
|
||||
BlueprintName = "LegacyTests"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D50E2F4D2A2B9DEE00BAFB03"
|
||||
BuildableName = "LegacyTestsHostApp.app"
|
||||
BlueprintName = "LegacyTestsHostApp"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D50E2F4D2A2B9DEE00BAFB03"
|
||||
BuildableName = "LegacyTestsHostApp.app"
|
||||
BlueprintName = "LegacyTestsHostApp"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1420"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D5F0BE4829C0DBE800AD95AB"
|
||||
BuildableName = "TestsHostApp.app"
|
||||
BlueprintName = "TestsHostApp"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D5F0BE5C29C0DC0000AD95AB"
|
||||
BuildableName = "Tests.xctest"
|
||||
BlueprintName = "Tests"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D5F0BE4829C0DBE800AD95AB"
|
||||
BuildableName = "TestsHostApp.app"
|
||||
BlueprintName = "TestsHostApp"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D5F0BE4829C0DBE800AD95AB"
|
||||
BuildableName = "TestsHostApp.app"
|
||||
BlueprintName = "TestsHostApp"
|
||||
ReferencedContainer = "container:Tests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,130 @@
|
|||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class PlatformTests: XCTestCase {
|
||||
func test_iOS() {
|
||||
#if os(iOS)
|
||||
if #available(iOS 17, *) {
|
||||
XCTAssertEqual(iOSVersion.v17.isCurrent, true)
|
||||
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
|
||||
} else if #available(iOS 16, *) {
|
||||
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v16.isCurrent, true)
|
||||
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
|
||||
} else if #available(iOS 15, *) {
|
||||
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v15.isCurrent, true)
|
||||
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
|
||||
} else if #available(iOS 14, *) {
|
||||
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v14.isCurrent, true)
|
||||
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
|
||||
} else if #available(iOS 13, *) {
|
||||
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v13.isCurrent, true)
|
||||
}
|
||||
#else
|
||||
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
|
||||
#endif
|
||||
}
|
||||
|
||||
func test_macOS() {
|
||||
#if os(macOS)
|
||||
if #available(macOS 14, *) {
|
||||
XCTAssertEqual(macOSVersion.v14.isCurrent, true)
|
||||
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
|
||||
} else if #available(macOS 13, *) {
|
||||
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v13.isCurrent, true)
|
||||
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
|
||||
} else if #available(macOS 12, *) {
|
||||
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v12.isCurrent, true)
|
||||
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
|
||||
} else if #available(macOS 11, *) {
|
||||
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v11.isCurrent, true)
|
||||
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
|
||||
} else if #available(macOS 10.15, *) {
|
||||
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v10_15.isCurrent, true)
|
||||
}
|
||||
#else
|
||||
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
|
||||
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
|
||||
#endif
|
||||
}
|
||||
|
||||
func test_tvOS() {
|
||||
#if os(tvOS)
|
||||
if #available(tvOS 17, *) {
|
||||
XCTAssertEqual(tvOSVersion.v17.isCurrent, true)
|
||||
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
|
||||
} else if #available(tvOS 16, *) {
|
||||
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v16.isCurrent, true)
|
||||
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
|
||||
} else if #available(tvOS 15, *) {
|
||||
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v15.isCurrent, true)
|
||||
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
|
||||
} else if #available(tvOS 14, *) {
|
||||
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v14.isCurrent, true)
|
||||
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
|
||||
} else if #available(tvOS 13, *) {
|
||||
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v13.isCurrent, true)
|
||||
}
|
||||
#else
|
||||
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
|
||||
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import SwiftUI
|
||||
import XCTest
|
||||
|
||||
#if canImport(UIKit)
|
||||
enum TestUtils {
|
||||
private static let window = UIWindow(frame: UIScreen.main.bounds)
|
||||
|
||||
static func present(view: some View) {
|
||||
window.rootViewController = UIHostingController(rootView: view)
|
||||
window.makeKeyAndVisible()
|
||||
window.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
#elseif canImport(AppKit)
|
||||
enum TestUtils {
|
||||
private static let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
|
||||
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||
backing: .buffered,
|
||||
defer: true
|
||||
)
|
||||
|
||||
static func present(view: some View) {
|
||||
window.contentView = NSHostingView(rootView: view)
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
func XCTAssertViewIntrospection<V: View, PV: AnyObject>(
|
||||
of type: PV.Type,
|
||||
@ViewBuilder view: (Spies<PV>) -> V,
|
||||
extraAssertions: ([PV]) -> Void = { _ in },
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) {
|
||||
let spies = Spies<PV>()
|
||||
let view = view(spies)
|
||||
TestUtils.present(view: view)
|
||||
XCTWaiter(delegate: spies).wait(for: spies.expectations.values.map(\.0), timeout: 3)
|
||||
extraAssertions(spies.objects.sorted(by: { $0.key < $1.key }).map(\.value))
|
||||
}
|
||||
|
||||
final class Spies<PV: AnyObject>: NSObject, XCTWaiterDelegate {
|
||||
private(set) var objects: [Int: PV] = [:]
|
||||
private(set) var expectations: [ObjectIdentifier: (XCTestExpectation, StaticString, UInt)] = [:]
|
||||
|
||||
subscript(
|
||||
number: Int,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) -> (PV) -> Void {
|
||||
let expectation = XCTestExpectation()
|
||||
expectations[ObjectIdentifier(expectation)] = (expectation, file, line)
|
||||
return { [self] in
|
||||
if let object = objects[number] {
|
||||
XCTAssert(object === $0, "Found view was overriden by another view", file: file, line: line)
|
||||
}
|
||||
objects[number] = $0
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
func waiter(
|
||||
_ waiter: XCTWaiter,
|
||||
didTimeoutWithUnfulfilledExpectations unfulfilledExpectations: [XCTestExpectation]
|
||||
) {
|
||||
for expectation in unfulfilledExpectations {
|
||||
let (_, file, line) = expectations[ObjectIdentifier(expectation)]!
|
||||
XCTFail("Spy not called", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func nestedWaiter(
|
||||
_ waiter: XCTWaiter,
|
||||
wasInterruptedByTimedOutWaiter outerWaiter: XCTWaiter
|
||||
) {
|
||||
XCTFail("wasInterruptedByTimedOutWaiter")
|
||||
}
|
||||
|
||||
func waiter(
|
||||
_ waiter: XCTWaiter,
|
||||
fulfillmentDidViolateOrderingConstraintsFor expectation: XCTestExpectation,
|
||||
requiredExpectation: XCTestExpectation
|
||||
) {
|
||||
XCTFail("fulfillmentDidViolateOrderingConstraintsFor")
|
||||
}
|
||||
|
||||
func waiter(
|
||||
_ waiter: XCTWaiter,
|
||||
didFulfillInvertedExpectation expectation: XCTestExpectation
|
||||
) {
|
||||
XCTFail("didFulfillInvertedExpectation")
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection {
|
||||
subscript(safe index: Index, file: StaticString = #file, line: UInt = #line) -> Element? {
|
||||
get {
|
||||
guard indices.contains(index) else {
|
||||
XCTFail("Index \(index) is out of bounds", file: file, line: line)
|
||||
return nil
|
||||
}
|
||||
return self[index]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class ButtonTests: XCTestCase {
|
||||
#if canImport(AppKit)
|
||||
typealias PlatformButton = NSButton
|
||||
#endif
|
||||
|
||||
func testButton() {
|
||||
XCTAssertViewIntrospection(of: PlatformButton.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
let spy3 = spies[3]
|
||||
|
||||
VStack {
|
||||
Button("Button 0", action: {})
|
||||
.buttonStyle(.bordered)
|
||||
#if os(macOS)
|
||||
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
|
||||
Button("Button 1", action: {})
|
||||
.buttonStyle(.borderless)
|
||||
#if os(macOS)
|
||||
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
|
||||
Button("Button 2", action: {})
|
||||
.buttonStyle(.link)
|
||||
#if os(macOS)
|
||||
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
|
||||
Button("Button 3", action: {})
|
||||
#if os(macOS)
|
||||
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy3)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(AppKit)
|
||||
XCTAssert(Set($0.map(ObjectIdentifier.init)).count == 4)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 14, macOS 11, *)
|
||||
final class ColorPickerTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformColor = UIColor
|
||||
typealias PlatformColorPicker = UIColorWell
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformColor = NSColor
|
||||
typealias PlatformColorPicker = NSColorWell
|
||||
#endif
|
||||
|
||||
func testColorPicker() throws {
|
||||
guard #available(iOS 14, macOS 11, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformColorPicker.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
ColorPicker("", selection: .constant(PlatformColor.red.cgColor))
|
||||
#if os(iOS)
|
||||
.introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy0)
|
||||
#elseif os(macOS)
|
||||
.introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
|
||||
ColorPicker("", selection: .constant(PlatformColor.green.cgColor))
|
||||
#if os(iOS)
|
||||
.introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy1)
|
||||
#elseif os(macOS)
|
||||
.introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
|
||||
ColorPicker("", selection: .constant(PlatformColor.blue.cgColor))
|
||||
#if os(iOS)
|
||||
.introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy2)
|
||||
#elseif os(macOS)
|
||||
.introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(UIKit)
|
||||
XCTAssertEqual($0[safe: 0]?.selectedColor, .red)
|
||||
XCTAssertEqual($0[safe: 1]?.selectedColor, .green)
|
||||
XCTAssertEqual($0[safe: 2]?.selectedColor, .blue)
|
||||
#elseif canImport(AppKit)
|
||||
XCTAssertEqual($0[safe: 0]?.color, .red)
|
||||
XCTAssertEqual($0[safe: 1]?.color, .green)
|
||||
XCTAssertEqual($0[safe: 2]?.color, .blue)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class DatePickerTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformDatePicker = UIDatePicker
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformDatePicker = NSDatePicker
|
||||
#endif
|
||||
|
||||
func testDatePicker() {
|
||||
let date0 = Date(timeIntervalSince1970: 0)
|
||||
let date1 = Date(timeIntervalSince1970: 5)
|
||||
let date2 = Date(timeIntervalSince1970: 10)
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformDatePicker.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
DatePicker("", selection: .constant(date0))
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date1))
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date2))
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(UIKit)
|
||||
XCTAssertEqual($0[safe: 0]?.date, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.date, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.date, date2)
|
||||
#elseif canImport(AppKit)
|
||||
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 14, macOS 10.15.4, *)
|
||||
final class DatePickerWithCompactStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformDatePickerWithCompactStyle = UIDatePicker
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformDatePickerWithCompactStyle = NSDatePicker
|
||||
#endif
|
||||
|
||||
func testDatePickerWithCompactStyle() throws {
|
||||
guard #available(iOS 14, macOS 10.15.4, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
let date0 = Date(timeIntervalSince1970: 0)
|
||||
let date1 = Date(timeIntervalSince1970: 5)
|
||||
let date2 = Date(timeIntervalSince1970: 10)
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformDatePickerWithCompactStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
DatePicker("", selection: .constant(date0))
|
||||
.datePickerStyle(.compact)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date1))
|
||||
.datePickerStyle(.compact)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date2))
|
||||
.datePickerStyle(.compact)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(UIKit)
|
||||
XCTAssertEqual($0[safe: 0]?.date, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.date, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.date, date2)
|
||||
#elseif canImport(AppKit)
|
||||
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class DatePickerWithFieldStyleTests: XCTestCase {
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
typealias PlatformDatePickerWithFieldStyle = NSDatePicker
|
||||
#endif
|
||||
|
||||
func testDatePickerWithFieldStyle() {
|
||||
let date0 = Date(timeIntervalSince1970: 0)
|
||||
let date1 = Date(timeIntervalSince1970: 5)
|
||||
let date2 = Date(timeIntervalSince1970: 10)
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformDatePickerWithFieldStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
DatePicker("", selection: .constant(date0))
|
||||
.datePickerStyle(.field)
|
||||
#if os(macOS)
|
||||
.introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date1))
|
||||
.datePickerStyle(.field)
|
||||
#if os(macOS)
|
||||
.introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date2))
|
||||
.datePickerStyle(.field)
|
||||
#if os(macOS)
|
||||
.introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
#if os(iOS) || os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 14, *)
|
||||
final class DatePickerWithGraphicalStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformDatePickerWithGraphicalStyle = UIDatePicker
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformDatePickerWithGraphicalStyle = NSDatePicker
|
||||
#endif
|
||||
|
||||
func testDatePickerWithGraphicalStyle() throws {
|
||||
guard #available(iOS 14, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
let date0 = Date(timeIntervalSince1970: 0)
|
||||
let date1 = Date(timeIntervalSince1970: 3600 * 24 * 1)
|
||||
let date2 = Date(timeIntervalSince1970: 3600 * 24 * 2)
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformDatePickerWithGraphicalStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
DatePicker("", selection: .constant(date0))
|
||||
.datePickerStyle(.graphical)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date1))
|
||||
.datePickerStyle(.graphical)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date2))
|
||||
.datePickerStyle(.graphical)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2)
|
||||
#elseif os(macOS)
|
||||
.introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(UIKit)
|
||||
XCTAssertEqual($0[safe: 0]?.date, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.date, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.date, date2)
|
||||
#elseif canImport(AppKit)
|
||||
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class DatePickerWithWheelStyleTests: XCTestCase {
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
typealias PlatformDatePickerWithWheelStyle = NSDatePicker
|
||||
#endif
|
||||
|
||||
func testDatePickerWithWheelStyle() {
|
||||
let date0 = Date(timeIntervalSince1970: 0)
|
||||
let date1 = Date(timeIntervalSince1970: 5)
|
||||
let date2 = Date(timeIntervalSince1970: 10)
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
DatePicker("", selection: .constant(date0))
|
||||
.datePickerStyle(.stepperField)
|
||||
#if os(macOS)
|
||||
.introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date1))
|
||||
.datePickerStyle(.stepperField)
|
||||
#if os(macOS)
|
||||
.introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date2))
|
||||
.datePickerStyle(.stepperField)
|
||||
#if os(macOS)
|
||||
.introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
|
||||
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#if os(iOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class DatePickerWithWheelStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformDatePickerWithWheelStyle = UIDatePicker
|
||||
#endif
|
||||
|
||||
func testDatePickerWithWheelStyle() {
|
||||
let date0 = Date(timeIntervalSince1970: 0)
|
||||
let date1 = Date(timeIntervalSince1970: 5)
|
||||
let date2 = Date(timeIntervalSince1970: 10)
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
let spy2 = spies[2]
|
||||
|
||||
VStack {
|
||||
DatePicker("", selection: .constant(date0))
|
||||
.datePickerStyle(.wheel)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date1))
|
||||
.datePickerStyle(.wheel)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1)
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
|
||||
DatePicker("", selection: .constant(date2))
|
||||
.datePickerStyle(.wheel)
|
||||
#if os(iOS)
|
||||
.introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2)
|
||||
#endif
|
||||
}
|
||||
} extraAssertions: {
|
||||
#if canImport(UIKit)
|
||||
XCTAssertEqual($0[safe: 0]?.date, date0)
|
||||
XCTAssertEqual($0[safe: 1]?.date, date1)
|
||||
XCTAssertEqual($0[safe: 2]?.date, date2)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#if !os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class FormTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformForm = UIScrollView // covers both UITableView and UICollectionView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformForm = NSScrollView
|
||||
#endif
|
||||
|
||||
func testForm() throws {
|
||||
XCTAssertViewIntrospection(of: PlatformForm.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
Form {
|
||||
Text("Item 1")
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
|
||||
.introspect(.form, on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
Form {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.form, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,49 @@
|
|||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 16, tvOS 16, macOS 13, *)
|
||||
final class FormWithGroupedStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformFormWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformFormWithGroupedStyle = NSScrollView
|
||||
#endif
|
||||
|
||||
func testFormWithGroupedStyle() throws {
|
||||
guard #available(iOS 16, tvOS 16, macOS 13, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformFormWithGroupedStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
Form {
|
||||
Text("Item 1")
|
||||
}
|
||||
.formStyle(.grouped)
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
.introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { spy0($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
Form {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.form(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.form(style: .grouped), on: .tvOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.form(style: .grouped), on: .macOS(.v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.formStyle(.grouped)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class ListCellTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListCell = UIView // covers both UITableViewCell and UICollectionViewCell
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformListCell = NSTableCellView
|
||||
#endif
|
||||
|
||||
func testListCell() {
|
||||
XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in
|
||||
let spy = spies[0]
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) }
|
||||
.introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testMaskedListCell() {
|
||||
XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in
|
||||
let spy = spies[0]
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) }
|
||||
.introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) }
|
||||
#endif
|
||||
.clipped()
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20.0))
|
||||
.cornerRadius(2.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class ListTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformList = UIScrollView // covers both UITableView and UICollectionView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformList = NSTableView
|
||||
#endif
|
||||
|
||||
func testList() {
|
||||
XCTAssertViewIntrospection(of: PlatformList.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(macOS)
|
||||
func testNestedList() {
|
||||
XCTAssertViewIntrospection(of: PlatformList.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy1($0) }
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#endif
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
func testMaskedList() {
|
||||
XCTAssertViewIntrospection(of: PlatformList.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
.clipped()
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20.0))
|
||||
.cornerRadius(2.0)
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#if os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(macOS 12, *)
|
||||
final class ListWithBorderedStyleTests: XCTestCase {
|
||||
#if canImport(AppKit)
|
||||
typealias PlatformListWithBorderedStyle = NSTableView
|
||||
#endif
|
||||
|
||||
func testListWithBorderedStyle() throws {
|
||||
guard #available(macOS 12, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformListWithBorderedStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.bordered)
|
||||
#if os(macOS)
|
||||
.introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(macOS)
|
||||
.introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.bordered)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#if !os(macOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class ListWithGroupedStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
|
||||
#endif
|
||||
|
||||
func testListWithGroupedStyle() {
|
||||
XCTAssertViewIntrospection(of: PlatformListWithGroupedStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
|
||||
.introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#if os(iOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 14, *)
|
||||
final class ListWithInsetGroupedStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithInsetGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
|
||||
#endif
|
||||
|
||||
func testListWithInsetGroupedStyle() throws {
|
||||
guard #available(iOS 14, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformListWithInsetGroupedStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { spy0($0) }
|
||||
.introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 14, macOS 11, *)
|
||||
final class ListWithInsetStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithInsetStyle = UIScrollView // covers both UITableView and UICollectionView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformListWithInsetStyle = NSTableView
|
||||
#endif
|
||||
|
||||
func testListWithInsetStyle() throws {
|
||||
guard #available(iOS 14, macOS 11, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformListWithInsetStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.inset)
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { spy0($0) }
|
||||
.introspect(.list(style: .inset), on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .inset), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list(style: .inset), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.inset)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
final class ListWithPlainStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithPlainStyle = UIScrollView // covers both UITableView and UICollectionView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformListWithPlainStyle = NSTableView
|
||||
#endif
|
||||
|
||||
func testListWithPlainStyle() {
|
||||
XCTAssertViewIntrospection(of: PlatformListWithPlainStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.plain)
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
|
||||
.introspect(.list(style: .plain), on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list(style: .plain), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.plain)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 14, macOS 10.15, *)
|
||||
final class ListWithSidebarStyleTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithSidebarStyle = UIScrollView // covers both UITableView and UICollectionView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformListWithSidebarStyle = NSTableView
|
||||
#endif
|
||||
|
||||
func testListWithSidebarStyle() throws {
|
||||
guard #available(iOS 14, macOS 10.15, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformListWithSidebarStyle.self) { spies in
|
||||
let spy0 = spies[0]
|
||||
let spy1 = spies[1]
|
||||
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { spy0($0) }
|
||||
.introspect(.list(style: .sidebar), on: .iOS(.v16, .v17)) { spy0($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .sidebar), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
|
||||
.introspect(.list(style: .sidebar), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
}
|
||||
} extraAssertions: {
|
||||
XCTAssert($0[safe: 0] !== $0[safe: 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
#if !LEGACY_MACOS_SDK
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 16, tvOS 16, macOS 13, *)
|
||||
final class NavigationSplitViewTests: XCTestCase {
|
||||
#if canImport(UIKit) && os(iOS)
|
||||
typealias PlatformNavigationSplitView = UISplitViewController
|
||||
#elseif canImport(UIKit) && os(tvOS)
|
||||
typealias PlatformNavigationSplitView = UINavigationController
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformNavigationSplitView = NSSplitView
|
||||
#endif
|
||||
|
||||
func testNavigationSplitView() throws {
|
||||
guard #available(iOS 16, tvOS 16, macOS 13, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in
|
||||
let spy = spies[0]
|
||||
|
||||
NavigationSplitView {
|
||||
ZStack {
|
||||
Color.red
|
||||
Text("Something")
|
||||
}
|
||||
} detail: {
|
||||
ZStack {
|
||||
Color.red
|
||||
Text("Detail")
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.introspect(.navigationSplitView, on: .iOS(.v16, .v17), customize: spy)
|
||||
#elseif os(tvOS)
|
||||
.introspect(.navigationSplitView, on: .tvOS(.v16, .v17), customize: spy)
|
||||
#elseif os(macOS)
|
||||
.introspect(.navigationSplitView, on: .macOS(.v13, .v14), customize: spy)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func testNavigationSplitViewAsAncestor() throws {
|
||||
guard #available(iOS 16, tvOS 16, macOS 13, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in
|
||||
let spy = spies[0]
|
||||
|
||||
// NB: columnVisibility is explicitly set here for ancestor introspection to work, because initially on iPad the sidebar is hidden, so the introspection modifier isn't triggered until the user makes the sidebar appear. This is why ancestor introspection is discouraged for most situations and it's opt-in.
|
||||
NavigationSplitView(columnVisibility: .constant(.all)) {
|
||||
ZStack {
|
||||
Color.red
|
||||
Text("Sidebar")
|
||||
#if os(iOS)
|
||||
.introspect(.navigationSplitView, on: .iOS(.v16, .v17), scope: .ancestor, customize: spy)
|
||||
#elseif os(tvOS)
|
||||
.introspect(.navigationSplitView, on: .tvOS(.v16, .v17), scope: .ancestor, customize: spy)
|
||||
#elseif os(macOS)
|
||||
.introspect(.navigationSplitView, on: .macOS(.v13, .v14), scope: .ancestor, customize: spy)
|
||||
#endif
|
||||
}
|
||||
} detail: {
|
||||
Text("Detail")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
#if !os(macOS) && !LEGACY_MACOS_SDK
|
||||
import SwiftUI
|
||||
import SwiftUIIntrospect
|
||||
import XCTest
|
||||
|
||||
@available(iOS 16, tvOS 16, *)
|
||||
final class NavigationStackTests: XCTestCase {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformNavigationStack = UINavigationController
|
||||
#endif
|
||||
|
||||
func testNavigationStack() throws {
|
||||
guard #available(iOS 16, tvOS 16, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in
|
||||
let spy = spies[0]
|
||||
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
Color.red
|
||||
Text("Something")
|
||||
}
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), customize: spy)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func testNavigationStackAsAncestor() throws {
|
||||
guard #available(iOS 16, tvOS 16, *) else {
|
||||
throw XCTSkip()
|
||||
}
|
||||
|
||||
XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in
|
||||
let spy = spies[0]
|
||||
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
Color.red
|
||||
Text("Something")
|
||||
#if os(iOS) || os(tvOS)
|
||||
.introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), scope: .ancestor, customize: spy)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue