From 2de87ff6635e11bebb05507f6385592f626f9446 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Mon, 5 Jun 2023 18:20:34 +0900 Subject: [PATCH 01/12] =?UTF-8?q?APP-7009=20:=20customTopView=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/PanModalPresentationController.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index 116b81c2..2861d396 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -136,6 +136,8 @@ public class PanModalPresentationController: UIPresentationController { public override var presentedView: UIView { return panContainerView } + + public var customTopView: UIView? // MARK: - Gesture Recognizers @@ -339,6 +341,13 @@ private extension PanModalPresentationController { if presentable.showDragIndicator { addDragIndicatorView(to: presentedView) } + + if let customTopView = customTopView { + containerView.addSubview(customTopView) + customTopView.translatesAutoresizingMaskIntoConstraints = false + customTopView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true + customTopView.bottomAnchor.constraint(equalTo: dragIndicatorView.topAnchor, constant: -10).isActive = true + } setNeedsLayoutUpdate() adjustPanContainerBackgroundColor() @@ -379,7 +388,6 @@ private extension PanModalPresentationController { backgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true backgroundView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true } - /** Adds the drag indicator view to the view hierarchy & configures its layout constraints. From 91173a7324435b19e2931e319f36d25372a3db26 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Mon, 5 Jun 2023 23:18:12 +0900 Subject: [PATCH 02/12] =?UTF-8?q?APP-7009=20:=20CustomTopView=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PanModalPresentationAnimator.swift | 10 +++++- .../PanModalPresentationController.swift | 22 ++++++++----- .../PanModalPresentable+Defaults.swift | 4 +++ .../Presentable/PanModalPresentable.swift | 2 ++ PanModal/View/CustomTopView.swift | 19 +++++++++++ PanModalDemo.xcodeproj/project.pbxproj | 6 ++++ .../BasicViewController.swift | 32 +++++++++++++++++++ 7 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 PanModal/View/CustomTopView.swift diff --git a/PanModal/Animator/PanModalPresentationAnimator.swift b/PanModal/Animator/PanModalPresentationAnimator.swift index 04f1f3fc..75d81914 100644 --- a/PanModal/Animator/PanModalPresentationAnimator.swift +++ b/PanModal/Animator/PanModalPresentationAnimator.swift @@ -60,13 +60,18 @@ public class PanModalPresentationAnimator: NSObject { // Use panView as presentingView if it already exists within the containerView let panView: UIView = transitionContext.containerView.panContainerView ?? toVC.view + let topView = transitionContext.containerView.customTopView // Move presented view offscreen (from the bottom) panView.frame = transitionContext.finalFrame(for: toVC) panView.frame.origin.y = transitionContext.containerView.frame.height + topView?.alpha = 0 + topView?.frame.origin.y = transitionContext.containerView.frame.height + (presentable?.customTopView?.frame.height ?? 0) PanModalAnimator.animate({ panView.frame.origin.y = yPos + topView?.frame.origin.y = yPos + (presentable?.customTopView?.frame.height ?? 0) + topView?.alpha = 1 }, config: presentable) { didComplete in transitionContext.completeTransition(didComplete) } @@ -82,9 +87,12 @@ public class PanModalPresentationAnimator: NSObject { let presentable = fromVC as? PanModalPresentable.LayoutType let panView: UIView = transitionContext.containerView.panContainerView ?? fromVC.view - + let topView = transitionContext.containerView.customTopView + PanModalAnimator.animate({ panView.frame.origin.y = transitionContext.containerView.frame.height + PanModalPresentationController.Constants.dragIndicatorHeight + topView?.frame.origin.y = transitionContext.containerView.frame.height + PanModalPresentationController.Constants.dragIndicatorHeight - (presentable?.customTopView?.frame.height ?? 0) * 1.5 + topView?.alpha = 0.0 }, config: presentable) { didComplete in fromVC.view.removeFromSuperview() transitionContext.completeTransition(didComplete) diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index 2861d396..f4149b67 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -136,8 +136,6 @@ public class PanModalPresentationController: UIPresentationController { public override var presentedView: UIView { return panContainerView } - - public var customTopView: UIView? // MARK: - Gesture Recognizers @@ -206,6 +204,7 @@ public class PanModalPresentationController: UIPresentationController { override public func presentationTransitionDidEnd(_ completed: Bool) { if completed { return } + presentable?.customTopView?.removeFromSuperview() backgroundView.removeFromSuperview() } @@ -342,12 +341,7 @@ private extension PanModalPresentationController { addDragIndicatorView(to: presentedView) } - if let customTopView = customTopView { - containerView.addSubview(customTopView) - customTopView.translatesAutoresizingMaskIntoConstraints = false - customTopView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true - customTopView.bottomAnchor.constraint(equalTo: dragIndicatorView.topAnchor, constant: -10).isActive = true - } + addCustomTopViewIfExisted(in: containerView) setNeedsLayoutUpdate() adjustPanContainerBackgroundColor() @@ -388,6 +382,18 @@ private extension PanModalPresentationController { backgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true backgroundView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true } + + + func addCustomTopViewIfExisted(in containerView: UIView) { + guard let customTopView = presentable?.customTopView else { return } + containerView.addSubview(customTopView) + customTopView.translatesAutoresizingMaskIntoConstraints = false + customTopView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true + customTopView.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true + customTopView.bottomAnchor.constraint(equalTo: dragIndicatorView.topAnchor, constant: -10).isActive = true + customTopView.heightAnchor.constraint(equalToConstant: customTopView.frame.height).isActive = true + } + /** Adds the drag indicator view to the view hierarchy & configures its layout constraints. diff --git a/PanModal/Presentable/PanModalPresentable+Defaults.swift b/PanModal/Presentable/PanModalPresentable+Defaults.swift index b4bdb886..94bbe52c 100644 --- a/PanModal/Presentable/PanModalPresentable+Defaults.swift +++ b/PanModal/Presentable/PanModalPresentable+Defaults.swift @@ -77,6 +77,10 @@ public extension PanModalPresentable where Self: UIViewController { var showDragIndicator: Bool { return true } + + var customTopView: CustomTopView? { + return nil + } func shouldRespond(to panModalGestureRecognizer: UIPanGestureRecognizer) -> Bool { return true diff --git a/PanModal/Presentable/PanModalPresentable.swift b/PanModal/Presentable/PanModalPresentable.swift index 00cae2ec..82765c03 100644 --- a/PanModal/Presentable/PanModalPresentable.swift +++ b/PanModal/Presentable/PanModalPresentable.swift @@ -134,6 +134,8 @@ public protocol PanModalPresentable { */ var showDragIndicator: Bool { get } + var customTopView: CustomTopView? { get } + /** Asks the delegate if the pan modal should respond to the pan modal gesture recognizer. diff --git a/PanModal/View/CustomTopView.swift b/PanModal/View/CustomTopView.swift new file mode 100644 index 00000000..ba928e9b --- /dev/null +++ b/PanModal/View/CustomTopView.swift @@ -0,0 +1,19 @@ +// +// CustomTopView.swift +// PanModal +// +// Created by Hyun Sik Yoo on 2023/06/05. +// Copyright © 2023 Detail. All rights reserved. +// + +import UIKit + +public class CustomTopView: UIView { + +} + +extension UIView { + var customTopView: CustomTopView? { + return subviews.compactMap({ $0 as? CustomTopView }).first + } +} diff --git a/PanModalDemo.xcodeproj/project.pbxproj b/PanModalDemo.xcodeproj/project.pbxproj index 8e493a2e..e6a7e6e2 100644 --- a/PanModalDemo.xcodeproj/project.pbxproj +++ b/PanModalDemo.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 638A9380256B4DC500A5E00B /* PanModalWrappedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 638A937F256B4DC500A5E00B /* PanModalWrappedViewController.swift */; }; 638A9383256B4E3F00A5E00B /* PanModalWrappedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 638A937F256B4DC500A5E00B /* PanModalWrappedViewController.swift */; }; 638A938D256BB26E00A5E00B /* EmbedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 638A9387256BB21800A5E00B /* EmbedViewController.swift */; }; + 6E763B4C2A2E2313002A77E8 /* CustomTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E763B4B2A2E2313002A77E8 /* CustomTopView.swift */; }; + 6E763B4D2A2E25A2002A77E8 /* CustomTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E763B4B2A2E2313002A77E8 /* CustomTopView.swift */; }; 743CABB02225FC9F00634A5A /* UserGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 743CABAF2225FC9F00634A5A /* UserGroupViewController.swift */; }; 743CABB22225FD1100634A5A /* UserGroupHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 743CABB12225FD1100634A5A /* UserGroupHeaderView.swift */; }; 743CABB42225FE7700634A5A /* UserGroupMemberPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 743CABB32225FE7700634A5A /* UserGroupMemberPresentable.swift */; }; @@ -99,6 +101,7 @@ 0F2A2C542239C119003BDB2F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 638A937F256B4DC500A5E00B /* PanModalWrappedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PanModalWrappedViewController.swift; sourceTree = ""; }; 638A9387256BB21800A5E00B /* EmbedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmbedViewController.swift; sourceTree = ""; }; + 6E763B4B2A2E2313002A77E8 /* CustomTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTopView.swift; sourceTree = ""; }; 743CABAF2225FC9F00634A5A /* UserGroupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserGroupViewController.swift; sourceTree = ""; }; 743CABB12225FD1100634A5A /* UserGroupHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserGroupHeaderView.swift; sourceTree = ""; }; 743CABB32225FE7700634A5A /* UserGroupMemberPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserGroupMemberPresentable.swift; sourceTree = ""; }; @@ -321,6 +324,7 @@ DC13906E216D9458007A3E64 /* DimmedView.swift */, 94795C9C21F03368008045A0 /* PanContainerView.swift */, D134CC7022641B350022AA29 /* IndicatorView.swift */, + 6E763B4B2A2E2313002A77E8 /* CustomTopView.swift */, ); path = View; sourceTree = ""; @@ -541,6 +545,7 @@ 0F2A2C622239C148003BDB2F /* PanModalHeight.swift in Sources */, 0F2A2C632239C14B003BDB2F /* PanModalPresentable.swift in Sources */, 0F2A2C642239C14E003BDB2F /* PanModalPresentable+Defaults.swift in Sources */, + 6E763B4C2A2E2313002A77E8 /* CustomTopView.swift in Sources */, 0F2A2C652239C151003BDB2F /* PanModalPresentable+UIViewController.swift in Sources */, 0F2A2C662239C153003BDB2F /* PanModalPresentable+LayoutHelpers.swift in Sources */, 0F2A2C672239C157003BDB2F /* PanModalPresenter.swift in Sources */, @@ -587,6 +592,7 @@ DC139061216D93ED007A3E64 /* SampleViewController.swift in Sources */, 743CABB02225FC9F00634A5A /* UserGroupViewController.swift in Sources */, 638A938D256BB26E00A5E00B /* EmbedViewController.swift in Sources */, + 6E763B4D2A2E25A2002A77E8 /* CustomTopView.swift in Sources */, DCC0EE7C21917F2500208DBC /* PanModalPresentable+Defaults.swift in Sources */, 94795C9D21F03368008045A0 /* PanContainerView.swift in Sources */, 74C072A7220BA78800124CE1 /* PanModalPresentable+LayoutHelpers.swift in Sources */, diff --git a/Sample/View Controllers/BasicViewController.swift b/Sample/View Controllers/BasicViewController.swift index 2056651f..132eaa19 100644 --- a/Sample/View Controllers/BasicViewController.swift +++ b/Sample/View Controllers/BasicViewController.swift @@ -33,4 +33,36 @@ extension BasicViewController: PanModalPresentable { var anchorModalToLongForm: Bool { return false } + + var customTopView: CustomTopView? { + return TopView() + } +} + +extension BasicViewController { + final class TopView: CustomTopView { + enum Layout { + static let size = CGSize(width: UIScreen.main.bounds.width, height: 50) + } + + private let affiliateSwitch = UISwitch() + + override init(frame: CGRect) { + super.init(frame: .init(origin: .zero, size: Layout.size)) + + setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + addSubview(affiliateSwitch) + + affiliateSwitch.translatesAutoresizingMaskIntoConstraints = false + affiliateSwitch.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true + affiliateSwitch.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + } + } } From 2620cfbf481458108cc73db25d312bb2bde03cc8 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Mon, 5 Jun 2023 23:24:22 +0900 Subject: [PATCH 03/12] =?UTF-8?q?APP-7009=20:=20CustomTopView=20open=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PanModal/View/CustomTopView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PanModal/View/CustomTopView.swift b/PanModal/View/CustomTopView.swift index ba928e9b..130b1329 100644 --- a/PanModal/View/CustomTopView.swift +++ b/PanModal/View/CustomTopView.swift @@ -8,7 +8,7 @@ import UIKit -public class CustomTopView: UIView { +open class CustomTopView: UIView { } From 1628e1d5d5121beca25025e5f1245e2ca83b789c Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Wed, 7 Jun 2023 22:29:59 +0900 Subject: [PATCH 04/12] =?UTF-8?q?APP-7009=20:=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EC=98=81=EC=97=AD=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift b/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift index 88569609..d4a35aca 100644 --- a/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift +++ b/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift @@ -92,7 +92,9 @@ extension PanModalPresentable where Self: UIViewController { } - return container.bounds.size.height - topOffset + let customTopViewHeight = customTopView?.frame.height ?? 0 + + return container.bounds.size.height - topOffset - customTopViewHeight } /** From 81a6381bc33660ad1f269edb3410f08fde06c265 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Mon, 12 Jun 2023 10:40:26 +0900 Subject: [PATCH 05/12] =?UTF-8?q?APP-7009=20:=20Pod=20=EB=B2=84=EC=A0=84?= =?UTF-8?q?=20=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcode/package.xcworkspace/contents.xcworkspacedata | 7 +++++++ PanModal.podspec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/PanModal.podspec b/PanModal.podspec index fff41959..67be67cf 100644 --- a/PanModal.podspec +++ b/PanModal.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'PanModal' - s.version = '1.4.9' + s.version = '1.5.0' s.summary = 'PanModal is an elegant and highly customizable presentation API for constructing bottom sheet modals on iOS.' # This description is used to generate tags and improve search results. From d992af6589fe8c54b9fc83868a9a1a0fe695428e Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Mon, 12 Jun 2023 14:15:01 +0900 Subject: [PATCH 06/12] =?UTF-8?q?APP-7009=20:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20(=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0,=20=EC=98=A4=ED=86=A0=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9,=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=9C=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcode/package.xcworkspace/contents.xcworkspacedata | 7 ------- PanModal/Controller/PanModalPresentationController.swift | 4 ++-- PanModal/View/CustomTopView.swift | 8 -------- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index f4149b67..e0fe7eaa 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -388,8 +388,8 @@ private extension PanModalPresentationController { guard let customTopView = presentable?.customTopView else { return } containerView.addSubview(customTopView) customTopView.translatesAutoresizingMaskIntoConstraints = false - customTopView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true - customTopView.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true + customTopView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true + customTopView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true customTopView.bottomAnchor.constraint(equalTo: dragIndicatorView.topAnchor, constant: -10).isActive = true customTopView.heightAnchor.constraint(equalToConstant: customTopView.frame.height).isActive = true } diff --git a/PanModal/View/CustomTopView.swift b/PanModal/View/CustomTopView.swift index 130b1329..dd954b8e 100644 --- a/PanModal/View/CustomTopView.swift +++ b/PanModal/View/CustomTopView.swift @@ -1,11 +1,3 @@ -// -// CustomTopView.swift -// PanModal -// -// Created by Hyun Sik Yoo on 2023/06/05. -// Copyright © 2023 Detail. All rights reserved. -// - import UIKit open class CustomTopView: UIView { From af7ab6a485386aa5c2558ed3f98685a7f97ab330 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Mon, 12 Jun 2023 17:03:39 +0900 Subject: [PATCH 07/12] =?UTF-8?q?APP-7009=20:=20=ED=95=98=EB=8B=A8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=93=9C=EB=9E=98=EA=B7=B8=20=ED=95=98?= =?UTF-8?q?=EB=8B=A4=EA=B0=80=20=EB=86=93=EC=95=98=EC=9D=84=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0,=20=ED=8A=B8=EB=9E=9C=EC=A7=80=EC=85=98=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PanModal/Controller/PanModalPresentationController.swift | 1 + PanModal/Controller/PanModalWrappedViewController.swift | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index e0fe7eaa..bbd3a6c2 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -644,6 +644,7 @@ private extension PanModalPresentationController { */ func adjust(toYPosition yPos: CGFloat) { presentedView.frame.origin.y = max(yPos, anchoredYPosition) + presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - (presentable?.customTopView?.frame.height ?? 0) * 1.5 guard presentedView.frame.origin.y > shortFormYPosition else { backgroundView.dimState = .max diff --git a/PanModal/Controller/PanModalWrappedViewController.swift b/PanModal/Controller/PanModalWrappedViewController.swift index 7523635c..65f9bab9 100644 --- a/PanModal/Controller/PanModalWrappedViewController.swift +++ b/PanModal/Controller/PanModalWrappedViewController.swift @@ -608,7 +608,8 @@ private extension PanModalWrappedViewController { */ func adjust(toYPosition yPos: CGFloat) { presentedView.frame.origin.y = max(yPos, anchoredYPosition) - + presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - (presentable?.customTopView?.frame.height ?? 0) * 1.5 + guard presentedView.frame.origin.y > shortFormYPosition else { backgroundView.dimState = .max return From 468562554a79217a2148930f3735ba5039def144 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Tue, 13 Jun 2023 11:49:53 +0900 Subject: [PATCH 08/12] =?UTF-8?q?APP-7009=20:=20=ED=8A=B8=EB=9E=9C?= =?UTF-8?q?=EC=A7=80=EC=85=98=20=EC=9C=84=EC=B9=98=20=EC=A0=95=ED=99=95?= =?UTF-8?q?=EB=8F=84=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PanModalPresentationAnimator.swift | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/PanModal/Animator/PanModalPresentationAnimator.swift b/PanModal/Animator/PanModalPresentationAnimator.swift index 75d81914..716b97de 100644 --- a/PanModal/Animator/PanModalPresentationAnimator.swift +++ b/PanModal/Animator/PanModalPresentationAnimator.swift @@ -61,16 +61,23 @@ public class PanModalPresentationAnimator: NSObject { // Use panView as presentingView if it already exists within the containerView let panView: UIView = transitionContext.containerView.panContainerView ?? toVC.view let topView = transitionContext.containerView.customTopView - + let topViewHeight: CGFloat = { + if let topViewHeight = topView?.frame.height { + return topViewHeight + 10 + } else { + return 0 + } + }() + // Move presented view offscreen (from the bottom) panView.frame = transitionContext.finalFrame(for: toVC) - panView.frame.origin.y = transitionContext.containerView.frame.height + panView.frame.origin.y = transitionContext.containerView.frame.height + topViewHeight topView?.alpha = 0 - topView?.frame.origin.y = transitionContext.containerView.frame.height + (presentable?.customTopView?.frame.height ?? 0) + topView?.frame.origin.y = transitionContext.containerView.frame.height PanModalAnimator.animate({ panView.frame.origin.y = yPos - topView?.frame.origin.y = yPos + (presentable?.customTopView?.frame.height ?? 0) + topView?.frame.origin.y = yPos - topViewHeight topView?.alpha = 1 }, config: presentable) { didComplete in transitionContext.completeTransition(didComplete) @@ -88,10 +95,17 @@ public class PanModalPresentationAnimator: NSObject { let presentable = fromVC as? PanModalPresentable.LayoutType let panView: UIView = transitionContext.containerView.panContainerView ?? fromVC.view let topView = transitionContext.containerView.customTopView + let topViewHeight: CGFloat = { + if let topViewHeight = topView?.frame.height { + return topViewHeight + 10 + } else { + return 0 + } + }() PanModalAnimator.animate({ - panView.frame.origin.y = transitionContext.containerView.frame.height + PanModalPresentationController.Constants.dragIndicatorHeight - topView?.frame.origin.y = transitionContext.containerView.frame.height + PanModalPresentationController.Constants.dragIndicatorHeight - (presentable?.customTopView?.frame.height ?? 0) * 1.5 + panView.frame.origin.y = transitionContext.containerView.frame.height + PanModalPresentationController.Constants.dragIndicatorHeight + topViewHeight + topView?.frame.origin.y = transitionContext.containerView.frame.height topView?.alpha = 0.0 }, config: presentable) { didComplete in fromVC.view.removeFromSuperview() From 1b82b644e06fd557ecfb4b40d66cc15e0051c6c7 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Tue, 13 Jun 2023 12:28:09 +0900 Subject: [PATCH 09/12] =?UTF-8?q?APP-7009=20:=20=ED=8A=B8=EB=9E=9C?= =?UTF-8?q?=EC=A7=80=EC=85=98=20=EA=B4=80=EB=A0=A8=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EA=B0=92=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Animator/PanModalPresentationAnimator.swift | 4 ++-- .../Controller/PanModalPresentationController.swift | 13 +++++++++++-- .../Controller/PanModalWrappedViewController.swift | 11 ++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/PanModal/Animator/PanModalPresentationAnimator.swift b/PanModal/Animator/PanModalPresentationAnimator.swift index 716b97de..9df0fbfc 100644 --- a/PanModal/Animator/PanModalPresentationAnimator.swift +++ b/PanModal/Animator/PanModalPresentationAnimator.swift @@ -63,7 +63,7 @@ public class PanModalPresentationAnimator: NSObject { let topView = transitionContext.containerView.customTopView let topViewHeight: CGFloat = { if let topViewHeight = topView?.frame.height { - return topViewHeight + 10 + return topViewHeight + PanModalPresentationController.Constants.customTopViewOffset } else { return 0 } @@ -97,7 +97,7 @@ public class PanModalPresentationAnimator: NSObject { let topView = transitionContext.containerView.customTopView let topViewHeight: CGFloat = { if let topViewHeight = topView?.frame.height { - return topViewHeight + 10 + return topViewHeight + PanModalPresentationController.Constants.customTopViewOffset } else { return 0 } diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index bbd3a6c2..4c109680 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -38,6 +38,7 @@ public class PanModalPresentationController: UIPresentationController { struct Constants { static let snapMovementSensitivity = CGFloat(0.7) static let dragIndicatorHeight = CGFloat(16) + static let customTopViewOffset = CGFloat(10) } // MARK: - Properties @@ -390,7 +391,7 @@ private extension PanModalPresentationController { customTopView.translatesAutoresizingMaskIntoConstraints = false customTopView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true customTopView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true - customTopView.bottomAnchor.constraint(equalTo: dragIndicatorView.topAnchor, constant: -10).isActive = true + customTopView.bottomAnchor.constraint(equalTo: dragIndicatorView.topAnchor, constant: -Constants.customTopViewOffset).isActive = true customTopView.heightAnchor.constraint(equalToConstant: customTopView.frame.height).isActive = true } @@ -643,8 +644,16 @@ private extension PanModalPresentationController { Sets the y position of the presentedView & adjusts the backgroundView. */ func adjust(toYPosition yPos: CGFloat) { + let topViewHeight: CGFloat = { + if let topViewHeight = presentable?.customTopView?.frame.height { + return topViewHeight + Constants.customTopViewOffset + } else { + return 0 + } + }() + presentedView.frame.origin.y = max(yPos, anchoredYPosition) - presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - (presentable?.customTopView?.frame.height ?? 0) * 1.5 + presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - topViewHeight - PanModalPresentationController.Constants.dragIndicatorHeight guard presentedView.frame.origin.y > shortFormYPosition else { backgroundView.dimState = .max diff --git a/PanModal/Controller/PanModalWrappedViewController.swift b/PanModal/Controller/PanModalWrappedViewController.swift index 65f9bab9..58cdd00c 100644 --- a/PanModal/Controller/PanModalWrappedViewController.swift +++ b/PanModal/Controller/PanModalWrappedViewController.swift @@ -12,6 +12,7 @@ public class PanModalWrappedViewController: UIViewController { struct Constants { static let snapMovementSensitivity = CGFloat(0.7) static let dragIndicatorHeight = CGFloat(16) + static let customTopViewOffset = CGFloat(10) } // MARK: - Properties @@ -607,8 +608,16 @@ private extension PanModalWrappedViewController { Sets the y position of the presentedView & adjusts the backgroundView. */ func adjust(toYPosition yPos: CGFloat) { + let topViewHeight: CGFloat = { + if let topViewHeight = presentable?.customTopView?.frame.height { + return topViewHeight + Constants.customTopViewOffset + } else { + return 0 + } + }() + presentedView.frame.origin.y = max(yPos, anchoredYPosition) - presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - (presentable?.customTopView?.frame.height ?? 0) * 1.5 + presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - topViewHeight - PanModalPresentationController.Constants.dragIndicatorHeight guard presentedView.frame.origin.y > shortFormYPosition else { backgroundView.dimState = .max From 9548902c07ea3bd0bf336a8ae68a0a451e13c580 Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Tue, 13 Jun 2023 12:36:54 +0900 Subject: [PATCH 10/12] =?UTF-8?q?APP-7009=20:=20=EC=98=88=EC=A0=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BasicViewController.swift | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/Sample/View Controllers/BasicViewController.swift b/Sample/View Controllers/BasicViewController.swift index 132eaa19..4360d72d 100644 --- a/Sample/View Controllers/BasicViewController.swift +++ b/Sample/View Controllers/BasicViewController.swift @@ -9,6 +9,7 @@ import UIKit class BasicViewController: UIViewController { + private let topView = TopView() override func viewDidLoad() { super.viewDidLoad() @@ -35,7 +36,7 @@ extension BasicViewController: PanModalPresentable { } var customTopView: CustomTopView? { - return TopView() + return topView } } @@ -45,8 +46,24 @@ extension BasicViewController { static let size = CGSize(width: UIScreen.main.bounds.width, height: 50) } + private let containerView: UIView = { + let containerView = UIView() + containerView.backgroundColor = .white + containerView.layer.cornerRadius = 12 + containerView.layer.masksToBounds = true + + return containerView + }() + private let affiliateSwitch = UISwitch() + private let titleLabel: UILabel = { + let label = UILabel() + label.text = "CustomTopView Example" + label.textColor = .black + return label + }() + override init(frame: CGRect) { super.init(frame: .init(origin: .zero, size: Layout.size)) @@ -58,11 +75,22 @@ extension BasicViewController { } private func setupUI() { - addSubview(affiliateSwitch) + containerView.addSubview(titleLabel) + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16).isActive = true + titleLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true + containerView.addSubview(affiliateSwitch) affiliateSwitch.translatesAutoresizingMaskIntoConstraints = false - affiliateSwitch.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true - affiliateSwitch.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + affiliateSwitch.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 10).isActive = true + affiliateSwitch.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true + + addSubview(containerView) + containerView.translatesAutoresizingMaskIntoConstraints = false + containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true + containerView.topAnchor.constraint(equalTo: topAnchor).isActive = true + containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true + containerView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true } } } From 785d08cb2180be04f5b5dcc47d5f3dfacc457c7b Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Tue, 13 Jun 2023 15:10:29 +0900 Subject: [PATCH 11/12] =?UTF-8?q?APP-7009=20:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20customTopView=20->=20panCustom?= =?UTF-8?q?TopView?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PanModal/Controller/PanModalPresentationController.swift | 8 ++++---- PanModal/Controller/PanModalWrappedViewController.swift | 4 ++-- PanModal/Presentable/PanModalPresentable+Defaults.swift | 2 +- .../Presentable/PanModalPresentable+LayoutHelpers.swift | 2 +- PanModal/Presentable/PanModalPresentable.swift | 2 +- Sample/View Controllers/BasicViewController.swift | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index 4c109680..a74d8f27 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -205,7 +205,7 @@ public class PanModalPresentationController: UIPresentationController { override public func presentationTransitionDidEnd(_ completed: Bool) { if completed { return } - presentable?.customTopView?.removeFromSuperview() + presentable?.panCustomTopView?.removeFromSuperview() backgroundView.removeFromSuperview() } @@ -386,7 +386,7 @@ private extension PanModalPresentationController { func addCustomTopViewIfExisted(in containerView: UIView) { - guard let customTopView = presentable?.customTopView else { return } + guard let customTopView = presentable?.panCustomTopView else { return } containerView.addSubview(customTopView) customTopView.translatesAutoresizingMaskIntoConstraints = false customTopView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true @@ -645,7 +645,7 @@ private extension PanModalPresentationController { */ func adjust(toYPosition yPos: CGFloat) { let topViewHeight: CGFloat = { - if let topViewHeight = presentable?.customTopView?.frame.height { + if let topViewHeight = presentable?.panCustomTopView?.frame.height { return topViewHeight + Constants.customTopViewOffset } else { return 0 @@ -653,7 +653,7 @@ private extension PanModalPresentationController { }() presentedView.frame.origin.y = max(yPos, anchoredYPosition) - presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - topViewHeight - PanModalPresentationController.Constants.dragIndicatorHeight + presentable?.panCustomTopView?.frame.origin.y = max(yPos, anchoredYPosition) - topViewHeight - PanModalPresentationController.Constants.dragIndicatorHeight guard presentedView.frame.origin.y > shortFormYPosition else { backgroundView.dimState = .max diff --git a/PanModal/Controller/PanModalWrappedViewController.swift b/PanModal/Controller/PanModalWrappedViewController.swift index 58cdd00c..f9ae542a 100644 --- a/PanModal/Controller/PanModalWrappedViewController.swift +++ b/PanModal/Controller/PanModalWrappedViewController.swift @@ -609,7 +609,7 @@ private extension PanModalWrappedViewController { */ func adjust(toYPosition yPos: CGFloat) { let topViewHeight: CGFloat = { - if let topViewHeight = presentable?.customTopView?.frame.height { + if let topViewHeight = presentable?.panCustomTopView?.frame.height { return topViewHeight + Constants.customTopViewOffset } else { return 0 @@ -617,7 +617,7 @@ private extension PanModalWrappedViewController { }() presentedView.frame.origin.y = max(yPos, anchoredYPosition) - presentable?.customTopView?.frame.origin.y = max(yPos, anchoredYPosition) - topViewHeight - PanModalPresentationController.Constants.dragIndicatorHeight + presentable?.panCustomTopView?.frame.origin.y = max(yPos, anchoredYPosition) - topViewHeight - PanModalPresentationController.Constants.dragIndicatorHeight guard presentedView.frame.origin.y > shortFormYPosition else { backgroundView.dimState = .max diff --git a/PanModal/Presentable/PanModalPresentable+Defaults.swift b/PanModal/Presentable/PanModalPresentable+Defaults.swift index 94bbe52c..c50ec30b 100644 --- a/PanModal/Presentable/PanModalPresentable+Defaults.swift +++ b/PanModal/Presentable/PanModalPresentable+Defaults.swift @@ -78,7 +78,7 @@ public extension PanModalPresentable where Self: UIViewController { return true } - var customTopView: CustomTopView? { + var panCustomTopView: CustomTopView? { return nil } diff --git a/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift b/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift index d4a35aca..51ecbf4b 100644 --- a/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift +++ b/PanModal/Presentable/PanModalPresentable+LayoutHelpers.swift @@ -92,7 +92,7 @@ extension PanModalPresentable where Self: UIViewController { } - let customTopViewHeight = customTopView?.frame.height ?? 0 + let customTopViewHeight = panCustomTopView?.frame.height ?? 0 return container.bounds.size.height - topOffset - customTopViewHeight } diff --git a/PanModal/Presentable/PanModalPresentable.swift b/PanModal/Presentable/PanModalPresentable.swift index 82765c03..cd70da81 100644 --- a/PanModal/Presentable/PanModalPresentable.swift +++ b/PanModal/Presentable/PanModalPresentable.swift @@ -134,7 +134,7 @@ public protocol PanModalPresentable { */ var showDragIndicator: Bool { get } - var customTopView: CustomTopView? { get } + var panCustomTopView: CustomTopView? { get } /** Asks the delegate if the pan modal should respond to the pan modal gesture recognizer. diff --git a/Sample/View Controllers/BasicViewController.swift b/Sample/View Controllers/BasicViewController.swift index 4360d72d..27099988 100644 --- a/Sample/View Controllers/BasicViewController.swift +++ b/Sample/View Controllers/BasicViewController.swift @@ -35,7 +35,7 @@ extension BasicViewController: PanModalPresentable { return false } - var customTopView: CustomTopView? { + var panCustomTopView: CustomTopView? { return topView } } From 8c201405f35601c107b40e4f5799a55d68dafebb Mon Sep 17 00:00:00 2001 From: Yoo Hyun Sik Date: Tue, 13 Jun 2023 15:17:58 +0900 Subject: [PATCH 12/12] =?UTF-8?q?APP-7009=20:=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=ED=83=91=EB=B7=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20prefix=20Pan=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PanModal/Animator/PanModalPresentationAnimator.swift | 4 ++-- PanModal/Presentable/PanModalPresentable+Defaults.swift | 2 +- PanModal/Presentable/PanModalPresentable.swift | 2 +- PanModal/View/CustomTopView.swift | 6 +++--- Sample/View Controllers/BasicViewController.swift | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PanModal/Animator/PanModalPresentationAnimator.swift b/PanModal/Animator/PanModalPresentationAnimator.swift index 9df0fbfc..18feed0e 100644 --- a/PanModal/Animator/PanModalPresentationAnimator.swift +++ b/PanModal/Animator/PanModalPresentationAnimator.swift @@ -60,7 +60,7 @@ public class PanModalPresentationAnimator: NSObject { // Use panView as presentingView if it already exists within the containerView let panView: UIView = transitionContext.containerView.panContainerView ?? toVC.view - let topView = transitionContext.containerView.customTopView + let topView = transitionContext.containerView.panCustomTopView let topViewHeight: CGFloat = { if let topViewHeight = topView?.frame.height { return topViewHeight + PanModalPresentationController.Constants.customTopViewOffset @@ -94,7 +94,7 @@ public class PanModalPresentationAnimator: NSObject { let presentable = fromVC as? PanModalPresentable.LayoutType let panView: UIView = transitionContext.containerView.panContainerView ?? fromVC.view - let topView = transitionContext.containerView.customTopView + let topView = transitionContext.containerView.panCustomTopView let topViewHeight: CGFloat = { if let topViewHeight = topView?.frame.height { return topViewHeight + PanModalPresentationController.Constants.customTopViewOffset diff --git a/PanModal/Presentable/PanModalPresentable+Defaults.swift b/PanModal/Presentable/PanModalPresentable+Defaults.swift index c50ec30b..7ca5d029 100644 --- a/PanModal/Presentable/PanModalPresentable+Defaults.swift +++ b/PanModal/Presentable/PanModalPresentable+Defaults.swift @@ -78,7 +78,7 @@ public extension PanModalPresentable where Self: UIViewController { return true } - var panCustomTopView: CustomTopView? { + var panCustomTopView: PanCustomTopView? { return nil } diff --git a/PanModal/Presentable/PanModalPresentable.swift b/PanModal/Presentable/PanModalPresentable.swift index cd70da81..eb4fef53 100644 --- a/PanModal/Presentable/PanModalPresentable.swift +++ b/PanModal/Presentable/PanModalPresentable.swift @@ -134,7 +134,7 @@ public protocol PanModalPresentable { */ var showDragIndicator: Bool { get } - var panCustomTopView: CustomTopView? { get } + var panCustomTopView: PanCustomTopView? { get } /** Asks the delegate if the pan modal should respond to the pan modal gesture recognizer. diff --git a/PanModal/View/CustomTopView.swift b/PanModal/View/CustomTopView.swift index dd954b8e..e0b5f83b 100644 --- a/PanModal/View/CustomTopView.swift +++ b/PanModal/View/CustomTopView.swift @@ -1,11 +1,11 @@ import UIKit -open class CustomTopView: UIView { +open class PanCustomTopView: UIView { } extension UIView { - var customTopView: CustomTopView? { - return subviews.compactMap({ $0 as? CustomTopView }).first + var panCustomTopView: PanCustomTopView? { + return subviews.compactMap({ $0 as? PanCustomTopView }).first } } diff --git a/Sample/View Controllers/BasicViewController.swift b/Sample/View Controllers/BasicViewController.swift index 27099988..13bdb257 100644 --- a/Sample/View Controllers/BasicViewController.swift +++ b/Sample/View Controllers/BasicViewController.swift @@ -35,13 +35,13 @@ extension BasicViewController: PanModalPresentable { return false } - var panCustomTopView: CustomTopView? { + var panCustomTopView: PanCustomTopView? { return topView } } extension BasicViewController { - final class TopView: CustomTopView { + final class TopView: PanCustomTopView { enum Layout { static let size = CGSize(width: UIScreen.main.bounds.width, height: 50) }