Skip to content

Commit

Permalink
Patch 2.2.3
Browse files Browse the repository at this point in the history
feat:
- Added release-cocoapods.yml

fix:
- Fixed the problem with memory leaks
- Fixed the issue where a drag gesture caused the wrong popup to close
- Fixed the rare issue where the popup may not fill the full width of the screen
  • Loading branch information
FulcrumOne committed Mar 9, 2024
1 parent e126d5c commit 7b684a1
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 33 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/release-cocoapods.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: CI
on:
push:
branches:
- master
workflow_dispatch:

jobs:
build:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Publish to CocoaPod register
env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
run: |
pod trunk push MijickPopupView.podspec
2 changes: 1 addition & 1 deletion MijickPopupView.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Pod::Spec.new do |s|
PopupView is a free and open-source library dedicated for SwiftUI that makes the process of presenting popups easier and much cleaner.
DESC

s.version = '2.2.1'
s.version = '2.2.3'
s.ios.deployment_target = '14.0'
s.osx.deployment_target = '12.0'
s.swift_version = '5.0'
Expand Down
5 changes: 0 additions & 5 deletions Sources/Internal/Managers/PopupManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ public class PopupManager: ObservableObject {
static let shared: PopupManager = .init()
private init() {}
}
extension PopupManager {
var top: [AnyPopup<TopPopupConfig>] { views.compactMap { $0 as? AnyPopup<TopPopupConfig> } }
var centre: [AnyPopup<CentrePopupConfig>] { views.compactMap { $0 as? AnyPopup<CentrePopupConfig> } }
var bottom: [AnyPopup<BottomPopupConfig>] { views.compactMap { $0 as? AnyPopup<BottomPopupConfig> } }
}

// MARK: - Operations
enum StackOperation {
Expand Down
5 changes: 5 additions & 0 deletions Sources/Internal/Protocols/Popup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ public extension Popup {

func configurePopup(popup: Config) -> Config { popup }
}

// MARK: - Helpers
extension Popup {
func remove() { PopupManager.performOperation(.remove(id: id)) }
}
6 changes: 3 additions & 3 deletions Sources/Internal/Protocols/PopupStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ protocol PopupStack: View {
associatedtype Config: Configurable

var items: [AnyPopup<Config>] { get }
var heights: [AnyPopup<Config>: CGFloat] { get }
var heights: [String: CGFloat] { get }
var globalConfig: GlobalConfig { get }
var gestureTranslation: CGFloat { get }
var translationProgress: CGFloat { get }
Expand All @@ -28,7 +28,7 @@ protocol PopupStack: View {
var tapOutsideClosesPopup: Bool { get }
}
extension PopupStack {
var heights: [AnyPopup<Config>: CGFloat] { [:] }
var heights: [String: CGFloat] { [:] }
var gestureTranslation: CGFloat { 0 }
var translationProgress: CGFloat { 1 }

Expand Down Expand Up @@ -112,7 +112,7 @@ extension PopupStack {
func getInitialHeight() -> CGFloat {
guard let previousView = items.nextToLast else { return 0 }

let height = heights.filter { $0.key == previousView }.first?.value ?? 0
let height = heights.filter { $0.key == previousView.id }.first?.value ?? 0
return height
}
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/Internal/Views/PopupBottomStackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct PopupBottomStackView: PopupStack {
let items: [AnyPopup<BottomPopupConfig>]
let globalConfig: GlobalConfig
@State var gestureTranslation: CGFloat = 0
@State var heights: [AnyPopup<BottomPopupConfig>: CGFloat] = [:]
@State var heights: [String: CGFloat] = [:]
@ObservedObject private var screen: ScreenManager = .shared
@ObservedObject private var keyboardManager: KeyboardManager = .shared

Expand Down Expand Up @@ -69,7 +69,7 @@ private extension PopupBottomStackView {
}
private extension PopupBottomStackView {
func dismissLastItemIfNeeded() {
if translationProgress >= gestureClosingThresholdFactor { items.last?.dismiss() }
if translationProgress >= gestureClosingThresholdFactor { items.last?.remove() }
}
func resetGestureTranslationOnEnd() {
let resetAfter = items.count == 1 && translationProgress >= gestureClosingThresholdFactor ? 0.25 : 0
Expand All @@ -79,7 +79,7 @@ private extension PopupBottomStackView {

// MARK: - Action Modifiers
private extension PopupBottomStackView {
func onScreenChange(_ value: Any) { if let lastItem = items.last { saveHeight(heights[lastItem] ?? .infinity, withAnimation: nil, for: lastItem) }}
func onScreenChange(_ value: Any) { if let lastItem = items.last { saveHeight(heights[lastItem.id] ?? .infinity, withAnimation: nil, for: lastItem) }}
}

// MARK: - View Modifiers
Expand All @@ -93,9 +93,9 @@ private extension PopupBottomStackView {
func saveHeight(_ height: CGFloat, withAnimation animation: Animation?, for item: AnyPopup<BottomPopupConfig>) { withAnimation(animation) {
let config = item.configurePopup(popup: .init())

if config.contentFillsEntireScreen { return heights[item] = screen.size.height }
if config.contentFillsWholeHeight { return heights[item] = getMaxHeight() }
return heights[item] = min(height, maxHeight)
if config.contentFillsEntireScreen { return heights[item.id] = screen.size.height }
if config.contentFillsWholeHeight { return heights[item.id] = getMaxHeight() }
return heights[item.id] = min(height, maxHeight)
}}
func getMaxHeight() -> CGFloat {
let basicHeight = screen.size.height - screen.safeArea.top
Expand All @@ -118,7 +118,7 @@ extension PopupBottomStackView {
var popupBottomPadding: CGFloat { lastPopupConfig.popupPadding.bottom }
var popupHorizontalPadding: CGFloat { lastPopupConfig.popupPadding.horizontal }
var popupShadow: Shadow { globalConfig.bottom.shadow }
var height: CGFloat { heights.first { $0.key == items.last }?.value ?? (lastPopupConfig.contentFillsEntireScreen ? screen.size.height : getInitialHeight()) }
var height: CGFloat { heights.first { $0.key == items.last?.id }?.value ?? (lastPopupConfig.contentFillsEntireScreen ? screen.size.height : getInitialHeight()) }
var maxHeight: CGFloat { getMaxHeight() - popupBottomPadding }
var distanceFromKeyboard: CGFloat { lastPopupConfig.distanceFromKeyboard ?? globalConfig.bottom.distanceFromKeyboard }
var cornerRadius: CGFloat { let cornerRadius = lastPopupConfig.cornerRadius ?? globalConfig.bottom.cornerRadius; return lastPopupConfig.contentFillsEntireScreen ? min(cornerRadius, screen.cornerRadius ?? 0) : cornerRadius }
Expand Down
8 changes: 4 additions & 4 deletions Sources/Internal/Views/PopupTopStackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct PopupTopStackView: PopupStack {
let items: [AnyPopup<TopPopupConfig>]
let globalConfig: GlobalConfig
@State var gestureTranslation: CGFloat = 0
@State var heights: [AnyPopup<TopPopupConfig>: CGFloat] = [:]
@State var heights: [String: CGFloat] = [:]
@ObservedObject private var screen: ScreenManager = .shared


Expand Down Expand Up @@ -67,7 +67,7 @@ private extension PopupTopStackView {
}
private extension PopupTopStackView {
func dismissLastItemIfNeeded() {
if translationProgress >= gestureClosingThresholdFactor { items.last?.dismiss() }
if translationProgress >= gestureClosingThresholdFactor { items.last?.remove() }
}
func resetGestureTranslationOnEnd() {
let resetAfter = items.count == 1 && translationProgress >= gestureClosingThresholdFactor ? 0.25 : 0
Expand All @@ -84,15 +84,15 @@ private extension PopupTopStackView {
}
}
func getBackgroundColour(for item: AnyPopup<TopPopupConfig>) -> Color { getConfig(item).backgroundColour ?? globalConfig.top.backgroundColour }
func saveHeight(_ height: CGFloat, for item: AnyPopup<TopPopupConfig>) { heights[item] = height }
func saveHeight(_ height: CGFloat, for item: AnyPopup<TopPopupConfig>) { heights[item.id] = height }
}

// MARK: - Flags & Values
extension PopupTopStackView {
var contentTopPadding: CGFloat { lastPopupConfig.contentIgnoresSafeArea ? 0 : max(screen.safeArea.top - popupTopPadding, 0) }
var popupTopPadding: CGFloat { lastPopupConfig.popupPadding.top }
var popupShadow: Shadow { globalConfig.top.shadow }
var height: CGFloat { heights.first { $0.key == items.last }?.value ?? getInitialHeight() }
var height: CGFloat { heights.first { $0.key == items.last?.id }?.value ?? getInitialHeight() }
var cornerRadius: CGFloat { lastPopupConfig.cornerRadius ?? globalConfig.top.cornerRadius }

var stackLimit: Int { globalConfig.top.stackLimit }
Expand Down
27 changes: 15 additions & 12 deletions Sources/Internal/Views/PopupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import SwiftUI
struct PopupView: View {
let globalConfig: GlobalConfig
@State private var zIndex: ZIndex = .init()
@ObservedObject private var stack: PopupManager = .shared
@ObservedObject private var popupManager: PopupManager = .shared
@ObservedObject private var screenManager: ScreenManager = .shared


Expand All @@ -27,13 +27,13 @@ struct PopupView: View {
struct PopupView: View {
let rootView: any View
let globalConfig: GlobalConfig
@ObservedObject private var stack: PopupManager = .shared
@ObservedObject private var popupManager: PopupManager = .shared
@ObservedObject private var screenManager: ScreenManager = .shared


var body: some View {
AnyView(rootView)
.disabled(!stack.views.isEmpty)
.disabled(!popupManager.views.isEmpty)
.overlay(createBody())
}
}
Expand All @@ -47,7 +47,7 @@ private extension PopupView {
.frame(height: screenManager.size.height)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(createOverlay())
.onChange(of: stack.views.count, perform: onViewsCountChange)
.onChange(of: popupManager.views.count, perform: onViewsCountChange)
}
}

Expand All @@ -58,7 +58,7 @@ private extension PopupView {
createCentrePopupStackView().zIndex(zIndex.centre)
createBottomPopupStackView().zIndex(zIndex.bottom)
}
.animation(stackAnimation, value: stack.views.map(\.id))
.animation(stackAnimation, value: popupManager.views.map(\.id))
}
func createOverlay() -> some View {
overlayColour
Expand All @@ -71,28 +71,31 @@ private extension PopupView {

private extension PopupView {
func createTopPopupStackView() -> some View {
PopupTopStackView(items: stack.top, globalConfig: globalConfig)
PopupTopStackView(items: getViews(AnyPopup<TopPopupConfig>.self), globalConfig: globalConfig)
}
func createCentrePopupStackView() -> some View {
PopupCentreStackView(items: stack.centre, globalConfig: globalConfig)
PopupCentreStackView(items: getViews(AnyPopup<CentrePopupConfig>.self), globalConfig: globalConfig)
}
func createBottomPopupStackView() -> some View {
PopupBottomStackView(items: stack.bottom, globalConfig: globalConfig)
PopupBottomStackView(items: getViews(AnyPopup<BottomPopupConfig>.self), globalConfig: globalConfig)
}
}
private extension PopupView {
func getViews<T: Popup>(_ type: T.Type) -> [T] { popupManager.views.compactMap { $0 as? T } }
}

private extension PopupView {
func onViewsCountChange(_ x: Any) { zIndex.reshuffle(stack.views.last) }
func onViewsCountChange(_ count: Int) { zIndex.reshuffle(popupManager.views.last) }
}

private extension PopupView {
var isOverlayActive: Bool { !isStackEmpty && !shouldOverlayBeHiddenForCurrentPopup }
var isStackEmpty: Bool { stack.views.isEmpty }
var shouldOverlayBeHiddenForCurrentPopup: Bool { stack.popupsWithoutOverlay.contains(stack.views.last?.id ?? "") }
var isStackEmpty: Bool { popupManager.views.isEmpty }
var shouldOverlayBeHiddenForCurrentPopup: Bool { popupManager.popupsWithoutOverlay.contains(popupManager.views.last?.id ?? "") }
}

private extension PopupView {
var stackAnimation: Animation { stack.presenting ? globalConfig.common.animation.entry : globalConfig.common.animation.removal }
var stackAnimation: Animation { popupManager.presenting ? globalConfig.common.animation.entry : globalConfig.common.animation.removal }
var overlayColour: Color { globalConfig.common.overlayColour }
var overlayAnimation: Animation { .easeInOut(duration: 0.44) }
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Public/Extensions/Public+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public extension View {
/// Initialises the library. Use directly with the view in your @main structure
func implementPopupView(config: (GlobalConfig) -> GlobalConfig = { $0 }) -> some View {
#if os(iOS) || os(macOS)
overlay(PopupView(globalConfig: config(.init())))
frame(maxWidth: .infinity).overlay(PopupView(globalConfig: config(.init())))
#elseif os(tvOS)
PopupView(rootView: self, globalConfig: config(.init()))
#endif
Expand Down

0 comments on commit 7b684a1

Please sign in to comment.