diff --git a/Example/TSAlertController.xcodeproj/project.pbxproj b/Example/TSAlertController.xcodeproj/project.pbxproj index 0c0a4cf..3238df0 100644 --- a/Example/TSAlertController.xcodeproj/project.pbxproj +++ b/Example/TSAlertController.xcodeproj/project.pbxproj @@ -545,9 +545,12 @@ MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -569,9 +572,12 @@ MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_STRICT_CONCURRENCY = minimal; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/Example/Tests/TSAlertControllerTests.swift b/Example/Tests/TSAlertControllerTests.swift index 8adefbe..b265849 100644 --- a/Example/Tests/TSAlertControllerTests.swift +++ b/Example/Tests/TSAlertControllerTests.swift @@ -3,6 +3,8 @@ import XCTest final class TSAlertControllerTests: XCTestCase { + // MARK: - Verifies that TSAlertController initializes with correct title, message, and preferredStyle + func testTSAlertController_WhenInitialized_ShouldSetTitleMessageAndPreferredStyle() { // given let alertController = TSAlertController( @@ -22,6 +24,9 @@ final class TSAlertControllerTests: XCTestCase { XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } + + // MARK: - Verifies that TSAlertController initializes with correct textfields + func testTSAlertController_WhenIntializedWithTextFields_ShouldSetTextFields() { // given let alertController = TSAlertController( @@ -41,6 +46,10 @@ final class TSAlertControllerTests: XCTestCase { XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } + + + // MARK: - Verifies that TSAlertController initializes with the correct actions order and count + func testTSAlertController_WhenInitializedWithTwoButtonsAndAutomaticAxis_ShouldSetCorrectActionsOrderAndCount() { // given let alertController = TSAlertController( @@ -95,6 +104,9 @@ final class TSAlertControllerTests: XCTestCase { } + + + func testTSAlertController_WhenIntlaizedWithAlertStyle_ShouldSetDefaultConfiguration() { // given let alertController = TSAlertController( @@ -205,12 +217,11 @@ final class TSAlertControllerTests: XCTestCase { // 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() { + func testTSAlertController_WhenIntializedWithActionSheetStyle_ShouldSetDefaultConfiguration() { // given let alertController = TSAlertController( title: "The title of the alert", @@ -263,8 +274,26 @@ final class TSAlertControllerTests: XCTestCase { // then let config = alertController.configuration XCTAssertFalse(alertController.options.contains(.interactiveScaleAndDrag)) - XCTAssertEqual(config.prefersGrabberVisible, true) XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) + } + + func testTSAlertController_WhenIntializedWithActionSheetStyle_ShouldSetCorrectViewConfigurationForcibly() { + // given + let alertController = TSAlertController( + title: "The title of the alert", + preferredStyle: .actionSheet + ) + let mockViewController = MockViewController() + + // when + alertController.viewConfiguration.cornerRadius = 25.0 + + mockViewController.present(alertController, animated: true) + + // then + let viewConfig = alertController.viewConfiguration + XCTAssertEqual(viewConfig.cornerRadius.bottomLeft, 0.0) + XCTAssertEqual(viewConfig.cornerRadius.bottomRight, 0.0) XCTAssertEqual(mockViewController.presentViewControllerTarget, alertController) } diff --git a/Sources/TSAlert/TSAlertController.swift b/Sources/TSAlert/TSAlertController.swift index bff41d3..0d1b2fe 100644 --- a/Sources/TSAlert/TSAlertController.swift +++ b/Sources/TSAlert/TSAlertController.swift @@ -236,6 +236,7 @@ public final class TSAlertController: UIViewController { public override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() + initialViewTopY = view.frame.origin.y view.applyRoundCorners(viewConfiguration.cornerRadius) } @@ -413,14 +414,12 @@ public final class TSAlertController: UIViewController { // Registers notifications to handle keyboard appearance and disappearance. private func registerKeyboardNotifications() { - NotificationCenter.default.addObserver(self, - selector: #selector(keyboardWillShow(_:)), - name: UIResponder.keyboardWillShowNotification, - object: nil) - NotificationCenter.default.addObserver(self, - selector: #selector(keyboardWillHide(_:)), - name: UIResponder.keyboardWillHideNotification, - object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillChangeFrame(_:)), + name: UIResponder.keyboardWillChangeFrameNotification, + object: nil + ) } // Registers notifications to handle keyboard appearance and disappearance. @@ -691,7 +690,6 @@ private extension TSAlertController { } } - /// @objc private func handleStretchyDragging(_ gesture: UIPanGestureRecognizer) { let interpolationFactor: CGFloat = 0.025 @@ -729,57 +727,45 @@ private extension TSAlertController { private extension TSAlertController { - // Adjusts the alert’s position when the keyboard appears. - @objc func keyboardWillShow(_ notification: Notification) { - // If this method is not blocked, the alert view's position may animate incorrectly - // when the keyboard suggestion bar appears or disappears. - guard !isKeyboardShown else { return } + @objc func keyboardWillChangeFrame(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect, - let keyboardAnimationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber else { - return - } - - let viewHeight = view.frame.height - let keyboardTopY = keyboardFrame.origin.y - - let duration = keyboardAnimationDuration.doubleValue - let adjustedViewTopY = keyboardTopY - viewConfiguration.spacing.keyboardSpacing - viewHeight - - // Move the alert up only if the spacing is smaller than the configured value. - // If the space between the alert and the keyboard is greater than the configured value, the alert will not move. - if adjustedViewTopY < initialViewTopY { - UIView.animate(withDuration: duration, - delay: 0, - options: .curveEaseIn) { - self.view.frame.origin.y = adjustedViewTopY - } - } + let keyboardAnimationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber + else { return } - isKeyboardShown = true - } - - // Resets the alert’s position when the keyboard disappears. - @objc func keyboardWillHide(_ notification: Notification) { - // If this method is not blocked, the alert view's position may animate incorrectly - // when the keyboard suggestion bar appears or disappears. - guard isKeyboardShown else { return } + let alertViewHeight = view.frame.height // Alert 뷰의 전체 높이 + let keyboardOriginY = keyboardFrame.origin.y // 상위 뷰 기준, 키보드의 상단 y 좌표 - guard let userInfo = notification.userInfo, - let keyboardAnimationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber else { - return - } let duration = keyboardAnimationDuration.doubleValue - - // Ensure the software keyboard is enabled for proper testing (Command + K). - UIView.animate(withDuration: duration, - delay: 0, - options: .curveEaseIn) { - self.view.frame.origin.y = self.initialViewTopY + let adjustedViewTopY = keyboardOriginY - viewConfiguration.spacing.keyboardSpacing - alertViewHeight + // 키보드 상단 y 좌표에서 지정된 간격(keyboardSpacing)을 뺀 뒤, + // Alert 뷰의 전체 높이를 빼면 Alert 뷰의 새로운 y 좌표를 구할 수 있음 + + // 키보드가 나타난 상태일 때 실행 + if isKeyboardShown { + UIView.animate( + withDuration: duration, + delay: 0, + options: .curveEaseIn + ) { + self.view.frame.origin.y = self.initialViewTopY + } completion: { _ in + self.isKeyboardShown = false + } + } else if adjustedViewTopY < initialViewTopY && !isKeyboardShown { + // Move the alert up only if the spacing is smaller than the configured value. + // If the space between the alert and the keyboard is greater than the configured value, the alert will not move. + UIView.animate( + withDuration: duration, + delay: 0, + options: .curveEaseIn + ) { + self.view.frame.origin.y = adjustedViewTopY + } completion: { _ in + self.isKeyboardShown = true + } } - - isKeyboardShown = false } }