### Complete Example with Accessory and Customization Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/BasicUsage.md A full example demonstrating a ScrollView with a PortalHeaderView, custom accessory image, and applied header configurations. ```swift struct PhotosView: View { var body: some View { NavigationStack { ScrollView { PortalHeaderView() .padding(.top, 20) LazyVStack(spacing: 12) { ForEach(photos) { photo in PhotoRow(photo: photo) } } .padding() } .portalHeaderDestination() } .portalHeader( title: "Photos", subtitle: "My Collection", displays: [.title, .accessory] ) { Image(systemName: "photo.on.rectangle.angled") .font(.system(size: 64)) .foregroundStyle(.tint) } } } ``` -------------------------------- ### Full Example - Level 1 Configuration Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md A comprehensive example demonstrating Level 1 layer configuration for portal transitions, including custom spring animation, fade transition, and styling modifications like clipping and shadows. ```swift .portalTransition( item: $selectedPhoto, in: namespace, animation: .spring(duration: 0.45, bounce: 0.2), transition: .fade ) { AsyncImage(url: photo.thumbnailURL) } configuration: { content .clipShape(.rect(cornerRadius: isActive ? 0 : 8, style: .continuous)) .shadow(radius: isActive ? 0 : 10) } ``` -------------------------------- ### Install SwiftLint Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Install SwiftLint using Homebrew to enforce code style consistency. This tool is a prerequisite for development. ```bash brew install swiftlint ``` -------------------------------- ### Full Example - Level 2 Configuration Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md This example showcases Level 2 layer configuration for portal transitions, providing full control over layout with manual frame and offset application. It includes custom spring animation and fade transition. ```swift .portalTransition( item: $selectedPhoto, in: namespace, animation: .spring(duration: 0.45, bounce: 0.2), transition: .fade ) { AsyncImage(url: photo.thumbnailURL) } configuration: { content .frame(width: size.width, height: size.height) .clipShape(.rect(cornerRadius: isActive ? 0 : 8, style: .continuous)) .shadow(radius: isActive ? 0 : 10) .offset(x: position.x, y: position.y) } ``` -------------------------------- ### Basic Element Transition Setup Source: https://github.com/aeastr/portal/blob/main/README.md Wrap your app in PortalContainer, mark source and destination views with .portal(id: .source/.destination), and apply .portalTransition to the presenting view. ```swift // 1. Wrap your app in PortalContainer PortalContainer { ContentView() } // 2. Mark the source view Image("cover") .portal(id: "book", .source) // 3. Mark the destination view Image("cover") .portal(id: "book", .destination) // 4. Apply the transition .fullScreenCover(item: $selectedBook) { book in BookDetail(book: book) } .portalTransition(item: $selectedBook) ``` -------------------------------- ### Quickly Enable Debug Overlays Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/DebugOverlays.md Enable all debug overlays globally for immediate visualization. This is the simplest way to start debugging. ```swift ContentView() .portalTransitionDebugOverlays(true) ``` -------------------------------- ### Example Snapshot Usage in Package.swift Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Demonstrates how to specify a development snapshot version of the Portal package in Swift Package Manager. Use this to test changes from development branches. ```swift .package(url: "https://github.com/Aeastr/Portal", exact: "dev-20250113-143022-a1b2c3d") ``` -------------------------------- ### Basic Header Setup Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/BasicUsage.md Use this snippet to set up a basic header transition in your ScrollView within a NavigationStack. Requires importing PortalHeaders. ```swift import PortalHeaders NavigationStack { ScrollView { PortalHeaderView() // Your content here ForEach(items) { item in ItemRow(item: item) } } .portalHeaderDestination() } .portalHeader(title: "Favorites", subtitle: "Your starred items") ``` -------------------------------- ### Portal Transition with Styling Configuration Source: https://github.com/aeastr/portal/blob/main/README.md Customize the animating layer's appearance during transitions using the configuration closure. This example adds corner radius based on the transition's active state. ```swift .portalTransition(item: $selectedBook) { book in Image("cover") } configuration: { content, isActive, size, position in content .frame(width: size.width, height: size.height) .clipShape(.rect(cornerRadius: isActive ? 0 : 12)) .offset(x: position.x, y: position.y) } ``` -------------------------------- ### Setup Git Hooks Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Set up Git hooks to automate code checking before commits. This script ensures code quality and style adherence. ```bash ./Scripts/setup-hooks.sh ``` -------------------------------- ### Commit Message Example Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Use a clear and concise commit message following the conventional commit format, indicating the type of change. ```git feat: add .portalFade animation option ``` -------------------------------- ### Create Hotfix Branch Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Use this command to create a new branch for urgent bug fixes, starting from the main branch. ```bash git checkout main git pull origin main git checkout -b hotfix/4.2.3 ``` -------------------------------- ### Layer Configuration - Raw Source/Destination Values Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Access both source and destination values for advanced interpolation or complex logic in portal layer transitions. This allows for precise control over size and position based on the animation's start and end states. ```swift .portalTransition(item: $selectedItem, in: namespace) { ItemView(item: item) } configuration: { let size = isActive ? destinationSize : sourceSize let position = isActive ? destinationPosition : sourcePosition return content .frame(width: size.width, height: size.height) .offset(x: position.x, y: position.y) } ``` -------------------------------- ### Completion Handler for Transition Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Get notified when a portal transition finishes, allowing you to execute code upon completion. The handler receives a boolean indicating whether the forward or reverse transition was completed. ```swift .portalTransition( item: $selectedItem, in: namespace, completion: { if finished { // Forward transition completed } else { // Reverse transition completed } } ) { ItemView(item: item) } ``` -------------------------------- ### Portal Transition with Basic Configuration Source: https://github.com/aeastr/portal/blob/main/README.md Apply a transition with a configuration closure for styling, such as adding clips or shadows, while still allowing Portal to manage frame and offset. ```swift .portalTransition(item: $selectedBook) { book in Image("cover") } configuration: { content, isActive in content.clipShape(.rect(cornerRadius: isActive ? 0 : 12)) } ``` -------------------------------- ### Import and Usage of _PortalPrivate Source: https://github.com/aeastr/portal/blob/main/docs/PortalPrivate.md Demonstrates how to import and use the _PortalPrivate module for view mirroring. This includes setting up the source view, the destination view, and applying the transition modifier. ```swift import _PortalPrivate // Source PortalPrivate(id: "item") { MyView() } // Destination PortalPrivateDestination(id: "item") // Transition modifier .portalPrivateTransition(item: $selectedItem) ``` -------------------------------- ### Accessing the Portal Model Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md In your detail view, access the `CrossModel` via environment. This is the first step to managing portal transitions. ```swift @Environment(CrossModel.self) private var portalModel ``` -------------------------------- ### Clone Portal Repository Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Clone the Portal repository and navigate into the project directory to begin contributing. ```bash git clone https://github.com/aeastr/Portal.git cd Portal ``` -------------------------------- ### Run SwiftLint Checks Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Execute SwiftLint to check code style. This command verifies all files against the configured rules. ```bash swiftlint lint --config .swiftlint.yml ``` -------------------------------- ### Integrating AnimatedItemLayer in a Grid and Transition Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/ItemBasedAnimatedLayers.md Demonstrates the usage pattern of `AnimatedItemLayer` within an item-based portal transition. It shows how to set up the source in a grid and then use the `AnimatedItemLayer` within the `.portalTransition` modifier. ```swift // In the grid ForEach(photos) { photo in PhotoView(photo: photo) .portal(item: photo, .source) .onTapGesture { selectedPhoto = photo } } // In the transition .portalTransition(item: $selectedPhoto) { AnimatedItemLayer(item: $selectedPhoto) { item, isActive in if let item { PhotoView(photo: item) .scaleEffect(isActive ? 1.05 : 1.0) } } } ``` -------------------------------- ### Import Portal Modules Source: https://github.com/aeastr/portal/blob/main/docs/README.md Import the specific Portal module you need for your project. ```swift import PortalTransitions // Element transitions import PortalHeaders // Flowing headers import _PortalPrivate // View mirroring (private API) ``` -------------------------------- ### Enable Debug Overlays Globally Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/DebugOverlays.md Quickly enable all debug overlays for header elements. This is the simplest way to turn on the visualization. ```swift ContentView() .portalHeaderDebugOverlays(true) ``` -------------------------------- ### Combine Style and Target for Precise Debugging Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/DebugOverlays.md Mix and match visual styles with specific targets to create highly customized debug overlay configurations. This enables focused debugging on particular aspects of portal transitions. ```swift // Show labels on sources, borders on destinations ContentView() .portalTransitionDebugOverlays([.label], for: .source) .portalTransitionDebugOverlays([.border], for: .destination) ``` ```swift // Full overlays on layer, minimal on sources ContentView() .portalTransitionDebugOverlays(.all, for: .layer) .portalTransitionDebugOverlays([.label], for: .source) ``` -------------------------------- ### Usage of ScalingLayer in Source, Destination, and Transition Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimatedLayers.md Demonstrates how to integrate the custom ScalingLayer within the source view, destination view, and the portalTransition closure for a specific portal ID. ```swift // Source view ScalingLayer(portalID: "hero") { ItemContent() } .portal(id: "hero", .source) // Destination view ScalingLayer(portalID: "hero") { ItemContent() } .portal(id: "hero", .destination) // Transition .portalTransition(id: "hero", isActive: $showDetail) { ScalingLayer(portalID: "hero") { ItemContent() } } ``` -------------------------------- ### Portal Transition with Full Control Configuration Source: https://github.com/aeastr/portal/blob/main/README.md Use the full control configuration closure to manually manage the frame, size, and position of the animating layer, useful for custom modifier ordering. ```swift .portalTransition(item: $selectedBook) { book in Image("cover") } ``` -------------------------------- ### Header Layout Options Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/BasicUsage.md Configure the layout of the title and accessory. Use '.horizontal' for side-by-side or '.vertical' for stacked arrangements. ```swift // Side by side (default for title-only) .portalHeader(title: "Title", subtitle: "Sub", layout: .horizontal) ``` ```swift // Stacked (default when accessory provided) .portalHeader(title: "Title", subtitle: "Sub", layout: .vertical) { AccessoryView() } ``` -------------------------------- ### Layer Configuration - Styling Only Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Customize the appearance of the portal layer during transitions using the 'configuration' closure. This level allows for styling modifications like clipping and shadows without affecting positioning. ```swift .portalTransition(item: $selectedItem, in: namespace) { ItemView(item: item) } configuration: { content .clipShape(.rect(cornerRadius: isActive ? 24 : 12, style: .continuous)) .shadow(radius: isActive ? 10 : 2) } ``` -------------------------------- ### Basic Scaling Animation Layer Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimatedLayers.md A simple implementation of AnimatedPortalLayer that scales the content up when the portal is active. Use this for straightforward scaling effects during transitions. ```swift struct ScalingLayer: AnimatedPortalLayer { let portalID: String @ViewBuilder let content: () -> Content @State private var scale: CGFloat = 1 func animatedContent(isActive: Bool) -> some View { content() .scaleEffect(scale) .onChange(of: isActive) { withAnimation(.spring(duration: 0.3)) { scale = newValue ? 1.1 : 1.0 } } } } ``` -------------------------------- ### Create Feature Branch Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Create a new branch for your feature development, following the 'feat/' prefix convention. ```bash git checkout -b feat/my-new-feature ``` -------------------------------- ### Good vs Bad Delay Implementation Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Demonstrates the correct usage of `PortalConstants` for delays and durations, avoiding hardcoded values. ```swift // ✅ Good DispatchQueue.main.asyncAfter(deadline: .now() + PortalConstants.animationDelay) // ❌ Bad DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) ``` -------------------------------- ### Using AnimatedItemLayer Wrapper Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/ItemBasedAnimatedLayers.md A convenience wrapper for creating item-based animated layers without defining a custom protocol conforming type. Directly provides the item and active state to a closure for building the animated content. ```swift AnimatedItemLayer(item: $selectedPhoto) { photo, isActive in if let photo { AsyncImage(url: photo.imageURL) .scaleEffect(isActive ? 1.1 : 1.0) .animation(.spring, value: isActive) } } ``` -------------------------------- ### Run SwiftLint Script Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Use the provided script to run SwiftLint checks and auto-corrections. This is a convenient way to manage code style. ```bash ./Scripts/run-swiftlint.sh ``` -------------------------------- ### Update Swift Packages Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Run this command to update your project's Swift package dependencies. ```bash swift package update ``` -------------------------------- ### Resolve Package Resolution Issues Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Attempt to resolve package dependency issues by running 'swift package resolve' or cleaning the build folder. ```bash swift package resolve ``` -------------------------------- ### Layer Configuration - Full Control (Interpolated Values) Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Gain complete control over the portal layer's layout during transitions by applying frame and offset manually. This level provides interpolated size and position values based on the animation state. ```swift .portalTransition(item: $selectedItem, in: namespace) { ItemView(item: item) } configuration: { content .frame(width: size.width, height: size.height) .clipShape(.rect(cornerRadius: isActive ? 24 : 12, style: .continuous)) .shadow(radius: isActive ? 10 : 2) .offset(x: position.x, y: position.y) } ``` -------------------------------- ### Mark Destination View with String ID Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Mark a destination view with the same string identifier used for the source view. This links the origin and target for the transition. ```swift Image(photo.fullSize) .portal(id: "hero-image", .destination) ``` -------------------------------- ### Control Display Components Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/BasicUsage.md Customize which elements (title, accessory) are displayed and transition to the navigation bar using the 'displays' parameter. ```swift // Title only (default) .portalHeader(title: "Title", subtitle: "Subtitle", displays: [.title]) ``` ```swift // Title and accessory .portalHeader(title: "Title", subtitle: "Subtitle", displays: [.title, .accessory]) { AccessoryView() } ``` ```swift // Accessory only .portalHeader(title: "Title", subtitle: "Subtitle", displays: [.accessory]) { AccessoryView() } ``` -------------------------------- ### Customize Visual Style of Debug Overlays Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/DebugOverlays.md Choose the visual elements (labels, borders, background highlights) that appear in the debug overlays. This allows for tailored visualization based on debugging needs. ```swift // Labels only (text indicators) .portalTransitionDebugOverlays([.label], for: .all) ``` ```swift // Borders only (outline around views) .portalTransitionDebugOverlays([.border], for: .all) ``` ```swift // Background highlights only .portalTransitionDebugOverlays([.background], for: .all) ``` ```swift // Multiple styles .portalTransitionDebugOverlays([.label, .border], for: .all) ``` ```swift // All styles .portalTransitionDebugOverlays(.all, for: .all) ``` -------------------------------- ### Add Package Dependency Source: https://github.com/aeastr/portal/blob/main/docs/README.md Add the Portal package to your project's dependencies. ```swift dependencies: [ .package(url: "https://github.com/Aeastr/Portal", from: "4.0.0") ] ``` -------------------------------- ### Make Git Hooks Executable Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Grant execute permissions to Git hook scripts and shell scripts in the Scripts directory. ```bash chmod +x .githooks/* chmod +x Scripts/*.sh ``` -------------------------------- ### Custom Animation with EaseInOut Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Use the easeInOut timing curve for custom animations to achieve a smoother transition effect. This provides more control over the animation's acceleration and deceleration. ```swift .portalTransition( item: $selectedItem, in: namespace, animation: .easeInOut(duration: 0.4) ) { ItemView(item: item) } ``` -------------------------------- ### Custom Animation with Spring Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Apply a custom spring animation to control the timing and easing of portal transitions. This is useful for creating more dynamic and engaging visual effects. ```swift .portalTransition(item: $selectedItem, in: namespace, animation: .spring(duration: 0.5)) { ItemView(item: item) } ``` -------------------------------- ### Using AnimatedGroupLayer Wrapper for Coordinated Animations Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/ItemBasedAnimatedLayers.md A convenience wrapper for `AnimatedGroupPortalLayer` that simplifies the creation of coordinated animations for multiple items. It takes a binding to the items, a group ID, and a closure to build the animated content based on items and their active states. ```swift AnimatedGroupLayer(items: $selectedPhotos, groupID: "photoStack") { items, activeStates in ZStack { ForEach(items) { photo in let isActive = activeStates[photo.id] ?? false PhotoView(photo: photo) .scaleEffect(isActive ? 1.1 : 1.0) .animation(.spring, value: isActive) } } } ``` -------------------------------- ### ID-Based Portal Transfer (String IDs) Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md Use the ID-based `transferActivePortal` when working directly with `Hashable` IDs, such as strings. This provides flexibility when item objects are not readily available. ```swift portalModel.transferActivePortal(from: "panel1", to: "panel2") ``` -------------------------------- ### Manually Fix SwiftLint Violations Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Manually run SwiftLint with the fix option to resolve violations. Ensure the configuration file is present. ```bash swiftlint --fix --config .swiftlint.yml ``` -------------------------------- ### Header with Accessory Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/BasicUsage.md Add an accessory view, like an icon or image, that animates alongside the title into the navigation bar. The accessory scales and fades during the transition. ```swift NavigationStack { ScrollView { PortalHeaderView() // Content... } .portalHeaderDestination() } .portalHeader( title: "Photos", subtitle: "My Collection", displays: [.title, .accessory] ) { Image(systemName: "photo.on.rectangle.angled") .font(.system(size: 64)) .foregroundStyle(.tint) } ``` -------------------------------- ### Mark Source View with String ID Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Alternatively, mark a source view using a unique string identifier with the .portal modifier. This is useful when Identifiable items are not directly available. ```swift Image(photo.thumbnail) .portal(id: "hero-image", .source) ``` -------------------------------- ### Nearest Snapping Behavior Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/SnappingBehavior.md Configures the header to snap to the closest position. If progress is greater than 0.5, it snaps to the nav bar (1.0); otherwise, it snaps to the inline header (0.0). ```swift .portalHeader( title: "Title", subtitle: "Subtitle", snappingBehavior: .nearest ) ``` -------------------------------- ### Tracking the Current Item Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md Use a binding to track which item is currently active for the portal. This state is crucial for managing the portal's animation target. ```swift @Binding var portalItem: CarouselItem? ``` -------------------------------- ### AnimatedPortalLayer Protocol Definition Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimatedLayers.md Defines the structure for custom animated layers in portal transitions. It requires a portal ID, base content, and a function to return animated content based on the active state. ```swift public protocol AnimatedPortalLayer: View { associatedtype Content: View associatedtype AnimatedContent: View var portalID: String { get } @ViewBuilder var content: () -> Content { get } @ViewBuilder func animatedContent(isActive: Bool) -> AnimatedContent } ``` -------------------------------- ### Wrap View Hierarchy with PortalContainer Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Use PortalContainer to wrap the view hierarchy where portal transitions will occur. This sets up the necessary context for transitions. ```swift PortalContainer { // Your views here } ``` -------------------------------- ### Mark Source View with Identifiable Item Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Mark a source view (e.g., a thumbnail in a grid) with an Identifiable item using the .portal modifier. This view will be the origin of the transition. ```swift // Source - e.g., thumbnail in a grid Image(photo.thumbnail) .portal(item: photo, .source) ``` -------------------------------- ### Group Transition with Styling Options Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/GroupAnimations.md Apply styling options to a group transition using the portalTransition modifier. This includes specifying animation, transition type, and a completion handler for when the group animation finishes. ```swift // Level 1: Styling only (simplest) .portalTransition( ids: ids, groupID: "myGroup", in: namespace, isActive: $isActive, animation: .spring(duration: 0.5), transition: .fade, completion: { finished in print("Group transition finished: \(finished)") } ) { id in ContentView(id: id) } configuration: { content, isActive in content .clipShape(.rect(cornerRadius: isActive ? 16 : 8)) .shadow(radius: isActive ? 20 : 5) } ``` -------------------------------- ### ID-Based Portal Transfer (Item IDs) Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md Use the ID-based `transferActivePortal` with the `id` property of your `Identifiable` items. This is an alternative when direct ID access is preferred. ```swift portalModel.transferActivePortal(from: oldItem.id, to: newItem.id) ``` -------------------------------- ### Target Debug Overlays to Specific Elements Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/DebugOverlays.md Control which header elements receive debug overlays. Use this to focus on sources, destinations, or accessories individually or in combination. ```swift // Show overlays on sources only .portalHeaderDebugOverlays(.all, for: .source) ``` ```swift // Show overlays on destinations only .portalHeaderDebugOverlays(.all, for: .destination) ``` ```swift // Show overlays on accessories only .portalHeaderDebugOverlays(.all, for: .accessory) ``` ```swift // Multiple targets .portalHeaderDebugOverlays(.all, for: [.source, .destination]) ``` -------------------------------- ### Target Debug Overlays to Specific Elements Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/DebugOverlays.md Control which portal elements (source, destination, or animated layer) display debug overlays. Use this to focus debugging on a particular part of the transition. ```swift // Show overlays on sources only .portalTransitionDebugOverlays(.all, for: .source) ``` ```swift // Show overlays on destinations only .portalTransitionDebugOverlays(.all, for: .destination) ``` ```swift // Show overlays on the animated layer only .portalTransitionDebugOverlays(.all, for: .layer) ``` ```swift // Multiple targets .portalTransitionDebugOverlays(.all, for: [.source, .destination]) ``` -------------------------------- ### Custom ItemScalingLayer Implementation Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/ItemBasedAnimatedLayers.md A concrete implementation of `AnimatedItemPortalLayer` that applies a scale effect to content based on the item's data and the transition's active state. Uses `withAnimation` for smooth scaling changes. ```swift struct ItemScalingLayer: AnimatedItemPortalLayer { let item: Item? @ViewBuilder let content: (Item) -> Content @State private var scale: CGFloat = 1 func animatedContent(item: Item?, isActive: Bool) -> some View { Group { if let item { content(item) .scaleEffect(scale) .onChange(of: isActive) { _, newValue in withAnimation(.spring(duration: 0.3)) { scale = newValue ? 1.1 : 1.0 } } } } } } ``` -------------------------------- ### Flowing Headers with ScrollView Source: https://github.com/aeastr/portal/blob/main/README.md Use PortalHeaderView within a ScrollView and apply .portalHeaderDestination() to the ScrollView. Configure the header with .portalHeader(). ```swift NavigationStack { ScrollView { PortalHeaderView() ForEach(items) { item in ItemRow(item: item) } } .portalHeaderDestination() } .portalHeader(title: "Favorites", subtitle: "Your starred items") ``` -------------------------------- ### Add Portal Dependency Source: https://github.com/aeastr/portal/blob/main/README.md Add the Portal package to your project's dependencies. Ensure you are using Swift 6.2 or later and iOS 17+. ```swift dependencies: [ .package(url: "https://github.com/Aeastr/Portal.git", from: "4.0.0") ] ``` -------------------------------- ### Group Transition with Full Control Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/GroupAnimations.md Gain full control over group transitions by using the portalTransition modifier with detailed configuration. This allows for custom frame, offset, and clipping based on the active state, size, and position. ```swift // Level 2: Full control (when modifier ordering matters) .portalTransition( ids: ids, groupID: "myGroup", in: namespace, isActive: $isActive ) { id in ContentView(id: id) } configuration: { content, isActive, size, position in content .frame(width: size.width, height: size.height) .clipShape(.rect(cornerRadius: isActive ? 16 : 8)) .offset(x: position.x, y: position.y) } ``` -------------------------------- ### Sync Hotfix to Dev Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md After a hotfix is merged to main, sync the changes back to the dev branch. ```bash git checkout dev git merge main git push origin dev ``` -------------------------------- ### Remove Transition with Fade Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimationOptions.md Control how the portal layer disappears upon transition completion. Using `.fade` provides a smooth fading out effect, while `.none` makes it disappear instantly. ```swift .portalTransition( item: $selectedItem, in: namespace, transition: .fade // Layer fades out ) { ItemView(item: item) } ``` -------------------------------- ### Mark Destination View with Identifiable Item Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Mark a destination view (e.g., a fullscreen detail) with the same Identifiable item using the .portal modifier. This view will be the target of the transition. ```swift // Destination - e.g., fullscreen detail Image(photo.fullSize) .portal(item: photo, .destination) ``` -------------------------------- ### Auto-fix SwiftLint Issues Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Automatically fix code style issues detected by SwiftLint. This command attempts to correct violations where possible. ```bash swiftlint autocorrect --config .swiftlint.yml ``` -------------------------------- ### AnimatedItemPortalLayer Protocol Definition Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/ItemBasedAnimatedLayers.md Defines the structure for custom animated layers that respond to identifiable items and their active states during transitions. Requires an `Item` conforming to `Identifiable` and a way to build `AnimatedContent`. ```swift public protocol AnimatedItemPortalLayer: View { associatedtype Item: Identifiable associatedtype AnimatedContent: View var item: Item? { get } @ViewBuilder func animatedContent(item: Item?, isActive: Bool) -> AnimatedContent } ``` -------------------------------- ### Customize Visual Style of Debug Overlays Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/DebugOverlays.md Modify the appearance of debug overlays to show labels, borders, background highlights, or a combination. This helps in visualizing different aspects of header elements. ```swift // Labels only .portalHeaderDebugOverlays([.label], for: .all) ``` ```swift // Borders only .portalHeaderDebugOverlays([.border], for: .all) ``` ```swift // Background highlights .portalHeaderDebugOverlays([.background], for: .all) ``` ```swift // Multiple styles .portalHeaderDebugOverlays([.label, .border], for: .all) ``` -------------------------------- ### Disable Debug Overlays Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/DebugOverlays.md Turn off all debug overlays. This can be done globally or by explicitly setting no styles for all targets. ```swift .portalHeaderDebugOverlays(false) ``` ```swift // Or explicitly .portalHeaderDebugOverlays(.none, for: .all) ``` -------------------------------- ### AnimatedGroupPortalLayer Protocol Definition Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/ItemBasedAnimatedLayers.md Defines the protocol for creating animated layers that coordinate animations across multiple identifiable items simultaneously. It provides access to a collection of items and their individual active states. ```swift public protocol AnimatedGroupPortalLayer: View { associatedtype Item: Identifiable associatedtype AnimatedContent: View var items: [Item] { get } var groupID: String { get } @ViewBuilder func animatedContent(items: [Item], activeStates: [Item.ID: Bool]) -> AnimatedContent } ``` -------------------------------- ### Item-Based Portal Transfer Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md Use the item-based `transferActivePortal` when working with `Identifiable` items. It automatically extracts the IDs for a seamless transition. ```swift portalModel.transferActivePortal(fromItem: oldItem, toItem: newItem) ``` -------------------------------- ### Group Animation with String IDs Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/GroupAnimations.md Mark multiple source and destination portals with a shared groupID to animate them simultaneously. Trigger the group transition using the portalTransition modifier with a list of IDs. ```swift Image("photo1") .portal(id: "photo1", .source, groupID: "photoStack", in: namespace) Image("photo2") .portal(id: "photo2", .source, groupID: "photoStack", in: namespace) Image("photo3") .portal(id: "photo3", .source, groupID: "photoStack", in: namespace) // Destinations also share the groupID ForEach(["photo1", "photo2", "photo3"], id: \.self) { id in Image(id) .portal(id: id, .destination, groupID: "photoStack", in: namespace) } // Trigger group transition .portalTransition( ids: ["photo1", "photo2", "photo3"], groupID: "photoStack", in: namespace, isActive: $showDetail ) { id in Image(id) } ``` -------------------------------- ### Trigger Transition with Identifiable Items Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Use the .portalTransition modifier on the destination view to animate the transition when an Identifiable item is selected. This requires a binding to the selected item. ```swift .portalTransition(item: $selectedPhoto) { // The view to animate Image(photo.thumbnail) } ``` -------------------------------- ### Transferring Portal on Page Change Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md When the user swipes to a new page, call `transferActivePortal` to update the portal's active state and update the binding. The modifier automatically updates the layer view content to match the new item. ```swift .onChange(of: currentIndex) { oldIndex, newIndex in let oldItem = items[oldIndex] let newItem = items[newIndex] portalModel.transferActivePortal(fromItem: oldItem, toItem: newItem) portalItem = newItem } ``` -------------------------------- ### Separating Sheet and Portal State Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/TransferringPortals.md Use separate state variables for sheet presentation and portal animation to prevent the sheet from dismissing when the portal item changes. This ensures independent control over UI elements. ```swift @State private var selectedItem: CarouselItem? // Controls sheet presentation @State private var portalItem: CarouselItem? // Controls portal animation .fullScreenCover(item: $selectedItem) { item in CarouselDetailView( items: items, initialItem: item, portalItem: $portalItem ) } .portalTransition(item: $portalItem) { GridItemView(item: item) } ``` -------------------------------- ### Bounce Effect Animation Layer Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/AnimatedLayers.md An advanced implementation of AnimatedPortalLayer that creates a bouncing scale effect. It applies an initial scale-up animation followed by a delayed scale-down animation for a more dynamic feel. ```swift struct BouncingLayer: AnimatedPortalLayer { let portalID: String var peakScale: CGFloat = 1.1 @ViewBuilder let content: () -> Content @State private var scale: CGFloat = 1 func animatedContent(isActive: Bool) -> some View { content() .scaleEffect(scale) .onChange(of: isActive) { // Scale up withAnimation(.smooth(duration: 0.35, extraBounce: 0.25)) { scale = newValue ? peakScale : 1.15 } // Then back down DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { withAnimation(.smooth(duration: 0.47, extraBounce: 0.55)) { scale = 1 } } } } } ``` -------------------------------- ### Revert Merge Commit on Main Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Use this command to revert a mistakenly merged PR on the main branch. Ensure you use the correct merge commit SHA. ```bash git revert -m 1 git push origin main ``` -------------------------------- ### Trigger Transition with String ID Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/BasicUsage.md Use the .portalTransition modifier with a string ID and a presentation binding to trigger the animation between source and destination views. This is used when matching views by ID. ```swift .portalTransition(id: "hero-image", isPresented: $showDetail) { Image(photo.thumbnail) } ``` -------------------------------- ### None Snapping Behavior Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/SnappingBehavior.md Disables snapping behavior, allowing the header to remain at its exact scroll position. Useful for precise tracking without automatic correction. ```swift .portalHeader( title: "Title", subtitle: "Subtitle", snappingBehavior: .none ) ``` -------------------------------- ### Disable Debug Overlays Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/DebugOverlays.md Turn off all debug overlays globally or explicitly for all targets. This is useful for cleaning up the view after debugging or for release builds. ```swift .portalTransitionDebugOverlays(false) ``` ```swift // Or explicitly .portalTransitionDebugOverlays(.none, for: .all) ``` -------------------------------- ### Directional Snapping Behavior Source: https://github.com/aeastr/portal/blob/main/docs/PortalHeaders/SnappingBehavior.md Configures the header to snap based on scroll direction. Scrolls down to snap to the nav bar (1.0), and scrolls up to snap to the inline header (0.0). This is the default behavior. ```swift .portalHeader( title: "Title", subtitle: "Subtitle", snappingBehavior: .directional ) ``` -------------------------------- ### Bypass Git Hooks for Commit Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Use this flag to bypass Git hooks during a commit. Use sparingly and only when necessary. ```bash git commit --no-verify ``` -------------------------------- ### Revert Merge Commit on Dev Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Use this command to revert a mistakenly merged PR on the dev branch. Ensure you use the correct merge commit SHA. ```bash git checkout dev git revert -m 1 git push origin dev ``` -------------------------------- ### Manually Delete Git Tag Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Command to manually delete a Git tag from the remote repository if a dev snapshot creation fails and automatic cleanup is insufficient. This is part of the recovery process for failed snapshot creations. ```bash git push --delete origin dev-YYYYMMDD-HHMMSS-sha ``` -------------------------------- ### Manually Delete Local Git Tag Source: https://github.com/aeastr/portal/blob/main/CONTRIBUTING.md Command to delete a local Git tag if it was created but the corresponding dev snapshot failed and requires manual intervention. This is used in conjunction with remote tag deletion during recovery. ```bash git tag -d dev-YYYYMMDD-HHMMSS-sha ``` -------------------------------- ### Group Animation with Identifiable Items Source: https://github.com/aeastr/portal/blob/main/docs/PortalTransitions/GroupAnimations.md Use identifiable items to mark source and destination portals for group animations. The portalTransition modifier accepts a collection of items to trigger the coordinated transition. ```swift // Mark sources ForEach(photos) { photo in PhotoThumbnail(photo: photo) .portal(item: photo, .source, groupID: "gallery", in: namespace) } // Mark destinations ForEach(photos) { photo in PhotoDetail(photo: photo) .portal(item: photo, .destination, groupID: "gallery", in: namespace) } // Trigger group transition .portalTransition( items: photos, groupID: "gallery", in: namespace, isActive: $showGallery ) { photo in PhotoThumbnail(photo: photo) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.