From 6d308024bccb05a67b85e670285fc3bf7a4b462d Mon Sep 17 00:00:00 2001 From: Muukii Date: Sat, 14 Oct 2023 00:51:27 +0900 Subject: [PATCH 1/2] :zap: WIP --- .../project.pbxproj | 4 +++ .../FluidStackPathTests.swift | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Development/Sources/FluidInterfaceKitTests/FluidStackPathTests.swift diff --git a/Development/FluidInterfaceKit.xcodeproj/project.pbxproj b/Development/FluidInterfaceKit.xcodeproj/project.pbxproj index 158f7393a..8e717590d 100644 --- a/Development/FluidInterfaceKit.xcodeproj/project.pbxproj +++ b/Development/FluidInterfaceKit.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 4B6F87CA27B599B100216F4E /* UINavigationItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6F87C927B599B100216F4E /* UINavigationItemTests.swift */; }; 4B7410B927ACE975003D8AB5 /* DemoCompositionOrderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7410B827ACE975003D8AB5 /* DemoCompositionOrderViewController.swift */; }; 4B79ABBD27E7CB9A008DE44F /* UIViewControllerExtentionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B79ABBC27E7CB9A008DE44F /* UIViewControllerExtentionTests.swift */; }; + 4B844F6D2AD99ACF0083C5F5 /* FluidStackPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B844F6C2AD99ACF0083C5F5 /* FluidStackPathTests.swift */; }; 4B853AB329D9940100F62504 /* DemoPopoverSwiftUIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B853AB229D9940100F62504 /* DemoPopoverSwiftUIViewController.swift */; }; 4B853AB529D9A32A00F62504 /* DemoPopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B853AB429D9A32A00F62504 /* DemoPopoverViewController.swift */; }; 4B97A9E527B9828E0068B330 /* TreeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC8F5F727B6B1DA00095412 /* TreeBuilder.swift */; }; @@ -131,6 +132,7 @@ 4B6F87C927B599B100216F4E /* UINavigationItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UINavigationItemTests.swift; sourceTree = ""; }; 4B7410B827ACE975003D8AB5 /* DemoCompositionOrderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoCompositionOrderViewController.swift; sourceTree = ""; }; 4B79ABBC27E7CB9A008DE44F /* UIViewControllerExtentionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExtentionTests.swift; sourceTree = ""; }; + 4B844F6C2AD99ACF0083C5F5 /* FluidStackPathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FluidStackPathTests.swift; sourceTree = ""; }; 4B853AB229D9940100F62504 /* DemoPopoverSwiftUIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoPopoverSwiftUIViewController.swift; sourceTree = ""; }; 4B853AB429D9A32A00F62504 /* DemoPopoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoPopoverViewController.swift; sourceTree = ""; }; 4B959B062764F77E0003FFB6 /* FluidInterfaceKit-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "FluidInterfaceKit-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -318,6 +320,7 @@ 4B79ABBC27E7CB9A008DE44F /* UIViewControllerExtentionTests.swift */, 4B2F9E9F27FC080000F74ADB /* UIKitPresentationTests.swift */, 4B3BE33B27EDF6370060BAD3 /* FluidLocalEnvironmentTests.swift */, + 4B844F6C2AD99ACF0083C5F5 /* FluidStackPathTests.swift */, ); path = FluidInterfaceKitTests; sourceTree = ""; @@ -566,6 +569,7 @@ 4B3BE33C27EDF6370060BAD3 /* FluidLocalEnvironmentTests.swift in Sources */, 4B79ABBD27E7CB9A008DE44F /* UIViewControllerExtentionTests.swift in Sources */, 4BC667BA27B2E69C00182E6C /* FluidInterfaceKitTests.swift in Sources */, + 4B844F6D2AD99ACF0083C5F5 /* FluidStackPathTests.swift in Sources */, 4BD2A2B727B6A9AC00E20F07 /* FluidStackControllerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Development/Sources/FluidInterfaceKitTests/FluidStackPathTests.swift b/Development/Sources/FluidInterfaceKitTests/FluidStackPathTests.swift new file mode 100644 index 000000000..97ccec4f8 --- /dev/null +++ b/Development/Sources/FluidInterfaceKitTests/FluidStackPathTests.swift @@ -0,0 +1,26 @@ +import FluidInterfaceKit +import XCTest + +@MainActor +final class FluidStackPathTests: XCTestCase { + + func test_identifiable() { + + class A: UIViewController, FluidIdentifiableViewController { + var fluidIdentifier: String { "1" } + } + + class B: UIViewController, FluidIdentifiableViewController { + var fluidIdentifier: String { "1" } + } + + XCTAssertNotEqual( + FluidStackPath.Component.Identifiable(A()), + FluidStackPath.Component.Identifiable(B()) + ) + + XCTAssertNotNil(FluidStackPath.Component.Identifiable(A()).restore(A.self)) + XCTAssertNil(FluidStackPath.Component.Identifiable(A()).restore(B.self)) + + } +} From 64e0ea5353eb65c167d2c1bfe95a32299314fa7c Mon Sep 17 00:00:00 2001 From: Muukii Date: Sat, 14 Oct 2023 00:51:50 +0900 Subject: [PATCH 2/2] :zap: WIP --- .../ViewController/FluidStackController.swift | 17 ++++- .../ViewController/FluidStackPath.swift | 75 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 Sources/FluidInterfaceKit/ViewController/FluidStackPath.swift diff --git a/Sources/FluidInterfaceKit/ViewController/FluidStackController.swift b/Sources/FluidInterfaceKit/ViewController/FluidStackController.swift index 80ce6e7f6..90b429e06 100644 --- a/Sources/FluidInterfaceKit/ViewController/FluidStackController.swift +++ b/Sources/FluidInterfaceKit/ViewController/FluidStackController.swift @@ -41,6 +41,7 @@ public struct FluidStackContentConfiguration { } + /// A container view controller that manages view controller and view as child view controllers. /// It provides transitions when adding and removing. /// @@ -55,7 +56,9 @@ open class FluidStackController: UIViewController { } // MARK: - Properties - + + public private(set) var path: FluidStackPath = .init() + /// A closure that receives ``Action`` public final var stackActionHandler: (Action) -> Void = { _ in } @@ -87,7 +90,17 @@ open class FluidStackController: UIViewController { didSet { if stackingItems != oldValue { - + + let components = stackingItems.map { item -> FluidStackPath.Component in + if let identifiable = item.viewController as? (any FluidIdentifiableViewController) { + return .identifiable(.init(identifiable)) + } else { + return .volatile(.init(objectIdentifier: .init(item.viewController), ref: item.viewController)) + } + } + + self.path = .init(components: components) + // TODO: Update with animation UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { self.setNeedsStatusBarAppearanceUpdate() diff --git a/Sources/FluidInterfaceKit/ViewController/FluidStackPath.swift b/Sources/FluidInterfaceKit/ViewController/FluidStackPath.swift new file mode 100644 index 000000000..1ae7dd7ce --- /dev/null +++ b/Sources/FluidInterfaceKit/ViewController/FluidStackPath.swift @@ -0,0 +1,75 @@ +import UIKit + +public struct FluidStackPath: Equatable { + + public enum Component: Equatable { + + public struct Volatile: Equatable { + public let objectIdentifier: ObjectIdentifier + public private(set) weak var ref: UIViewController? + } + + public struct Identifiable: Equatable { + public static func == (lhs: Self, rhs: Self) -> Bool { + guard lhs.id == rhs.id else { return false } + guard lhs.viewControllerType == rhs.viewControllerType else { return false } + return true + } + + public let id: AnyHashable + public let viewControllerType: UIViewController.Type + + @MainActor + public init(_ viewController: some FluidIdentifiableViewController) { + self.id = viewController.fluidIdentifier as AnyHashable + self.viewControllerType = type(of: viewController) + } + + public func restore(_ target: Target.Type) -> Target.FluidID? { + guard viewControllerType == target else { return nil } + guard let id = id as? Target.FluidID else { return nil } + return id + } + } + + case volatile(Volatile) + case identifiable(AnyFluidIdentifiableViewController) + } + + public private(set) var components: [Component] = [] + + public init() { + + } + + public init(components: [Component]) { + self.components = components + } + + public mutating func append(_ component: Component) { + components.append(component) + } + + public mutating func removeLast(_ k: Int = 1) { + components.removeLast(k) + } +} + +public struct AnyFluidIdentifiableViewController: Equatable { + + public var base: AnyHashable + + public init(_ base: some FluidIdentifiableViewController) { + self.base = base as AnyHashable + } + +} + +@MainActor +public protocol FluidIdentifiableViewController: UIViewController { + + associatedtype FluidID: Hashable + + var fluidIdentifier: FluidID { get } + +}