Update SwiftMessage Layout Improvements [WIP]#585
Update SwiftMessage Layout Improvements [WIP]#585mofeejegi wants to merge 10 commits intowork/10.1.0-basefrom
Conversation
…ages into work/10.0.0-new2 # Conflicts: # SwiftMessages.podspec # SwiftMessages.xcodeproj/project.pbxproj # SwiftMessages/SwiftMessages.swift
# Conflicts: # Demo/Demo/Base.lproj/Main.storyboard # SwiftMessages.podspec # SwiftMessages.xcodeproj/project.pbxproj # SwiftMessages/BaseView.swift # SwiftMessages/EdgeAnimation.swift # SwiftMessages/PhysicsAnimation.swift # SwiftMessages/SwiftMessagesSegue.swift # SwiftMessages/UIViewController+Extensions.swift
There was a problem hiding this comment.
Pull request overview
This PR introduces layout improvements to SwiftMessages by adding support for leading/trailing message animations and refactoring the constraint system. The changes include renaming TopBottomAnimation to EdgeAnimation, introducing a new Layout configuration system with flexible boundary and dimension specifications, and adding new layout segue classes.
Changes:
- Renamed
TopBottomAnimationtoEdgeAnimationand added leading/trailing animation support - Introduced new
Layoutstruct with flexible inset, size, and center configuration - Added new segue layout types:
.leadingMessage,.trailingMessage,.leadingCard,.trailingCard,.leadingTab, and.trailingTab
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| UILayoutPriority+Extensions.swift | Defines new layout priority constants for the message sizing system |
| Layout.swift | New layout definition system with protocols and configuration structs |
| SwiftMessagesSegue.swift | Expanded layout options and refactored constraint configuration |
| EdgeAnimation.swift | Renamed from TopBottomAnimation with added leading/trailing animation support |
| MaskingView.swift | Implements layout constraint application logic |
| BaseView.swift | Conforms to LayoutDefining protocol and removes deprecated methods |
| PhysicsAnimation.swift | Updates to support LayoutDefining views |
| Presenter.swift | Updates class references from TopBottomAnimation to EdgeAnimation |
| CornerRoundingView.swift | Documentation updates for renamed class |
| MessageView.swift | Minor comment correction |
| Animator.swift | Updates AnimationContext to accept LayoutInstalling containers |
| TabView.xib | Removes obsolete constraints and variations |
| CenteredView.xib | Removes obsolete constraints and variations |
| CardView.xib | Removes obsolete constraints and variations |
| ViewControllersViewController.swift | Adds new segue class implementations for leading/trailing layouts |
| Main.storyboard | XML formatting changes |
| project.pbxproj | Updates file references for renamed EdgeAnimation file |
Comments suppressed due to low confidence (3)
SwiftMessages/EdgeAnimation.swift:106
- The empty do block serves no purpose and should be removed as it adds unnecessary clutter to the code.
SwiftMessages/EdgeAnimation.swift:25 - Type mismatch: The class defines its own Style enum with cases for top, bottom, leading, and trailing (lines 17-22), but the style property uses TopBottomAnimationStyle which only has top and bottom cases. This will cause compilation errors or prevent the new leading/trailing functionality from working. The property should be declared as
public let style: Styleinstead ofpublic let style: TopBottomAnimationStyle.
SwiftMessages/EdgeAnimation.swift:77 - There's a TODO comment indicating that proper leading and trailing animations need to be implemented. The current implementation may not be calculating transforms correctly for leading/trailing animations.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public struct Layout { | ||
| public var size = Size() | ||
| public var insets = Insets() | ||
| public var center = Center() | ||
| } |
There was a problem hiding this comment.
There's a nested type naming conflict. The Layout struct has a nested type also called Layout (at line 71). This creates ambiguity and makes the code harder to understand. The nested Layout struct should be renamed to something more descriptive like MinMaxLayout or LayoutBounds.
| /// The self-retainer will not allow the segue, presenting and presented view controllers to be released if the presenting view controller | ||
| /// is removed without first dismissing. This monitor handles that scenario by setting `self.selfRetainer = nil` if | ||
| /// the presenting view controller is no longer in the heirarchy. | ||
| private func startReleaseMonitor() { | ||
| Task { @MainActor [weak self] in | ||
| try? await Task.sleep(seconds: 2) | ||
| guard let self = self else { return } | ||
| switch self.source.view.window { | ||
| case .none: self.selfRetainer = nil | ||
| case .some: self.startReleaseMonitor() | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The method startReleaseMonitor should be an instance method of the SwiftMessagesSegue class. The current implementation has incorrect indentation and appears to be defined at the wrong scope level. The opening brace structure suggests this method should be inside the SwiftMessagesSegue class definition, not as a standalone function.
| /// The self-retainer will not allow the segue, presenting and presented view controllers to be released if the presenting view controller | |
| /// is removed without first dismissing. This monitor handles that scenario by setting `self.selfRetainer = nil` if | |
| /// the presenting view controller is no longer in the heirarchy. | |
| private func startReleaseMonitor() { | |
| Task { @MainActor [weak self] in | |
| try? await Task.sleep(seconds: 2) | |
| guard let self = self else { return } | |
| switch self.source.view.window { | |
| case .none: self.selfRetainer = nil | |
| case .some: self.startReleaseMonitor() | |
| } | |
| } | |
| } | |
| } | |
| /// The self-retainer will not allow the segue, presenting and presented view controllers to be released if the presenting view controller | |
| /// is removed without first dismissing. This monitor handles that scenario by setting `self.selfRetainer = nil` if | |
| /// the presenting view controller is no longer in the heirarchy. | |
| private func startReleaseMonitor() { | |
| Task { @MainActor [weak self] in | |
| try? await Task.sleep(seconds: 2) | |
| guard let self = self else { return } | |
| switch self.source.view.window { | |
| case .none: self.selfRetainer = nil | |
| case .some: self.startReleaseMonitor() | |
| } | |
| } | |
| } | |
| } |
…oard and fix comments
SwiftMessages/EdgeAnimation.swift
Outdated
| let view = context.messageView | ||
| self.context = context | ||
| UIView.animate(withDuration: hideDuration, delay: 0, options: [.beginFromCurrentState, .curveEaseIn], animations: { | ||
| UIView.animate(withDuration: hideDuration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: [.beginFromCurrentState]) { |
There was a problem hiding this comment.
Reverting this change. It broke the hide animation for bottom swift messages
There was a problem hiding this comment.
Define “broke”. I think this change was made for a reason
There was a problem hiding this comment.
It looked like the reason was to explore a different kind of animation for the hide() action.
I figured when we start implementing the leading and trailing animation, we can revisit it.
For now, I reverted it back to its current state on master.
Here's the behaviour as it is on the current release compared to the behaviour introduced in the change:
| Current Animation | Broken Animation |
|---|---|
![]() |
![]() |
| NSLayoutConstraint.activate([ | ||
| backgroundView.centerXAnchor.constraint(equalTo: centerXAnchor) | ||
| .with(priority: .belowMessageSizeable), | ||
| backgroundView.topAnchor.constraint(equalTo: topAnchor) |
There was a problem hiding this comment.
Excluding the layoutMarginsGuide.topAnchor and other layoutMarginsGuide anchors breaks the current behaviour of the background view - it now extends edge to edge horizontally and ignores the top safe area. An example can be seen in the "AnyView" in the demo.
| With Layout Margin Guides | Without Layout Margin Guides |
|---|---|
![]() |
![]() |




This PR brings forward a set of layout and segue improvements originally developed for an older version, updating them to align with the current release.
The changes introduce new segue classes and improve storyboard constraints and runtime attributes to enable more flexible message layouts. The work has been reconciled with the latest SwiftMessages updates, including recent iOS 26 changes.
The goal is to preserve and modernize prior layout work while ensuring full compatibility with the current release.