From 15ad54fe2e70a2fe698c06d2d36df2e2c3d55cbd Mon Sep 17 00:00:00 2001 From: rlarjsdn3 Date: Wed, 26 Feb 2025 14:25:31 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=EC=97=90=20actionSheet=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/ViewController.swift | 2 +- .../TSAlertPresentationController.swift | 4 ++++ Sources/TSAlert/TSAlertController+Style.swift | 2 +- Sources/TSAlert/TSAlertController.swift | 8 ++++++-- .../DefaultAlertView/DefaultAlertView.swift | 10 +++++----- Sources/Utils/Extensions/UIView+Extension.swift | 10 +++++----- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Example/TSAlertController/Presentation/ViewController.swift b/Example/TSAlertController/Presentation/ViewController.swift index ffd8cd6..cf5bb11 100644 --- a/Example/TSAlertController/Presentation/ViewController.swift +++ b/Example/TSAlertController/Presentation/ViewController.swift @@ -94,7 +94,7 @@ class ViewController: UIViewController { let actionSheet = TSAlertController(title: "What kind of inquiry do you have?", options: [.interactiveScaleAndDrag], - preferredStyle: .floatingSheet) + preferredStyle: .actionSheet) actionSheet.viewConfiguration.margin = .init(buttonLeft: 5, buttonRight: 5) let config = TSButton.Configuration(imageSpacing: 15, diff --git a/Sources/PresentationController/TSAlertPresentationController.swift b/Sources/PresentationController/TSAlertPresentationController.swift index 9eac77f..6e739d9 100644 --- a/Sources/PresentationController/TSAlertPresentationController.swift +++ b/Sources/PresentationController/TSAlertPresentationController.swift @@ -96,6 +96,10 @@ final class TSAlertPresentationController: UIPresentationController { case .alert: presentedView.center(in: containerView) + case .actionSheet: + presentedView.centerX(in: containerView) + presentedView.anchor(bottom: containerView.bottomAnchor, bottomInset: 0) + case .floatingSheet: presentedView.centerX(in: containerView) presentedView.anchor(bottom: containerView.safeAreaLayoutGuide.bottomAnchor, bottomInset: 10) diff --git a/Sources/TSAlert/TSAlertController+Style.swift b/Sources/TSAlert/TSAlertController+Style.swift index 2408152..b02572f 100644 --- a/Sources/TSAlert/TSAlertController+Style.swift +++ b/Sources/TSAlert/TSAlertController+Style.swift @@ -30,7 +30,7 @@ public extension TSAlertController { case alert /// An action sheet displayed by the view controller that presented it. - // case actionSheet + case actionSheet /// An floating sheet displayed by the view controller that presented it. case floatingSheet diff --git a/Sources/TSAlert/TSAlertController.swift b/Sources/TSAlert/TSAlertController.swift index 08835bb..689ea2e 100644 --- a/Sources/TSAlert/TSAlertController.swift +++ b/Sources/TSAlert/TSAlertController.swift @@ -248,7 +248,7 @@ public final class TSAlertController: UIViewController { configuration.exitingTransition = .fadeOut configuration.prefersGrabberVisible = false - case .floatingSheet: + case .actionSheet, .floatingSheet: configuration.enteringTransition = .slideUp configuration.exitingTransition = .slideDown configuration.prefersGrabberVisible = true @@ -265,6 +265,10 @@ public final class TSAlertController: UIViewController { viewConfiguration.size.width = .proportional(minimumRatio: 0.75, maximumRatio: 0.75) viewConfiguration.spacing.keyboardSpacing = 100 + case .actionSheet: + viewConfiguration.size.width = .proportional(minimumRatio: 1.0, maximumRatio: 1.0) + viewConfiguration.spacing.keyboardSpacing = 0 + case .floatingSheet: viewConfiguration.size.width = .proportional(minimumRatio: 0.95, maximumRatio: 0.95) viewConfiguration.spacing.keyboardSpacing = 20 @@ -343,7 +347,7 @@ public final class TSAlertController: UIViewController { // Applies size constraints and positions the alert view within its parent view. private func setupConstraints() { view.applySizeConstraint(with: viewConfiguration.size) - alertView?.fill(to: view) + alertView?.fill(to: view, applySafeAreaGuideInsets: true) } // Sets up visual attributes such as background color, blur effect, border, and shadow. diff --git a/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift b/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift index 86006a7..ec6f941 100644 --- a/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift +++ b/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift @@ -77,11 +77,11 @@ class DefaultAlertView: UIView, TSAlertView { bottomInset: textfieldButtonSpacing) buttonGroupView.anchor(leading: self.leadingAnchor, - trailing: self.trailingAnchor, - bottom: self.bottomAnchor, - leadingInset: viewConfiguration.margin.buttonLeft, - trailingInset: viewConfiguration.margin.buttonRight, - bottomInset: viewConfiguration.margin.buttonBottom) + trailing: self.trailingAnchor, + bottom: self.bottomAnchor, + leadingInset: viewConfiguration.margin.buttonLeft, + trailingInset: viewConfiguration.margin.buttonRight, + bottomInset: viewConfiguration.margin.buttonBottom) let actionsCount = CGFloat(alert.actions.count) let actionHeight: CGFloat = viewConfiguration.buttonHeight diff --git a/Sources/Utils/Extensions/UIView+Extension.swift b/Sources/Utils/Extensions/UIView+Extension.swift index 08d6550..b764f58 100644 --- a/Sources/Utils/Extensions/UIView+Extension.swift +++ b/Sources/Utils/Extensions/UIView+Extension.swift @@ -189,12 +189,12 @@ extension UIView { /// Makes the view fill its parent view by setting constraints to all edges. /// /// - Parameter view: The parent view to be filled. - func fill(to view: UIView) { + func fill(to view: UIView, applySafeAreaGuideInsets: Bool = false) { translatesAutoresizingMaskIntoConstraints = false - anchor(top: view.topAnchor, - leading: view.leadingAnchor, - trailing: view.trailingAnchor, - bottom: view.bottomAnchor, + anchor(top: applySafeAreaGuideInsets ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor, + leading: applySafeAreaGuideInsets ? view.safeAreaLayoutGuide.leadingAnchor : view.leadingAnchor, + trailing: applySafeAreaGuideInsets ? view.safeAreaLayoutGuide.trailingAnchor : view.trailingAnchor, + bottom: applySafeAreaGuideInsets ? view.safeAreaLayoutGuide.bottomAnchor : view.bottomAnchor, topInset: 0, leadingInset: 0, trailingInset: 0, From 1b65e9b710a618726fdd5626f505b884d1f875a5 Mon Sep 17 00:00:00 2001 From: rlarjsdn3 Date: Thu, 6 Mar 2025 13:13:21 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20StretchyDragging=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example/Pods/Pods.xcodeproj/project.pbxproj | 2 + .../project.pbxproj | 2 + .../Presentation/ViewController.swift | 2 +- .../TSAlert/TSAlertController+Options.swift | 12 ++-- Sources/TSAlert/TSAlertController.swift | 61 ++++++++++++++++++- .../Utils/Extensions/UIView+Extension.swift | 29 +++++++++ 6 files changed, 99 insertions(+), 9 deletions(-) diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 9c4ebd8..e04e56f 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -688,6 +688,7 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -788,6 +789,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_INSTALL_OBJC_HEADER = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Example/TSAlertController.xcodeproj/project.pbxproj b/Example/TSAlertController.xcodeproj/project.pbxproj index 2225584..0c0a4cf 100644 --- a/Example/TSAlertController.xcodeproj/project.pbxproj +++ b/Example/TSAlertController.xcodeproj/project.pbxproj @@ -545,6 +545,7 @@ MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; }; @@ -568,6 +569,7 @@ MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; }; diff --git a/Example/TSAlertController/Presentation/ViewController.swift b/Example/TSAlertController/Presentation/ViewController.swift index cf5bb11..6d9538d 100644 --- a/Example/TSAlertController/Presentation/ViewController.swift +++ b/Example/TSAlertController/Presentation/ViewController.swift @@ -93,7 +93,7 @@ class ViewController: UIViewController { @IBAction func showActionSheetWithVariousAppearance(_ sender: Any) { let actionSheet = TSAlertController(title: "What kind of inquiry do you have?", - options: [.interactiveScaleAndDrag], + options: [.stretchyDragging, .dismissOnSwipeDown], preferredStyle: .actionSheet) actionSheet.viewConfiguration.margin = .init(buttonLeft: 5, buttonRight: 5) diff --git a/Sources/TSAlert/TSAlertController+Options.swift b/Sources/TSAlert/TSAlertController+Options.swift index f7d76cb..e7f8e92 100644 --- a/Sources/TSAlert/TSAlertController+Options.swift +++ b/Sources/TSAlert/TSAlertController+Options.swift @@ -24,20 +24,22 @@ import UIKit public extension TSAlertController { - /// struct Options: OptionSet { - /// Adds an interactive scaling and dragging effect to the alert + /// Adds an interactive scaling and dragging effect to the alert. Cannot be applied to `actionSheet`. public static let interactiveScaleAndDrag = Options(rawValue: 1 << 0) - + /// Dismisses the action sheet when dragged downward beyond a certain threshold. public static let dismissOnSwipeDown = Options(rawValue: 1 << 1) - + /// Dismisses the alert by tapping the outside area. public static let dismissOnTapOutside = Options(rawValue: 1 << 2) - + /// Dismisses the alert by tapping the inside area. public static let dismissOnTapInside = Options(rawValue: 1 << 3) + + /// Applies a stretching effect to the alert when dragged. Can only be applied to `actionSheet`. + public static let stretchyDragging = Options(rawValue: 1 << 4) public var rawValue: Int public init(rawValue: Int) { diff --git a/Sources/TSAlert/TSAlertController.swift b/Sources/TSAlert/TSAlertController.swift index 689ea2e..71bda90 100644 --- a/Sources/TSAlert/TSAlertController.swift +++ b/Sources/TSAlert/TSAlertController.swift @@ -64,7 +64,10 @@ import UIKit /// This will apply the properties to the alert buttons. /// For more details, refer to ``TSButton.Configuration``. /// -/// > Contributing: All types of contributions are welcome, from minor typo fixes and comment improvements to adding new features! Bug reports and feature requests are also highly appreciated, and I will actively review them. TSAlertController is continuously updated with the goal of providing an easy-to-use, modern, and elegant alert system for everyone. I truly appreciate your support! 😃 +/// > Contributing: All types of contributions are welcome, from minor typo fixes and comment improvements to adding new features! +/// Bug reports and feature requests are also highly appreciated, and I will actively review them. +/// TSAlertController is continuously updated with the goal of providing an easy-to-use, modern, and elegant alert system for everyone. +/// I truly appreciate your support! 😃 /// public final class TSAlertController: UIViewController { @@ -212,6 +215,7 @@ public final class TSAlertController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() + adjustOptions() adjustConfiguration() adjustViewConfiguration() @@ -275,6 +279,19 @@ public final class TSAlertController: UIViewController { } } + /// Before the alert is displayed on the screen, this method forcibly updates the final `Options` + /// based on `TSAlertController.Style`. + /// This ensures that the alert is presented correctly and prevents unintended behavior. + private func adjustOptions() { + switch preferredStyle { + case .actionSheet: + options.remove(.interactiveScaleAndDrag) + + case .alert, .floatingSheet: + options.remove(.stretchyDragging) + } + } + /// Before the alert is displayed on the screen, this method forcibly updates the final `Configuration` /// based on `TSAlertController.Style`. /// This ensures that the alert is presented correctly and prevents unintended behavior. @@ -418,6 +435,11 @@ public final class TSAlertController: UIViewController { addGestureRecognizerIfNeeded(for: .dismissOnTapInside, gesture: UITapGestureRecognizer(target: self, action: #selector(handleTapInsideToDismissGesture(_:))), to: view) + + addGestureRecognizerIfNeeded(for: .stretchyDragging, + gesture: UIPanGestureRecognizer(target: self, + action: #selector(handleStretchyDragging(_:))), + to: view) } @@ -558,7 +580,6 @@ private extension TSAlertController { guard let presentingView = self.presentingViewController?.view else { return } // - let scaleDownFactor: CGFloat = 0.95 let dismissThreshold: CGFloat = 100 let velocityThreshold: CGFloat = 800 @@ -568,8 +589,10 @@ private extension TSAlertController { switch gesture.state { case .changed: if translation.y > 0 { - self.view.transform = CGAffineTransform(translationX: translation.x * 0.1, y: translation.y) + self.view.transform = CGAffineTransform(translationX: 0, + y: translation.y) + let scaleDownFactor: CGFloat = 0.95 if options.contains(.interactiveScaleAndDrag) { self.view.transform = CGAffineTransform(translationX: translation.x * 0.1, y: translation.y) .scaledBy(x: scaleDownFactor, y: scaleDownFactor) @@ -649,6 +672,38 @@ private extension TSAlertController { break } } + + /// + @objc private func handleStretchyDragging(_ gesture: UIPanGestureRecognizer) { + + let interpolationFactor: CGFloat = 0.025 + let translation = gesture.translation(in: view) + + switch gesture.state { + case .began: + view.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 1.0)) + + case .changed: + if translation.y < 0 { + let adjustedScaleY = 1.0 + (abs(translation.y) * interpolationFactor) / view.bounds.height + view.transform = CGAffineTransform(scaleX: 1.0, y: adjustedScaleY) + } + + case .ended, .cancelled, .failed: + UIView.animate(withDuration: 0.5, + delay: 0, + usingSpringWithDamping: 0.6, + initialSpringVelocity: 0.6, + options: .curveEaseIn, + animations: { + + self.view.transform = .identity + }) + + default: + break + } + } } diff --git a/Sources/Utils/Extensions/UIView+Extension.swift b/Sources/Utils/Extensions/UIView+Extension.swift index b764f58..6394b2a 100644 --- a/Sources/Utils/Extensions/UIView+Extension.swift +++ b/Sources/Utils/Extensions/UIView+Extension.swift @@ -281,3 +281,32 @@ extension UIView { } } } + + + +extension UIView { + + // MARK: - Set AnchorPoint + + /// + func setAnchorPoint(anchorPoint: CGPoint) { + var oldPoint = CGPoint(x: self.bounds.width * self.layer.anchorPoint.x, + y: self.bounds.height * self.layer.anchorPoint.y) + + var newPoint = CGPoint(x: self.bounds.width * anchorPoint.x, + y: self.bounds.height * anchorPoint.y) + + oldPoint = oldPoint.applying(self.transform) + newPoint = newPoint.applying(self.transform) + + var position = self.layer.position + position.x -= oldPoint.x + position.x += newPoint.x + + position.y -= oldPoint.y + position.y += newPoint.y + + self.layer.position = position + self.layer.anchorPoint = anchorPoint + } +} From 970c2552e76878fb6f3198c90dfbd28ff6edc3ff Mon Sep 17 00:00:00 2001 From: rlarjsdn3 Date: Tue, 18 Mar 2025 16:06:16 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20TestCode=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example/Tests/TSAlertControllerTests.swift | 92 +++++++++++++++++----- Sources/TSAlert/TSAlertController.swift | 24 +++--- 2 files changed, 83 insertions(+), 33 deletions(-) diff --git a/Example/Tests/TSAlertControllerTests.swift b/Example/Tests/TSAlertControllerTests.swift index 3d0bf96..8adefbe 100644 --- a/Example/Tests/TSAlertControllerTests.swift +++ b/Example/Tests/TSAlertControllerTests.swift @@ -94,7 +94,8 @@ final class TSAlertControllerTests: XCTestCase { XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } - func testTSAlertController_WhenInitalizedWithFourButtonsAndAutomaticAxis_ShouldSetCorrectActionsOrderAndCount() { + + func testTSAlertController_WhenIntlaizedWithAlertStyle_ShouldSetDefaultConfiguration() { // given let alertController = TSAlertController( title: "The title of the alert", @@ -102,11 +103,20 @@ final class TSAlertControllerTests: XCTestCase { ) let mockViewController = MockViewController() - alertController.addAction(.init(title: "Cancel", style: .cancel)) + // when + mockViewController.present(alertController, animated: true) + + // then + let config = alertController.configuration + XCTAssertEqual(config.enteringTransition, .fadeInAndScaleDown) + XCTAssertEqual(config.exitingTransition, .fadeOut) + XCTAssertEqual(config.headerAnimation, .none) + XCTAssertEqual(config.buttonGroupAnimation, .none) + XCTAssertEqual(config.prefersGrabberVisible, false) + XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } - - func testTSAlertController_WhenIntlaizedWithAlertStyle_ShouldSetDefaultConfiguration() { + func testTSAlertController_WhenIntializedWithAlertStyle_ShouldSetDefaultViewConfiguration() { // given let alertController = TSAlertController( title: "The title of the alert", @@ -118,16 +128,13 @@ final class TSAlertControllerTests: XCTestCase { mockViewController.present(alertController, animated: true) // then - let config = alertController.configuration - XCTAssertEqual(config.enteringTransition, .fadeInAndScaleDown) - XCTAssertEqual(config.exitingTransition, .fadeOut) - XCTAssertEqual(config.headerAnimation, .none) - XCTAssertEqual(config.buttonGroupAnimation, .none) - XCTAssertEqual(config.prefersGrabberVisible, false) + let viewConfig = alertController.viewConfiguration + XCTAssertEqual(viewConfig.size.width, .proportional(minimumRatio: 0.75, maximumRatio: 0.75)) + XCTAssertEqual(viewConfig.spacing.keyboardSpacing, 100) XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } - func testTSAlertController_WhenIntializedWithAlertStyle_SouldSetEnforceCorrectConfiguration() { + func testTSAlertController_WhenIntializedWithAlertStyle_SouldSetCorrectConfigurationForcibly() { // given let alertController = TSAlertController( title: "The title of the alert", @@ -166,7 +173,7 @@ final class TSAlertControllerTests: XCTestCase { XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } - func testTSAlertController_WhenIntilizedWithFloatinSheetStyle_ShouldSetEnforceCorrectConfiguration() { + func testTSAlertController_WhenInitializedWithFloatingSheetStyle_ShouldSetDefaultViewConfiguration() { // given let alertController = TSAlertController( title: "The title of the alert", @@ -178,15 +185,57 @@ final class TSAlertControllerTests: XCTestCase { mockViewController.present(alertController, animated: true) // then - XCTAssertTrue(true) + let viewConfig = alertController.viewConfiguration + XCTAssertEqual(viewConfig.size.width, .proportional(minimumRatio: 0.95, maximumRatio: 0.95)) + XCTAssertEqual(viewConfig.spacing.keyboardSpacing, 20) XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } - func testTSAlertController_WhenIntializedWithAlertStyle_ShouldSetDefaultViewConfiguration() { + func testTSAlertController_WhenIntilizedWithFloatingSheetStyle_ShouldSetCorrectConfigurationForcibly() { // given let alertController = TSAlertController( title: "The title of the alert", - preferredStyle: .alert + preferredStyle: .floatingSheet + ) + let mockViewController = MockViewController() + + // when + mockViewController.present(alertController, animated: true) + + // then + let config = alertController.configuration + XCTAssertFalse(alertController.options.contains(.stretchyDragging)) + XCTAssertEqual(config.prefersGrabberVisible, true) + XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) + XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) + } + + func testTSAlertController_WhenIntializedWithActionSheetStyle_ShouldSetDefaultCconfiguration() { + // given + let alertController = TSAlertController( + title: "The title of the alert", + preferredStyle: .actionSheet + ) + let mockViewController = MockViewController() + + // when + mockViewController.present(alertController, animated: true) + + // then + let config = alertController.configuration + XCTAssertEqual(config.enteringTransition, .slideUp) + XCTAssertEqual(config.exitingTransition, .slideDown) + XCTAssertEqual(config.headerAnimation, .none) + XCTAssertEqual(config.buttonGroupAnimation, .none) + XCTAssertEqual(config.prefersGrabberVisible, true) + XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) + } + + func testTSAlertController_WhenIntializedWithActionSheetStyle_ShouldSetDefaultViewConfiguration() { + // given + let alertController = TSAlertController( + title: "The title of the alert", + preferredStyle: .actionSheet ) let mockViewController = MockViewController() @@ -195,12 +244,12 @@ final class TSAlertControllerTests: XCTestCase { // then let viewConfig = alertController.viewConfiguration - XCTAssertEqual(viewConfig.size.width, .proportional(minimumRatio: 0.75, maximumRatio: 0.75)) - XCTAssertEqual(viewConfig.spacing.keyboardSpacing, 100) + XCTAssertEqual(viewConfig.size.width, .proportional(minimumRatio: 1.0, maximumRatio: 1.0)) + XCTAssertEqual(viewConfig.spacing.keyboardSpacing, 0.0) XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } - func testTSAlertController_WhenInitializedWithFloatingSheetStyle_ShouldSetDefaultViewConfiguration() { + func testTSAlertController_WhenIntializedWithActionSheetStyle_ShouldSetCorrectConfigurationForcibly() { // given let alertController = TSAlertController( title: "The title of the alert", @@ -212,9 +261,10 @@ final class TSAlertControllerTests: XCTestCase { mockViewController.present(alertController, animated: true) // then - let viewConfig = alertController.viewConfiguration - XCTAssertEqual(viewConfig.size.width, .proportional(minimumRatio: 0.95, maximumRatio: 0.95)) - XCTAssertEqual(viewConfig.spacing.keyboardSpacing, 20) + let config = alertController.configuration + XCTAssertFalse(alertController.options.contains(.interactiveScaleAndDrag)) + XCTAssertEqual(config.prefersGrabberVisible, true) + XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } diff --git a/Sources/TSAlert/TSAlertController.swift b/Sources/TSAlert/TSAlertController.swift index 71bda90..a74c405 100644 --- a/Sources/TSAlert/TSAlertController.swift +++ b/Sources/TSAlert/TSAlertController.swift @@ -215,9 +215,9 @@ public final class TSAlertController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() - adjustOptions() - adjustConfiguration() - adjustViewConfiguration() + adjustOptionsForcibly() + adjustConfigurationForcibly() + adjustViewConfigurationForcibly() initializeAlertView() registerKeyboardNotifications() @@ -282,7 +282,7 @@ public final class TSAlertController: UIViewController { /// Before the alert is displayed on the screen, this method forcibly updates the final `Options` /// based on `TSAlertController.Style`. /// This ensures that the alert is presented correctly and prevents unintended behavior. - private func adjustOptions() { + private func adjustOptionsForcibly() { switch preferredStyle { case .actionSheet: options.remove(.interactiveScaleAndDrag) @@ -295,20 +295,20 @@ public final class TSAlertController: UIViewController { /// Before the alert is displayed on the screen, this method forcibly updates the final `Configuration` /// based on `TSAlertController.Style`. /// This ensures that the alert is presented correctly and prevents unintended behavior. - private func adjustConfiguration() { - adjustPrefersGrabberVisible() + private func adjustConfigurationForcibly() { + adjustPrefersGrabberVisibleForcibly() } /// Before the alert is displayed on the screen, this method forcibly updates the final `ViewConfiguration` /// based on `TSAlertController.Style`. /// This ensures that the alert is presented correctly and prevents unintended behavior. - private func adjustViewConfiguration() { - adjustButtonGroupAxis() - adjustActionOrder() + private func adjustViewConfigurationForcibly() { + adjustButtonGroupAxisForcibly() + adjustActionOrderForcibly() } /// - private func adjustPrefersGrabberVisible() { + private func adjustPrefersGrabberVisibleForcibly() { if preferredStyle == .alert { configuration.prefersGrabberVisible = false } @@ -316,7 +316,7 @@ public final class TSAlertController: UIViewController { /// If `buttonLayoutAxis` is set to `.automatic`, the axis is adjusted based on the number of buttons. /// - Parameter axis: The current button layout axis. - private func adjustButtonGroupAxis() { + private func adjustButtonGroupAxisForcibly() { let axis = viewConfiguration.buttonGroupAxis viewConfiguration.buttonGroupAxis = axis.resolvedAxis(for: actions.count) } @@ -325,7 +325,7 @@ public final class TSAlertController: UIViewController { /// - Parameter actions: The list of `TSAlertAction` instances to be reordered. /// /// - TODO: Modify sorting logic based on `UITraitCollectionLayoutDirection` to handle right-to-left layouts properly. - private func adjustActionOrder() { + private func adjustActionOrderForcibly() { actions.sort { let isHorizontal = viewConfiguration.buttonGroupAxis == .horizontal return isHorizontal ? ($0.style == .cancel && $1.style != .cancel)