diff --git a/PanModal/Controller/PanModalPresentationController.swift b/PanModal/Controller/PanModalPresentationController.swift index 11fb2a6e..cbdae6ca 100644 --- a/PanModal/Controller/PanModalPresentationController.swift +++ b/PanModal/Controller/PanModalPresentationController.swift @@ -111,9 +111,21 @@ open class PanModalPresentationController: UIPresentationController { } else { view = DimmedView() } - view.didTap = { [weak self] _ in - if self?.presentable?.allowsTapToDismiss == true { - self?.presentedViewController.dismiss(animated: true) + + if let backgroundInteraction = self.presentable?.backgroundInteraction { + switch backgroundInteraction { + case .forward: + view.hitTestHandler = { [weak self] (point, event) in + return self?.presentingViewController.view.hitTest(point, with: event) + } + + case .dismiss: + view.didTap = { [weak self] _ in + self?.presentedViewController.dismiss(animated: true) + } + + default: + break } } return view diff --git a/PanModal/Presentable/PanModalBackgroundInteraction.swift b/PanModal/Presentable/PanModalBackgroundInteraction.swift new file mode 100644 index 00000000..064ef709 --- /dev/null +++ b/PanModal/Presentable/PanModalBackgroundInteraction.swift @@ -0,0 +1,23 @@ +// +// PanModelBackgroundInteraction.swift +// PanModal +// +// Created by Ilya Kharlamov on 19.08.2020. +// Copyright © 2020 Detail. All rights reserved. +// + +import Foundation + +/** Describes the user interaction events that are triggered as the user taps the background */ +public enum PanModalBackgroundInteraction: Equatable { + + /** Taps dismiss the modal immediately */ + case dismiss + + /** Touches are forwarded to the lower window (In most cases it would be the application main window that will handle it */ + case forward + + /** Absorbs touches. The modal does nothing (Swallows the touch) */ + case none + +} diff --git a/PanModal/Presentable/PanModalPresentable+Defaults.swift b/PanModal/Presentable/PanModalPresentable+Defaults.swift index 76a0679f..55a65676 100644 --- a/PanModal/Presentable/PanModalPresentable+Defaults.swift +++ b/PanModal/Presentable/PanModalPresentable+Defaults.swift @@ -80,6 +80,10 @@ public extension PanModalPresentable where Self: UIViewController { var allowsTapToDismiss: Bool { return true } + + var backgroundInteraction: PanModalBackgroundInteraction { + return self.allowsTapToDismiss ? .dismiss : .none + } var isUserInteractionEnabled: Bool { return true diff --git a/PanModal/Presentable/PanModalPresentable.swift b/PanModal/Presentable/PanModalPresentable.swift index 76c15015..db8114a0 100644 --- a/PanModal/Presentable/PanModalPresentable.swift +++ b/PanModal/Presentable/PanModalPresentable.swift @@ -138,10 +138,19 @@ public protocol PanModalPresentable: AnyObject { /** A flag to determine if dismissal should be initiated when tapping on the dimmed background view. + - Note: This parameter id deprecated. Use `backgroundInteraction` instead. + Default value is true. */ var allowsTapToDismiss: Bool { get } + /** + Describes what happens when the user interacts the background view. + + Default value is .dismiss. + */ + var backgroundInteraction: PanModalBackgroundInteraction { get } + /** A flag to toggle user interactions on the container view. diff --git a/PanModal/View/DimmedView.swift b/PanModal/View/DimmedView.swift index 6fdd1d8b..a3400483 100644 --- a/PanModal/View/DimmedView.swift +++ b/PanModal/View/DimmedView.swift @@ -40,11 +40,24 @@ public class DimmedView: UIView { } } } + + /** + The closure to be executed on hitTest + */ + var hitTestHandler: ((_ point: CGPoint, _ event: UIEvent?) -> UIView?)? /** The closure to be executed when a tap occurs */ - var didTap: ((_ recognizer: UIGestureRecognizer) -> Void)? + var didTap: ((_ recognizer: UIGestureRecognizer) -> Void)? { + didSet { + if self.didTap != nil { + addGestureRecognizer(tapGesture) + } else { + removeGestureRecognizer(tapGesture) + } + } + } /** Tap gesture recognizer @@ -59,7 +72,6 @@ public class DimmedView: UIView { super.init(frame: .zero) alpha = 0.0 backgroundColor = dimColor - addGestureRecognizer(tapGesture) } required public init?(coder aDecoder: NSCoder) { @@ -67,7 +79,11 @@ public class DimmedView: UIView { } // MARK: - Event Handlers - + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return self.hitTestHandler?(point, event) ?? super.hitTest(point, with: event) + } + @objc private func didTapView() { didTap?(tapGesture) } diff --git a/PanModalDemo.xcodeproj/project.pbxproj b/PanModalDemo.xcodeproj/project.pbxproj index 0fe4e141..a9d4e909 100644 --- a/PanModalDemo.xcodeproj/project.pbxproj +++ b/PanModalDemo.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ DC3B2EBE222A58C9000C8A4A /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3B2EBD222A58C9000C8A4A /* AlertView.swift */; }; DCA741AE216D90410021F2F2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA741AD216D90410021F2F2 /* AppDelegate.swift */; }; DCC0EE7C21917F2500208DBC /* PanModalPresentable+Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC0EE7B21917F2500208DBC /* PanModalPresentable+Defaults.swift */; }; + F31828BE24ED570600E3867B /* PanModalBackgroundInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31828BD24ED570600E3867B /* PanModalBackgroundInteraction.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -130,6 +131,7 @@ DCA741AD216D90410021F2F2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; DCA741B9216D90420021F2F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DCC0EE7B21917F2500208DBC /* PanModalPresentable+Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PanModalPresentable+Defaults.swift"; sourceTree = ""; }; + F31828BD24ED570600E3867B /* PanModalBackgroundInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanModalBackgroundInteraction.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -300,6 +302,7 @@ isa = PBXGroup; children = ( 74C072A4220BA76D00124CE1 /* PanModalHeight.swift */, + F31828BD24ED570600E3867B /* PanModalBackgroundInteraction.swift */, DC139068216D9458007A3E64 /* PanModalPresentable.swift */, DCC0EE7B21917F2500208DBC /* PanModalPresentable+Defaults.swift */, DC139069216D9458007A3E64 /* PanModalPresentable+UIViewController.swift */, @@ -531,6 +534,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F31828BE24ED570600E3867B /* PanModalBackgroundInteraction.swift in Sources */, 0F2A2C5E2239C137003BDB2F /* PanModalAnimator.swift in Sources */, 0F2A2C5F2239C139003BDB2F /* PanModalPresentationAnimator.swift in Sources */, 0F2A2C602239C13C003BDB2F /* PanModalPresentationController.swift in Sources */, diff --git a/Tests/PanModalTests.swift b/Tests/PanModalTests.swift index 7652b55f..efd03c3d 100644 --- a/Tests/PanModalTests.swift +++ b/Tests/PanModalTests.swift @@ -55,6 +55,7 @@ class PanModalTests: XCTestCase { XCTAssertEqual(vc.allowsExtendedPanScrolling, false) XCTAssertEqual(vc.allowsDragToDismiss, true) XCTAssertEqual(vc.allowsTapToDismiss, true) + XCTAssertEqual(vc.backgroundInteraction, PanModalBackgroundInteraction.dismiss) XCTAssertEqual(vc.isUserInteractionEnabled, true) XCTAssertEqual(vc.isHapticFeedbackEnabled, true) XCTAssertEqual(vc.shouldRoundTopCorners, false)