diff --git a/SWindow.xcodeproj/project.xcworkspace/xcuserdata/shial.xcuserdatad/UserInterfaceState.xcuserstate b/SWindow.xcodeproj/project.xcworkspace/xcuserdata/shial.xcuserdatad/UserInterfaceState.xcuserstate index 1fe4408..af05629 100644 Binary files a/SWindow.xcodeproj/project.xcworkspace/xcuserdata/shial.xcuserdatad/UserInterfaceState.xcuserstate and b/SWindow.xcodeproj/project.xcworkspace/xcuserdata/shial.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SWindow.xcodeproj/xcuserdata/shial.xcuserdatad/xcschemes/SWindow.xcscheme b/SWindow.xcodeproj/xcuserdata/shial.xcuserdatad/xcschemes/SWindow.xcscheme index a533ed9..34e4264 100644 --- a/SWindow.xcodeproj/xcuserdata/shial.xcuserdatad/xcschemes/SWindow.xcscheme +++ b/SWindow.xcodeproj/xcuserdata/shial.xcuserdatad/xcschemes/SWindow.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/SWindow/SWindow.swift b/SWindow/SWindow.swift index c6b1f98..90291e0 100644 --- a/SWindow/SWindow.swift +++ b/SWindow/SWindow.swift @@ -23,7 +23,7 @@ public enum SModalStatus { } public class SModal { - static let modalWindow: UIWindow = { + public static let modalWindow: UIWindow = { let window = UIWindow(frame: UIScreen.main.bounds) window.backgroundColor = .clear window.windowLevel = windowLevel @@ -31,15 +31,31 @@ public class SModal { return window }() - static var stack: [SModalPresentation] = [] + public static var stack: [SModalPresentation] = [] - static var windowLevel: UIWindowLevel { + public static var shouldMakeKey: Bool { + return false + } + + public static var windowLevel: UIWindowLevel { return UIWindowLevelAlert - 1 } - static var animationDuration: TimeInterval { + public static var animationDuration: TimeInterval { return 0.2 } + + fileprivate static func dropRootViewController() { + modalWindow.isHidden = true + modalWindow.rootViewController = nil + modalWindow.resignKey() + } + + fileprivate static func makeKey() { + if shouldMakeKey { + SModal.modalWindow.makeKey() + } + } } public protocol SModalStack { @@ -79,29 +95,29 @@ extension SModalPresentation { } extension SModalPresentation where Self: UIViewController { - func sReplace(with controller: T, animated: Bool, completion: (() -> Void)?) where T: SModalPresentation { + public func sReplace(with controller: T, animated: Bool = false, completion: (() -> Void)? = nil) where T: SModalPresentation { if animated { SModal.modalWindow.isHidden = false UIView.animate(withDuration: SModal.animationDuration, animations: { SModal.modalWindow.rootViewController = controller - SModal.modalWindow.makeKey() + SModal.makeKey() SModal.modalWindow.alpha = 1 }, completion: { completed in completion?() }) } else { SModal.modalWindow.rootViewController = controller - SModal.modalWindow.makeKey() + SModal.makeKey() SModal.modalWindow.isHidden = false completion?() } } - func sPresent(animated: Bool, completion: (() -> Void)?) { + public func sPresent(animated: Bool = false, completion: (() -> Void)? = nil) { guard let currentPresented = SModal.modalWindow.rootViewController else { SModal.modalWindow.rootViewController = self - SModal.modalWindow.makeKey() + SModal.makeKey() if animated { SModal.modalWindow.alpha = 0 SModal.modalWindow.isHidden = false @@ -116,40 +132,39 @@ extension SModalPresentation where Self: UIViewController { } return } - if let sModalController = currentPresented as? Self, sModalController.canDismiss == true { - sModalController.sReplace(with: self, animated: animated, completion: completion) + SModal.stack.append(self) + if (currentPresented as? SModalPresentation)?.canDismiss == true { + (currentPresented as? SModalPresentation)?.sWithdraw(animated: animated, completion: completion) } else { - SModal.stack.append(self) + completion?() } } - func sWithdraw(animated: Bool, completion: (() -> Void)?) { + public func sWithdraw(animated: Bool = false, completion: (() -> Void)? = nil) { + func completedProcedure() { + if let controller = SModal.stack.sorted(by: {$0.priority > $1.priority}).first { + SModal.stack = SModal.stack.filter({ $0 !== controller }) + controller.sPresent(animated: animated, completion: completion) + }else { + completion?() + } + } + if SModal.modalWindow.rootViewController === self { if animated { SModal.modalWindow.alpha = 1 UIView.animate(withDuration: SModal.animationDuration, animations: { SModal.modalWindow.alpha = 0 }, completion: { completed in - SModal.modalWindow.isHidden = true - completion?() + SModal.dropRootViewController() + completedProcedure() }) } else { - SModal.modalWindow.isHidden = true - completion?() + SModal.dropRootViewController() + completedProcedure() } - SModal.modalWindow.rootViewController = nil - SModal.modalWindow.resignKey() } else if SModal.stack.contains(where: { $0 === self }) { SModal.stack = SModal.stack.filter() { !($0 === self) } } - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + SModal.animationDuration) { - if let controller = SModal.stack.sorted(by: {$0.priority > $1.priority}).first { - SModal.stack.removeFirst() - controller.sPresent(animated: true, completion: nil) - } - } } } - - - diff --git a/SWindowTests/SWindowTests.swift b/SWindowTests/SWindowTests.swift index 666b1b0..2b8e37f 100644 --- a/SWindowTests/SWindowTests.swift +++ b/SWindowTests/SWindowTests.swift @@ -13,23 +13,166 @@ class SWindowTests: XCTestCase { override func setUp() { super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func testLowPriority() { + XCTAssert(SModalPriority.Low == 250) } - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. + func testRequiredPriority() { + XCTAssert(SModalPriority.Required == 500) + } + + func testHighPriority() { + XCTAssert(SModalPriority.High == 750) + } + + func testSModalWindow() { + XCTAssertNotNil(SModal.modalWindow) + } + + func testSModalWindowLevel() { + XCTAssert(SModal.windowLevel == UIWindowLevelAlert - 1) + } + + func testSModalWindowStack() { + XCTAssertNotNil(SModal.stack) + } + + func testSModalDuration() { + XCTAssertEqualWithAccuracy(SModal.animationDuration, 0.2, accuracy: 0.01) + } + + func testModalCanDismiss() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + XCTAssert(!controller.canDismiss) + } + + func testModalStatusNone() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + XCTAssert(controller.modalStatus == .none) + } + + func testModalStatusPresented() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + SModal.modalWindow.rootViewController = controller + XCTAssert(controller.modalStatus == .presented) + } + + func testModalStatusWaitingInStack() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + SModal.stack.append(controller) + XCTAssert(controller.modalStatus == .waitingInStack) + } + + func testModalStatusStack() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + XCTAssertNotNil(controller.stack) + } + + func testModalStatusPriority() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + XCTAssertNotNil(controller.priority == .Required) + } + + func testModalPresentationStack() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + SModal.modalWindow.rootViewController = controller + + let controllerToPresent = Controller() + controllerToPresent.sPresent() + XCTAssert(SModal.stack.contains(where: { $0 === controllerToPresent })) + } + + func testModalPresentationCanDismiss() { + class Controller: UIViewController, SModalPresentation { + var canDismiss: Bool { + return true + } + } + let controller = Controller() + SModal.modalWindow.rootViewController = controller + class ControllerHigh: UIViewController, SModalPresentation {} + let controllerToPresent = ControllerHigh() + controllerToPresent.sPresent { + XCTAssertTrue(SModal.modalWindow.rootViewController === controllerToPresent) + } + } + + func testModalPresentationStackWithdraw() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + + class ControllerTwo: UIViewController, SModalPresentation {} + let controllerTwo = ControllerTwo() + + class ControllerThree: UIViewController, SModalPresentation {} + let controllerThree = ControllerThree() + + SModal.stack.append(controller) + SModal.stack.append(controllerTwo) + SModal.stack.append(controllerThree) + + controllerTwo.sWithdraw { + XCTAssertFalse(controller.modalStatus == .waitingInStack) + } + } + + func testModalPresentationPriorityAnimated() { + class Controller: UIViewController, SModalPresentation { + var canDismiss: Bool { + return true + } + } + let controller = Controller() + SModal.modalWindow.rootViewController = controller + class ControllerHigh: UIViewController, SModalPresentation {} + let controllerToPresent = ControllerHigh() + controllerToPresent.sPresent(animated: true) { + XCTAssertFalse(SModal.modalWindow.rootViewController === controller) + } + } + + func testModalPresentationStackWithdrawAnimated() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + + class ControllerTwo: UIViewController, SModalPresentation {} + let controllerTwo = ControllerTwo() + + class ControllerThree: UIViewController, SModalPresentation {} + let controllerThree = ControllerThree() + + SModal.stack.append(controller) + SModal.stack.append(controllerTwo) + SModal.stack.append(controllerThree) + + controllerTwo.sWithdraw(animated: true) { + XCTAssertFalse(controller.modalStatus == .waitingInStack) + } + } + + func testModalPresentationReplace() { + class Controller: UIViewController, SModalPresentation {} + let controller = Controller() + + class ControllerTwo: UIViewController, SModalPresentation {} + let controllerTwo = ControllerTwo() + + SModal.modalWindow.rootViewController = controller + controller.sReplace(with: controllerTwo) { + XCTAssertTrue(SModal.modalWindow.rootViewController === controllerTwo) } }