diff --git a/.github/workflows/run-tests-WaterfallGridSample.yml b/.github/workflows/run-tests-WaterfallGridSample.yml index 9cd4e72..e91cebe 100644 --- a/.github/workflows/run-tests-WaterfallGridSample.yml +++ b/.github/workflows/run-tests-WaterfallGridSample.yml @@ -48,5 +48,8 @@ jobs: - name: Build (tvOS) run: xcodebuild build -scheme WaterfallGrid -destination "platform=tvOS Simulator,OS=17.5,name=Apple TV" | xcpretty + - name: Build (visionOS) + run: xcodebuild build -scheme WaterfallGrid -destination "platform=visionOS Simulator,OS=1.2,name=Apple Vision Pro" | xcpretty + - name: Build (watchOS) run: xcodebuild build -scheme WaterfallGrid -destination "platform=watchOS Simulator,OS=10.5,name=Apple Watch Ultra 2 (49mm)" | xcpretty diff --git a/Package.swift b/Package.swift index 51bae84..7848829 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.9 // // Copyright © 2019 Paolo Leonardi. @@ -14,6 +14,7 @@ let package = Package( .iOS(.v13), .macOS(.v10_15), .tvOS(.v13), + .visionOS(.v1), .watchOS(.v6) ], products: [ diff --git a/README.md b/README.md index e3335be..242952f 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,8 @@ A waterfall grid layout view for SwiftUI. Image Demo 1

-

- - - - Swift Package Manager - - - - Twitter: @paololeonardi - -

+[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpaololeonardi%2FWaterfallGrid%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/paololeonardi/WaterfallGrid) +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpaololeonardi%2FWaterfallGrid%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/paololeonardi/WaterfallGrid) ## Features @@ -26,12 +17,6 @@ A waterfall grid layout view for SwiftUI. - [x] Horizontal or vertical scroll direction. - [x] Items update can be animated. -## Requirements - -- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ -- Xcode 11.0+ -- Swift 5.1+ - ## Usage ### Initialization @@ -174,7 +159,7 @@ Add it as a dependency within your `Package.swift` manifest: ```swift dependencies: [ - .package(url: "https://github.com/paololeonardi/WaterfallGrid.git", from: "1.0.0") + .package(url: "https://github.com/paololeonardi/WaterfallGrid.git", from: "1.1.0") ] ``` @@ -183,7 +168,7 @@ dependencies: [ You can install `WaterfallGrid` via CocoaPods by adding the following line to your `Podfile`: ```ruby -pod 'WaterfallGrid', '~> 1.0.0' +pod 'WaterfallGrid', '~> 1.1.0' ``` Run the `pod install` command to download the library diff --git a/Sources/WaterfallGrid/Environment/GridSyle.swift b/Sources/WaterfallGrid/Environment/GridSyle.swift index 3ded68c..6b8bb79 100644 --- a/Sources/WaterfallGrid/Environment/GridSyle.swift +++ b/Sources/WaterfallGrid/Environment/GridSyle.swift @@ -14,7 +14,7 @@ struct GridSyle { let animation: Animation? var columns: Int { - #if os(OSX) || os(tvOS) || targetEnvironment(macCatalyst) + #if os(OSX) || os(tvOS) || targetEnvironment(macCatalyst) || os(visionOS) return columnsInLandscape #elseif os(watchOS) return columnsInPortrait diff --git a/Sources/WaterfallGrid/View+GridStyle.swift b/Sources/WaterfallGrid/View+GridStyle.swift index d4cae6a..09643c1 100644 --- a/Sources/WaterfallGrid/View+GridStyle.swift +++ b/Sources/WaterfallGrid/View+GridStyle.swift @@ -37,6 +37,7 @@ extension View { /// - Parameter animation: The animation to apply when data change. If `animation` is `nil`, the grid doesn't animate. @available(OSX, unavailable) @available(tvOS, unavailable) + @available(visionOS, unavailable) @available(watchOS, unavailable) public func gridStyle( columnsInPortrait: Int = 2, diff --git a/Sources/WaterfallGrid/WaterfallGrid.swift b/Sources/WaterfallGrid/WaterfallGrid.swift index 5e3cddf..70dcc7d 100644 --- a/Sources/WaterfallGrid/WaterfallGrid.swift +++ b/Sources/WaterfallGrid/WaterfallGrid.swift @@ -7,7 +7,7 @@ import SwiftUI /// A container that presents items of variable heights arranged in a grid. -@available(iOS 13, OSX 10.15, tvOS 13, watchOS 6, *) +@available(iOS 13, OSX 10.15, tvOS 13, visionOS 1, watchOS 6, *) public struct WaterfallGrid: View where Data : RandomAccessCollection, Content : View, ID : Hashable { @Environment(\.gridStyle) private var style @@ -61,7 +61,7 @@ public struct WaterfallGrid: View where Data : RandomAccessCo .opacity(self.alignmentGuides[element[keyPath: self.dataId]] != nil ? 1 : 0) } } - .animation(self.loaded ? self.style.animation : nil) + .animation(self.loaded ? self.style.animation : nil, value: UUID()) } // MARK: - Helpers diff --git a/WaterfallGrid.podspec b/WaterfallGrid.podspec index 72b7b5b..1c50f92 100644 --- a/WaterfallGrid.podspec +++ b/WaterfallGrid.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "WaterfallGrid" - spec.version = "1.0.1" + spec.version = "1.1.0" spec.summary = "A waterfall grid layout view for SwiftUI." spec.homepage = "https://github.com/paololeonardi/WaterfallGrid" @@ -14,7 +14,7 @@ Pod::Spec.new do |spec| spec.watchos.deployment_target = "6.0" spec.tvos.deployment_target = "13.0" - spec.swift_version = '5.1' + spec.swift_version = '5.9' spec.source = { :git => "https://github.com/paololeonardi/WaterfallGrid.git", :tag => "#{spec.version}" } diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..40a59f6 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "icon_512x512@2x.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon_512x512@2x.png b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon_512x512@2x.png new file mode 100644 index 0000000..2cb1c41 Binary files /dev/null and b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon_512x512@2x.png differ diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 0000000..579db7c --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,14 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + } + ] +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..04056a5 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..40a59f6 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "icon_512x512@2x.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/icon_512x512@2x.png b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/icon_512x512@2x.png new file mode 100644 index 0000000..2cb1c41 Binary files /dev/null and b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/icon_512x512@2x.png differ diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Info.plist b/WaterfallGridSample/WaterfallGridSample visionOS/Info.plist new file mode 100644 index 0000000..20f75e2 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Info.plist @@ -0,0 +1,15 @@ + + + + + UIApplicationSceneManifest + + UIApplicationPreferredDefaultSceneSessionRole + UIWindowSceneSessionRoleApplication + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + + diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/Preview Content/Preview Assets.xcassets/Contents.json b/WaterfallGridSample/WaterfallGridSample visionOS/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallGridSample/WaterfallGridSample visionOS/WaterfallGridSample_visionOSApp.swift b/WaterfallGridSample/WaterfallGridSample visionOS/WaterfallGridSample_visionOSApp.swift new file mode 100644 index 0000000..c9faec7 --- /dev/null +++ b/WaterfallGridSample/WaterfallGridSample visionOS/WaterfallGridSample_visionOSApp.swift @@ -0,0 +1,16 @@ +// +// Copyright © 2024 Paolo Leonardi. +// +// Licensed under the MIT license. See the LICENSE file for more info. +// + +import SwiftUI + +@main +struct WaterfallGridSample_visionOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/WaterfallGridSample/WaterfallGridSample.xcodeproj/project.pbxproj b/WaterfallGridSample/WaterfallGridSample.xcodeproj/project.pbxproj index 37e0451..aa7ad2f 100644 --- a/WaterfallGridSample/WaterfallGridSample.xcodeproj/project.pbxproj +++ b/WaterfallGridSample/WaterfallGridSample.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -98,6 +98,29 @@ 40EADE89235E778600D9F615 /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40EADE87235E778600D9F615 /* CardView.swift */; }; 40EADE8A235E778600D9F615 /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40EADE87235E778600D9F615 /* CardView.swift */; }; 40EADE8B235E778600D9F615 /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40EADE87235E778600D9F615 /* CardView.swift */; }; + 89AAC18F2C4F2B3E00EF13CE /* WaterfallGridSample_visionOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AAC18E2C4F2B3E00EF13CE /* WaterfallGridSample_visionOSApp.swift */; }; + 89AAC1932C4F2B3F00EF13CE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 89AAC1922C4F2B3F00EF13CE /* Assets.xcassets */; }; + 89AAC1962C4F2B3F00EF13CE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 89AAC1952C4F2B3F00EF13CE /* Preview Assets.xcassets */; }; + 89AAC19C2C4F2B6000EF13CE /* WaterfallGrid in Frameworks */ = {isa = PBXBuildFile; productRef = 89AAC19B2C4F2B6000EF13CE /* WaterfallGrid */; }; + 89AAC19D2C4F2BC500EF13CE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C2909B2332FA66002406AB /* ContentView.swift */; }; + 89AAC19E2C4F2BCC00EF13CE /* RectanglesGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40DBA1FB2362501900F7CECD /* RectanglesGrid.swift */; }; + 89AAC19F2C4F2BD000EF13CE /* RectanglesContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A2D26C2358A89C00CB5CEB /* RectanglesContainer.swift */; }; + 89AAC1A02C4F2BD300EF13CE /* EditMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4068E7452362631600485327 /* EditMode.swift */; }; + 89AAC1A12C4F2BD600EF13CE /* RectangleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40541D4E2357DE9700B77014 /* RectangleView.swift */; }; + 89AAC1A22C4F2BDA00EF13CE /* RectangleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40541D502357DEFB00B77014 /* RectangleModel.swift */; }; + 89AAC1A32C4F2BDD00EF13CE /* ImagesGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40DBA200236253AB00F7CECD /* ImagesGrid.swift */; }; + 89AAC1A42C4F2BE000EF13CE /* ImagesContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40075AC4235939EC008C0D1C /* ImagesContainer.swift */; }; + 89AAC1A52C4F2BE200EF13CE /* CardsGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40DBA205236254E900F7CECD /* CardsGrid.swift */; }; + 89AAC1A62C4F2BE500EF13CE /* CardsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40EADE85235E541000D9F615 /* CardsContainer.swift */; }; + 89AAC1A72C4F2BE900EF13CE /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40EADE87235E778600D9F615 /* CardView.swift */; }; + 89AAC1A82C4F2BEB00EF13CE /* Card.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BC57C92360B06B00505B55 /* Card.swift */; }; + 89AAC1A92C4F2BEE00EF13CE /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402B18852360E562002A4AC8 /* Settings.swift */; }; + 89AAC1AA2C4F2BF200EF13CE /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402B188A2360F6C9002A4AC8 /* SettingsView.swift */; }; + 89AAC1AB2C4F2BF500EF13CE /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 205FCEBB2361A9D700E87B12 /* Screen.swift */; }; + 89AAC1AC2C4F2BF700EF13CE /* Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4068E74A236264C500485327 /* Generator.swift */; }; + 89AAC1AD2C4F2BFB00EF13CE /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4068E74F23626D0E00485327 /* View+Extension.swift */; }; + 89AAC1AE2C4F2BFE00EF13CE /* LoremIpsum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 203EAB5B235F02C100CC96B8 /* LoremIpsum.swift */; }; + 89AAC1AF2C4F2C1E00EF13CE /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4054A936235928160026CC38 /* Shared.xcassets */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -197,6 +220,11 @@ 40E42D8B236E16E300B7D2EE /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 40EADE85235E541000D9F615 /* CardsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardsContainer.swift; sourceTree = ""; }; 40EADE87235E778600D9F615 /* CardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = ""; }; + 89AAC1882C4F2B3E00EF13CE /* WaterfallGridSample visionOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WaterfallGridSample visionOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 89AAC18E2C4F2B3E00EF13CE /* WaterfallGridSample_visionOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterfallGridSample_visionOSApp.swift; sourceTree = ""; }; + 89AAC1922C4F2B3F00EF13CE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 89AAC1952C4F2B3F00EF13CE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 89AAC1972C4F2B3F00EF13CE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -232,6 +260,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 89AAC1852C4F2B3E00EF13CE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 89AAC19C2C4F2B6000EF13CE /* WaterfallGrid in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -375,6 +411,7 @@ 405059FE2357E5D200FB8691 /* WaterfallGridSample tvOS */, 40505A4B2357EDA900FB8691 /* WaterfallGridSample watchOS WatchKit App */, 40505A5A2357EDAD00FB8691 /* WaterfallGridSample watchOS WatchKit Extension */, + 89AAC1892C4F2B3E00EF13CE /* WaterfallGridSample visionOS */, 40C290722332F912002406AB /* Products */, 40C290A12332FB26002406AB /* Frameworks */, ); @@ -389,6 +426,7 @@ 40505A442357EDA800FB8691 /* WaterfallGridSample watchOS.app */, 40505A472357EDA900FB8691 /* WaterfallGridSample watchOS WatchKit App.app */, 40505A562357EDAD00FB8691 /* WaterfallGridSample watchOS WatchKit Extension.appex */, + 89AAC1882C4F2B3E00EF13CE /* WaterfallGridSample visionOS.app */, ); name = Products; sourceTree = ""; @@ -431,6 +469,25 @@ path = Images; sourceTree = ""; }; + 89AAC1892C4F2B3E00EF13CE /* WaterfallGridSample visionOS */ = { + isa = PBXGroup; + children = ( + 89AAC18E2C4F2B3E00EF13CE /* WaterfallGridSample_visionOSApp.swift */, + 89AAC1922C4F2B3F00EF13CE /* Assets.xcassets */, + 89AAC1972C4F2B3F00EF13CE /* Info.plist */, + 89AAC1942C4F2B3F00EF13CE /* Preview Content */, + ); + path = "WaterfallGridSample visionOS"; + sourceTree = ""; + }; + 89AAC1942C4F2B3F00EF13CE /* Preview Content */ = { + isa = PBXGroup; + children = ( + 89AAC1952C4F2B3F00EF13CE /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -548,13 +605,33 @@ productReference = 40505A562357EDAD00FB8691 /* WaterfallGridSample watchOS WatchKit Extension.appex */; productType = "com.apple.product-type.watchkit2-extension"; }; + 89AAC1872C4F2B3E00EF13CE /* WaterfallGridSample visionOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 89AAC19A2C4F2B3F00EF13CE /* Build configuration list for PBXNativeTarget "WaterfallGridSample visionOS" */; + buildPhases = ( + 89AAC1842C4F2B3E00EF13CE /* Sources */, + 89AAC1852C4F2B3E00EF13CE /* Frameworks */, + 89AAC1862C4F2B3E00EF13CE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "WaterfallGridSample visionOS"; + packageProductDependencies = ( + 89AAC19B2C4F2B6000EF13CE /* WaterfallGrid */, + ); + productName = "WaterfallGridSample visionOS"; + productReference = 89AAC1882C4F2B3E00EF13CE /* WaterfallGridSample visionOS.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 40C290692332F912002406AB /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1100; + LastSwiftUpdateCheck = 1540; LastUpgradeCheck = 1110; ORGANIZATIONNAME = "Paolo Leonardi"; TargetAttributes = { @@ -577,6 +654,9 @@ 40505A552357EDAD00FB8691 = { CreatedOnToolsVersion = 11.0; }; + 89AAC1872C4F2B3E00EF13CE = { + CreatedOnToolsVersion = 15.4; + }; }; }; buildConfigurationList = 40C2906C2332F912002406AB /* Build configuration list for PBXProject "WaterfallGridSample" */; @@ -598,6 +678,7 @@ 40505A432357EDA800FB8691 /* WaterfallGridSample watchOS */, 40505A462357EDA900FB8691 /* WaterfallGridSample watchOS WatchKit App */, 40505A552357EDAD00FB8691 /* WaterfallGridSample watchOS WatchKit Extension */, + 89AAC1872C4F2B3E00EF13CE /* WaterfallGridSample visionOS */, ); }; /* End PBXProject section */ @@ -662,6 +743,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 89AAC1862C4F2B3E00EF13CE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 89AAC1962C4F2B3F00EF13CE /* Preview Assets.xcassets in Resources */, + 89AAC1932C4F2B3F00EF13CE /* Assets.xcassets in Resources */, + 89AAC1AF2C4F2C1E00EF13CE /* Shared.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -761,6 +852,32 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 89AAC1842C4F2B3E00EF13CE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 89AAC1AA2C4F2BF200EF13CE /* SettingsView.swift in Sources */, + 89AAC1A22C4F2BDA00EF13CE /* RectangleModel.swift in Sources */, + 89AAC19D2C4F2BC500EF13CE /* ContentView.swift in Sources */, + 89AAC18F2C4F2B3E00EF13CE /* WaterfallGridSample_visionOSApp.swift in Sources */, + 89AAC1A12C4F2BD600EF13CE /* RectangleView.swift in Sources */, + 89AAC1A92C4F2BEE00EF13CE /* Settings.swift in Sources */, + 89AAC1A82C4F2BEB00EF13CE /* Card.swift in Sources */, + 89AAC1AB2C4F2BF500EF13CE /* Screen.swift in Sources */, + 89AAC1A42C4F2BE000EF13CE /* ImagesContainer.swift in Sources */, + 89AAC1A52C4F2BE200EF13CE /* CardsGrid.swift in Sources */, + 89AAC1AC2C4F2BF700EF13CE /* Generator.swift in Sources */, + 89AAC1A02C4F2BD300EF13CE /* EditMode.swift in Sources */, + 89AAC19E2C4F2BCC00EF13CE /* RectanglesGrid.swift in Sources */, + 89AAC19F2C4F2BD000EF13CE /* RectanglesContainer.swift in Sources */, + 89AAC1A32C4F2BDD00EF13CE /* ImagesGrid.swift in Sources */, + 89AAC1AE2C4F2BFE00EF13CE /* LoremIpsum.swift in Sources */, + 89AAC1A72C4F2BE900EF13CE /* CardView.swift in Sources */, + 89AAC1AD2C4F2BFB00EF13CE /* View+Extension.swift in Sources */, + 89AAC1A62C4F2BE500EF13CE /* CardsContainer.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1193,6 +1310,75 @@ }; name = Release; }; + 89AAC1982C4F2B3F00EF13CE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"WaterfallGridSample visionOS/Preview Content\""; + DEVELOPMENT_TEAM = 586FE2J8R2; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.paololeonardi.WaterfallGridSample-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = xros; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.2; + }; + name = Debug; + }; + 89AAC1992C4F2B3F00EF13CE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"WaterfallGridSample visionOS/Preview Content\""; + DEVELOPMENT_TEAM = 586FE2J8R2; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.paololeonardi.WaterfallGridSample-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = xros; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.2; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1259,6 +1445,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 89AAC19A2C4F2B3F00EF13CE /* Build configuration list for PBXNativeTarget "WaterfallGridSample visionOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 89AAC1982C4F2B3F00EF13CE /* Debug */, + 89AAC1992C4F2B3F00EF13CE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1278,6 +1473,10 @@ isa = XCSwiftPackageProductDependency; productName = WaterfallGrid; }; + 89AAC19B2C4F2B6000EF13CE /* WaterfallGrid */ = { + isa = XCSwiftPackageProductDependency; + productName = WaterfallGrid; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 40C290692332F912002406AB /* Project object */; diff --git a/WaterfallGridSample/WaterfallGridSample/Settings/Settings.swift b/WaterfallGridSample/WaterfallGridSample/Settings/Settings.swift index 8465dca..39affcc 100644 --- a/WaterfallGridSample/WaterfallGridSample/Settings/Settings.swift +++ b/WaterfallGridSample/WaterfallGridSample/Settings/Settings.swift @@ -18,7 +18,7 @@ struct Settings { var animationSpeed: Double var columns: Double { - #if os(OSX) || os(tvOS) || targetEnvironment(macCatalyst) + #if os(OSX) || os(tvOS) || targetEnvironment(macCatalyst) || os(visionOS) return columnsInLandscape #else return columnsInPortrait diff --git a/WaterfallGridSample/WaterfallGridSample/Settings/SettingsView.swift b/WaterfallGridSample/WaterfallGridSample/Settings/SettingsView.swift index 959f272..b2da8a8 100644 --- a/WaterfallGridSample/WaterfallGridSample/Settings/SettingsView.swift +++ b/WaterfallGridSample/WaterfallGridSample/Settings/SettingsView.swift @@ -24,76 +24,76 @@ struct SettingsView: View { } var body: some View { - GeometryReader { geometry in - NavigationView { - Form() { - Section(header: Text("Columns")) { - #if os(iOS) && !targetEnvironment(macCatalyst) - self.valueSelector(self.$newSettings.columnsInPortrait, bounds: 1...10, step: 1, label: "Portrait", in: geometry) - self.valueSelector(self.$newSettings.columnsInLandscape, bounds: 1...10, step: 1, label: "Landscape", in: geometry) - #else - self.valueSelector(self.$newSettings.columnsInLandscape, bounds: 1...10, step: 1, label: "Columns", in: geometry) - #endif - } + NavigationView { + Form() { + Section(header: Text("Columns")) { + #if os(iOS) && !targetEnvironment(macCatalyst) + self.valueSelector(self.$newSettings.columnsInPortrait, bounds: 1...10, step: 1, label: "Portrait") + self.valueSelector(self.$newSettings.columnsInLandscape, bounds: 1...10, step: 1, label: "Landscape") + #else + self.valueSelector(self.$newSettings.columnsInLandscape, bounds: 1...10, step: 1, label: "Columns") + #endif + } - Section(header: Text("Spacing")) { - self.valueSelector(self.$newSettings.spacing, bounds: 0...40, step: 1, label: "Spacing", in: geometry) - } + Section(header: Text("Spacing")) { + self.valueSelector(self.$newSettings.spacing, bounds: 0...40, step: 1, label: "Spacing") + } - Section(header: Text("Padding")) { - self.valueSelector(self.$newSettings.padding.top, bounds: 0...40, step: 1, label: "Top", in: geometry) - self.valueSelector(self.$newSettings.padding.leading, bounds: 0...40, step: 1, label: "Leading", in: geometry) - self.valueSelector(self.$newSettings.padding.bottom, bounds: 0...40, step: 1, label: "Bottom", in: geometry) - self.valueSelector(self.$newSettings.padding.trailing, bounds: 0...40, step: 1, label: "Trailing", in: geometry) - } + Section(header: Text("Padding")) { + self.valueSelector(self.$newSettings.padding.top, bounds: 0...40, step: 1, label: "Top") + self.valueSelector(self.$newSettings.padding.leading, bounds: 0...40, step: 1, label: "Leading") + self.valueSelector(self.$newSettings.padding.bottom, bounds: 0...40, step: 1, label: "Bottom") + self.valueSelector(self.$newSettings.padding.trailing, bounds: 0...40, step: 1, label: "Trailing") + } - Section(header: Text("Scroll Options")) { - Picker(selection: self.$newSettings.scrollDirection, label: Text("Direction")) { - ForEach(Axis.allCases, id: \.self) { axes in - Text(axes.description.capitalized) - } + Section(header: Text("Scroll Options")) { + Picker(selection: self.$newSettings.scrollDirection, label: Text("Direction")) { + ForEach(Axis.allCases, id: \.self) { axes in + Text(axes.description.capitalized) } - Toggle(isOn: self.$newSettings.showsIndicators) { - Text("Show Indicators") - } - .pickerStyle(SegmentedPickerStyle()) } - - Section(header: Text("Animation")) { - Toggle(isOn: self.$animationEnabled) { - Text("Enabled") - } - if self.animationEnabled { - self.valueSelector(self.$newSettings.animationSpeed, bounds: 0.1...1, step: 0.05, label: "Speed", in: geometry) - } + Toggle(isOn: self.$newSettings.showsIndicators) { + Text("Show Indicators") } + .pickerStyle(SegmentedPickerStyle()) } - .onDisappear { - let animation = Animation.default.speed(self.newSettings.animationSpeed) - self.newSettings.animation = self.animationEnabled ? animation : nil - self.settings.wrappedValue = self.newSettings + + Section(header: Text("Animation")) { + Toggle(isOn: self.$animationEnabled) { + Text("Enabled") + } + if self.animationEnabled { + self.valueSelector(self.$newSettings.animationSpeed, bounds: 0.1...1, step: 0.05, label: "Speed") + } } - .customNavigationBarTitle(Text("Settings"), displayMode: .inline) - .customNavigationBarItems(leading: self.leadingNavigationBarItems(), trailing: self.trailingNavigationBarItems()) } + .onDisappear { + let animation = Animation.default.speed(self.newSettings.animationSpeed) + self.newSettings.animation = self.animationEnabled ? animation : nil + self.settings.wrappedValue = self.newSettings + } + .customNavigationBarTitle(Text("Settings"), displayMode: .inline) + .customNavigationBarItems(leading: self.leadingNavigationBarItems(), trailing: self.trailingNavigationBarItems()) } .navigationViewStyle(StackNavigationViewStyle()) } - private func valueSelector(_ selection: Binding, bounds: ClosedRange, step: V.Stride, label: String, in geometry: GeometryProxy) -> some View + private func valueSelector(_ selection: Binding, bounds: ClosedRange, step: V.Stride, label: String) -> some View where V : BinaryFloatingPoint, V.Stride : BinaryFloatingPoint { - #if os(tvOS) - return Picker(selection: selection, label: Text(label)) { + #if os(tvOS) || os(visionOS) + Picker(selection: selection, label: Text(label)) { ForEach(Array(stride(from: bounds.lowerBound, through: bounds.upperBound, by: step)), id: \.self) { index in Text(String(format:"%.2f", Double(index))) } } #else - return HStack() { - Text("\(label): \(String(format:"%.2f", Double(selection.wrappedValue)))") - Spacer() - Slider(value: selection, in: bounds, step: step) - .frame(width: geometry.size.width / 2) + GeometryReader { geometry in + HStack() { + Text("\(label): \(String(format:"%.2f", Double(selection.wrappedValue)))") + Spacer() + Slider(value: selection, in: bounds, step: step) + .frame(width: geometry.size.width / 2) + } } #endif }