From 01518c4be4f31ab04d26a24f69bd614dea57e867 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 8 Apr 2019 18:56:37 +0900 Subject: [PATCH 01/14] Fix action button --- SimpleAlert/AlertAction.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleAlert/AlertAction.swift b/SimpleAlert/AlertAction.swift index 193ada6..5d5f71a 100644 --- a/SimpleAlert/AlertAction.swift +++ b/SimpleAlert/AlertAction.swift @@ -52,7 +52,7 @@ open class AlertAction { let handler: ((AlertAction) -> Void)? let style: Style let dismissesAlert: Bool - public let button = UIButton(type: .system) + public var button = UIButton(type: .system) public var isEnabled: Bool { get { return button.isEnabled } set { button.isEnabled = newValue } From ecf68a6652a37c90eddd54d38dd21e557b34d098 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 8 Apr 2019 18:58:28 +0900 Subject: [PATCH 02/14] Update podspec --- SimpleAlert.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index 7d93e98..bc0d70b 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "3.1.1" + s.version = "3.1.2" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } From c550d4a5a10a34474bf948f3edb132b17b85df8d Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 8 Apr 2019 19:05:48 +0900 Subject: [PATCH 03/14] Update to swift 5 --- SimpleAlert.podspec | 4 ++-- SimpleAlert.xcodeproj/project.pbxproj | 12 +++++++----- .../xcschemes/SimpleAlert.xcscheme | 2 +- .../project.pbxproj | 18 ++++++++++-------- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index bc0d70b..ec28b9e 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "3.1.2" + s.version = "4.0.0" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { "Kyohei Ito" => "je.suis.kyohei@gmail.com" } - s.swift_version = '4.2' + s.swift_version = '5.0' s.platform = :ios, "8.0" s.source = { :git => "https://github.com/KyoheiG3/SimpleAlert.git", :tag => s.version.to_s } s.source_files = "SimpleAlert/**/*.{h,swift,xib}" diff --git a/SimpleAlert.xcodeproj/project.pbxproj b/SimpleAlert.xcodeproj/project.pbxproj index b7f2fea..5e1770a 100644 --- a/SimpleAlert.xcodeproj/project.pbxproj +++ b/SimpleAlert.xcodeproj/project.pbxproj @@ -204,12 +204,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = kyohei_ito; TargetAttributes = { C70C0C371A5FBEC8002BC071 = { CreatedOnToolsVersion = 6.2; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; C70C0C421A5FBEC8002BC071 = { CreatedOnToolsVersion = 6.2; @@ -219,7 +219,7 @@ }; buildConfigurationList = C70C0C321A5FBEC8002BC071 /* Build configuration list for PBXProject "SimpleAlert" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -298,6 +298,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -356,6 +357,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -419,7 +421,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -438,7 +440,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.kyoheiito.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/SimpleAlert.xcodeproj/xcshareddata/xcschemes/SimpleAlert.xcscheme b/SimpleAlert.xcodeproj/xcshareddata/xcschemes/SimpleAlert.xcscheme index 2d20d09..9074732 100644 --- a/SimpleAlert.xcodeproj/xcshareddata/xcschemes/SimpleAlert.xcscheme +++ b/SimpleAlert.xcodeproj/xcshareddata/xcschemes/SimpleAlert.xcscheme @@ -1,6 +1,6 @@ Date: Thu, 13 Jun 2019 23:03:27 +0900 Subject: [PATCH 04/14] Fix warnings --- SimpleAlert/SimpleAlert.swift | 35 ++++++++------ SimpleAlert/SimpleAlert.xib | 48 +++++++++++++++++-- .../UIAlertControllerStyleExtension.swift | 3 ++ 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/SimpleAlert/SimpleAlert.swift b/SimpleAlert/SimpleAlert.swift index 9d2e82b..e25fa49 100644 --- a/SimpleAlert/SimpleAlert.swift +++ b/SimpleAlert/SimpleAlert.swift @@ -21,9 +21,12 @@ open class AlertController: UIViewController { @IBOutlet weak var marginView: UIView! @IBOutlet weak var contentBaseView: UIVisualEffectView! @IBOutlet weak var cancelBaseView: UIVisualEffectView! - @IBOutlet weak var contentView: UIScrollView! - @IBOutlet weak var alertButtonView: UIScrollView! - @IBOutlet weak var cancelButtonView: UIScrollView! + @IBOutlet weak var contentScrollView: UIScrollView! + @IBOutlet weak var alertButtonScrollView: UIScrollView! + @IBOutlet weak var cancelButtonScrollView: UIScrollView! + @IBOutlet weak var contentView: UIView! + @IBOutlet weak var alertButtonView: UIView! + @IBOutlet weak var cancelButtonView: UIView! @IBOutlet weak var alertContentView: AlertContentView! @IBOutlet weak var containerViewWidth: NSLayoutConstraint! @@ -132,9 +135,9 @@ open class AlertController: UIViewController { cancelBaseView.clipsToBounds = true if #available(iOS 11.0, *) { - contentView.contentInsetAdjustmentBehavior = .never - alertButtonView.contentInsetAdjustmentBehavior = .never - cancelButtonView.contentInsetAdjustmentBehavior = .never + contentScrollView.contentInsetAdjustmentBehavior = .never + alertButtonScrollView.contentInsetAdjustmentBehavior = .never + cancelButtonScrollView.contentInsetAdjustmentBehavior = .never } view.autoresizingMask = [.flexibleWidth, .flexibleHeight] @@ -193,30 +196,30 @@ open class AlertController: UIViewController { let margin = marginInsets.top + marginInsets.bottom let backgroundViewHeight = view.bounds.size.height - backgroundViewBottomSpace.constant - margin - if cancelButtonView.contentSize.height > cancelbuttonViewHeight.constant { - cancelbuttonViewHeight.constant = cancelButtonView.contentSize.height + if cancelButtonScrollView.contentSize.height > cancelbuttonViewHeight.constant { + cancelbuttonViewHeight.constant = cancelButtonScrollView.contentSize.height } if cancelbuttonViewHeight.constant > backgroundViewHeight { - cancelButtonView.contentSize.height = cancelbuttonViewHeight.constant + cancelButtonScrollView.contentSize.height = cancelbuttonViewHeight.constant cancelbuttonViewHeight.constant = backgroundViewHeight contentViewHeight.constant = 0 alertButtonViewHeight.constant = 0 } else { let baseViewHeight = backgroundViewHeight - cancelbuttonViewHeight.constant - spaceBetweenAlertAndCancel.constant - if alertButtonView.contentSize.height > alertButtonViewHeight.constant { - alertButtonViewHeight.constant = alertButtonView.contentSize.height + if alertButtonScrollView.contentSize.height > alertButtonViewHeight.constant { + alertButtonViewHeight.constant = alertButtonScrollView.contentSize.height } if alertButtonViewHeight.constant > baseViewHeight { - alertButtonView.contentSize.height = alertButtonViewHeight.constant + alertButtonScrollView.contentSize.height = alertButtonViewHeight.constant alertButtonViewHeight.constant = baseViewHeight contentViewHeight.constant = 0 } else { let mainViewHeight = baseViewHeight - alertButtonViewHeight.constant if contentViewHeight.constant > mainViewHeight { - contentView.contentSize.height = contentViewHeight.constant + contentScrollView.contentSize.height = contentViewHeight.constant contentViewHeight.constant = mainViewHeight } } @@ -270,7 +273,7 @@ extension AlertController { } func layoutContents() { - alertContentView.frame.size.width = contentView.frame.size.width + alertContentView.frame.size.width = contentScrollView.frame.size.width alertContentView.layoutContents() contentViewHeight.constant = alertContentView.bounds.height @@ -374,6 +377,8 @@ extension AlertController: UIViewControllerTransitioningDelegate { return AlertControllerPresentTransition(backgroundColor: coverColor) case .actionSheet: return ActionSheetControllerPresentTransition(backgroundColor: coverColor, topSpace: self.backgroundViewTopSpace, bottomSpace: self.backgroundViewBottomSpace) + @unknown default: + fatalError() } } @@ -383,6 +388,8 @@ extension AlertController: UIViewControllerTransitioningDelegate { return AlertControllerDismissTransition(backgroundColor: coverColor) case .actionSheet: return ActionSheetControllerDismissTransition(backgroundColor: coverColor, topSpace: self.backgroundViewTopSpace, bottomSpace: self.backgroundViewBottomSpace) + @unknown default: + fatalError() } } } diff --git a/SimpleAlert/SimpleAlert.xib b/SimpleAlert/SimpleAlert.xib index 6d674d1..3053710 100644 --- a/SimpleAlert/SimpleAlert.xib +++ b/SimpleAlert/SimpleAlert.xib @@ -1,30 +1,33 @@ - + - + - + + - + + - + + @@ -57,14 +60,36 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -88,6 +113,19 @@ + + + + + + + + + + + + + diff --git a/SimpleAlert/UIAlertControllerStyleExtension.swift b/SimpleAlert/UIAlertControllerStyleExtension.swift index a9b6c6d..3deb5b3 100644 --- a/SimpleAlert/UIAlertControllerStyleExtension.swift +++ b/SimpleAlert/UIAlertControllerStyleExtension.swift @@ -11,6 +11,7 @@ extension UIAlertController.Style { switch self { case .alert: return 17 case .actionSheet: return 21 + @unknown default: return 17 } } @@ -24,6 +25,8 @@ extension UIAlertController.Style { } else { return 44 } + @unknown default: + return 48 } } } From 320e9d86d7aebffee3bbafef071e7adfc44fa8b5 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Thu, 13 Jun 2019 23:06:23 +0900 Subject: [PATCH 05/14] Update podspec --- SimpleAlert.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index ec28b9e..7ca2e40 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "4.0.0" + s.version = "4.0.1" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } From 84cc53513bc4750cc242cf708a32e8774fb7e2e3 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 17 Jun 2019 16:10:50 +0900 Subject: [PATCH 06/14] Refactoring --- SimpleAlert.xcodeproj/project.pbxproj | 78 ++-- ...tionSheetControllerPresentTransition.swift | 28 -- .../ActionSheetControllerTransition.swift | 19 - SimpleAlert/AlertAction.swift | 30 +- SimpleAlert/AlertContentView.swift | 93 ++--- SimpleAlert/AlertContentView.xib | 80 ++++ SimpleAlert/AlertController.swift | 385 +++++++++++++++++ SimpleAlert/AlertController.xib | 160 +++++++ .../{ => Extension}/CGFloatExtension.swift | 0 .../UIAlertControllerStyleExtension.swift | 0 SimpleAlert/Extension/UIImageExtension.swift | 25 ++ .../Extension/UIStackViewExtension.swift | 42 ++ .../{ => Extension}/UIViewExtension.swift | 0 SimpleAlert/Info.swift | 10 +- SimpleAlert/SimpleAlert.swift | 395 ------------------ SimpleAlert/SimpleAlert.xib | 248 ----------- SimpleAlert/TextField.swift | 9 +- ...tionSheetControllerDismissTransition.swift | 16 +- ...tionSheetControllerPresentTransition.swift | 31 ++ .../AlertControllerDismissTransition.swift | 8 +- .../AlertControllerPresentTransition.swift | 15 +- .../ViewControllerAnimatedTransition.swift | 0 22 files changed, 858 insertions(+), 814 deletions(-) delete mode 100644 SimpleAlert/ActionSheetControllerPresentTransition.swift delete mode 100644 SimpleAlert/ActionSheetControllerTransition.swift create mode 100644 SimpleAlert/AlertContentView.xib create mode 100644 SimpleAlert/AlertController.swift create mode 100644 SimpleAlert/AlertController.xib rename SimpleAlert/{ => Extension}/CGFloatExtension.swift (100%) rename SimpleAlert/{ => Extension}/UIAlertControllerStyleExtension.swift (100%) create mode 100644 SimpleAlert/Extension/UIImageExtension.swift create mode 100644 SimpleAlert/Extension/UIStackViewExtension.swift rename SimpleAlert/{ => Extension}/UIViewExtension.swift (100%) delete mode 100644 SimpleAlert/SimpleAlert.swift delete mode 100644 SimpleAlert/SimpleAlert.xib rename SimpleAlert/{ => Transition}/ActionSheetControllerDismissTransition.swift (53%) create mode 100644 SimpleAlert/Transition/ActionSheetControllerPresentTransition.swift rename SimpleAlert/{ => Transition}/AlertControllerDismissTransition.swift (77%) rename SimpleAlert/{ => Transition}/AlertControllerPresentTransition.swift (61%) rename SimpleAlert/{ => Transition}/ViewControllerAnimatedTransition.swift (100%) diff --git a/SimpleAlert.xcodeproj/project.pbxproj b/SimpleAlert.xcodeproj/project.pbxproj index 5e1770a..4d3875c 100644 --- a/SimpleAlert.xcodeproj/project.pbxproj +++ b/SimpleAlert.xcodeproj/project.pbxproj @@ -10,8 +10,7 @@ C70C0C3E1A5FBEC8002BC071 /* SimpleAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = C70C0C3D1A5FBEC8002BC071 /* SimpleAlert.h */; settings = {ATTRIBUTES = (Public, ); }; }; C70C0C441A5FBEC8002BC071 /* SimpleAlert.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C70C0C381A5FBEC8002BC071 /* SimpleAlert.framework */; }; C70C0C4B1A5FBEC8002BC071 /* SimpleAlertTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C0C4A1A5FBEC8002BC071 /* SimpleAlertTests.swift */; }; - C70C0C561A5FBFB4002BC071 /* SimpleAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C0C541A5FBFB4002BC071 /* SimpleAlert.swift */; }; - C70C0C571A5FBFB4002BC071 /* SimpleAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = C70C0C551A5FBFB4002BC071 /* SimpleAlert.xib */; }; + C70C0C571A5FBFB4002BC071 /* AlertContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C70C0C551A5FBFB4002BC071 /* AlertContentView.xib */; }; C7453D5F1D868B96004E13BD /* AlertAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7453D5E1D868B96004E13BD /* AlertAction.swift */; }; C7453D611D868C30004E13BD /* AlertContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7453D601D868C30004E13BD /* AlertContentView.swift */; }; C75D875D1FC80357003592E3 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D875C1FC80357003592E3 /* TextField.swift */; }; @@ -19,12 +18,15 @@ C75D87651FC88175003592E3 /* Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87641FC88175003592E3 /* Info.swift */; }; C75D87671FC995D2003592E3 /* ViewControllerAnimatedTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87661FC995D2003592E3 /* ViewControllerAnimatedTransition.swift */; }; C75D87751FC99726003592E3 /* ActionSheetControllerPresentTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87721FC99725003592E3 /* ActionSheetControllerPresentTransition.swift */; }; - C75D87761FC99726003592E3 /* ActionSheetControllerTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87731FC99725003592E3 /* ActionSheetControllerTransition.swift */; }; C75D87771FC99726003592E3 /* ActionSheetControllerDismissTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87741FC99725003592E3 /* ActionSheetControllerDismissTransition.swift */; }; C75D877A1FC99730003592E3 /* AlertControllerDismissTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87781FC99730003592E3 /* AlertControllerDismissTransition.swift */; }; C75D877B1FC99730003592E3 /* AlertControllerPresentTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D87791FC99730003592E3 /* AlertControllerPresentTransition.swift */; }; C75D877D1FC997F0003592E3 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D877C1FC997F0003592E3 /* UIViewExtension.swift */; }; C75D87801FCBD996003592E3 /* UIAlertControllerStyleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75D877F1FCBD996003592E3 /* UIAlertControllerStyleExtension.swift */; }; + C78B849222B2991900DC995A /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78B849022B2991900DC995A /* AlertController.swift */; }; + C78B849322B2991900DC995A /* AlertController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C78B849122B2991900DC995A /* AlertController.xib */; }; + C78B849A22B4980600DC995A /* UIStackViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78B849922B4980500DC995A /* UIStackViewExtension.swift */; }; + C78B849C22B498BE00DC995A /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78B849B22B498BE00DC995A /* UIImageExtension.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -44,8 +46,7 @@ C70C0C431A5FBEC8002BC071 /* SimpleAlertTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimpleAlertTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C70C0C491A5FBEC8002BC071 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C70C0C4A1A5FBEC8002BC071 /* SimpleAlertTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleAlertTests.swift; sourceTree = ""; }; - C70C0C541A5FBFB4002BC071 /* SimpleAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleAlert.swift; sourceTree = ""; }; - C70C0C551A5FBFB4002BC071 /* SimpleAlert.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SimpleAlert.xib; sourceTree = ""; }; + C70C0C551A5FBFB4002BC071 /* AlertContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AlertContentView.xib; sourceTree = ""; }; C7453D5E1D868B96004E13BD /* AlertAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertAction.swift; sourceTree = ""; }; C7453D601D868C30004E13BD /* AlertContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertContentView.swift; sourceTree = ""; }; C75D875C1FC80357003592E3 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; @@ -53,12 +54,15 @@ C75D87641FC88175003592E3 /* Info.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Info.swift; sourceTree = ""; }; C75D87661FC995D2003592E3 /* ViewControllerAnimatedTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerAnimatedTransition.swift; sourceTree = ""; }; C75D87721FC99725003592E3 /* ActionSheetControllerPresentTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetControllerPresentTransition.swift; sourceTree = ""; }; - C75D87731FC99725003592E3 /* ActionSheetControllerTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetControllerTransition.swift; sourceTree = ""; }; C75D87741FC99725003592E3 /* ActionSheetControllerDismissTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetControllerDismissTransition.swift; sourceTree = ""; }; C75D87781FC99730003592E3 /* AlertControllerDismissTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertControllerDismissTransition.swift; sourceTree = ""; }; C75D87791FC99730003592E3 /* AlertControllerPresentTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertControllerPresentTransition.swift; sourceTree = ""; }; C75D877C1FC997F0003592E3 /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtension.swift; sourceTree = ""; }; C75D877F1FCBD996003592E3 /* UIAlertControllerStyleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertControllerStyleExtension.swift; sourceTree = ""; }; + C78B849022B2991900DC995A /* AlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = ""; }; + C78B849122B2991900DC995A /* AlertController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlertController.xib; sourceTree = ""; }; + C78B849922B4980500DC995A /* UIStackViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackViewExtension.swift; sourceTree = ""; }; + C78B849B22B498BE00DC995A /* UIImageExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,35 +105,21 @@ C70C0C3A1A5FBEC8002BC071 /* SimpleAlert */ = { isa = PBXGroup; children = ( - C70C0C3B1A5FBEC8002BC071 /* Supporting Files */, - C75D87741FC99725003592E3 /* ActionSheetControllerDismissTransition.swift */, - C75D87721FC99725003592E3 /* ActionSheetControllerPresentTransition.swift */, - C75D87731FC99725003592E3 /* ActionSheetControllerTransition.swift */, + C78B849D22B498C300DC995A /* Extension */, + C78B849422B33B1600DC995A /* Transition */, C7453D5E1D868B96004E13BD /* AlertAction.swift */, C7453D601D868C30004E13BD /* AlertContentView.swift */, - C75D87781FC99730003592E3 /* AlertControllerDismissTransition.swift */, - C75D87791FC99730003592E3 /* AlertControllerPresentTransition.swift */, - C75D87621FC88148003592E3 /* CGFloatExtension.swift */, + C70C0C551A5FBFB4002BC071 /* AlertContentView.xib */, + C70C0C3C1A5FBEC8002BC071 /* Info.plist */, C75D87641FC88175003592E3 /* Info.swift */, C70C0C3D1A5FBEC8002BC071 /* SimpleAlert.h */, - C70C0C541A5FBFB4002BC071 /* SimpleAlert.swift */, - C70C0C551A5FBFB4002BC071 /* SimpleAlert.xib */, C75D875C1FC80357003592E3 /* TextField.swift */, - C75D877F1FCBD996003592E3 /* UIAlertControllerStyleExtension.swift */, - C75D877C1FC997F0003592E3 /* UIViewExtension.swift */, - C75D87661FC995D2003592E3 /* ViewControllerAnimatedTransition.swift */, + C78B849022B2991900DC995A /* AlertController.swift */, + C78B849122B2991900DC995A /* AlertController.xib */, ); path = SimpleAlert; sourceTree = ""; }; - C70C0C3B1A5FBEC8002BC071 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - C70C0C3C1A5FBEC8002BC071 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; C70C0C471A5FBEC8002BC071 /* SimpleAlertTests */ = { isa = PBXGroup; children = ( @@ -147,6 +137,30 @@ name = "Supporting Files"; sourceTree = ""; }; + C78B849422B33B1600DC995A /* Transition */ = { + isa = PBXGroup; + children = ( + C75D87661FC995D2003592E3 /* ViewControllerAnimatedTransition.swift */, + C75D87741FC99725003592E3 /* ActionSheetControllerDismissTransition.swift */, + C75D87721FC99725003592E3 /* ActionSheetControllerPresentTransition.swift */, + C75D87781FC99730003592E3 /* AlertControllerDismissTransition.swift */, + C75D87791FC99730003592E3 /* AlertControllerPresentTransition.swift */, + ); + path = Transition; + sourceTree = ""; + }; + C78B849D22B498C300DC995A /* Extension */ = { + isa = PBXGroup; + children = ( + C75D87621FC88148003592E3 /* CGFloatExtension.swift */, + C75D877F1FCBD996003592E3 /* UIAlertControllerStyleExtension.swift */, + C78B849922B4980500DC995A /* UIStackViewExtension.swift */, + C78B849B22B498BE00DC995A /* UIImageExtension.swift */, + C75D877C1FC997F0003592E3 /* UIViewExtension.swift */, + ); + path = Extension; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -240,7 +254,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C70C0C571A5FBFB4002BC071 /* SimpleAlert.xib in Resources */, + C78B849322B2991900DC995A /* AlertController.xib in Resources */, + C70C0C571A5FBFB4002BC071 /* AlertContentView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -259,19 +274,20 @@ buildActionMask = 2147483647; files = ( C7453D611D868C30004E13BD /* AlertContentView.swift in Sources */, + C78B849222B2991900DC995A /* AlertController.swift in Sources */, C7453D5F1D868B96004E13BD /* AlertAction.swift in Sources */, - C70C0C561A5FBFB4002BC071 /* SimpleAlert.swift in Sources */, C75D87651FC88175003592E3 /* Info.swift in Sources */, C75D875D1FC80357003592E3 /* TextField.swift in Sources */, C75D877B1FC99730003592E3 /* AlertControllerPresentTransition.swift in Sources */, C75D87671FC995D2003592E3 /* ViewControllerAnimatedTransition.swift in Sources */, C75D87751FC99726003592E3 /* ActionSheetControllerPresentTransition.swift in Sources */, + C78B849C22B498BE00DC995A /* UIImageExtension.swift in Sources */, C75D87771FC99726003592E3 /* ActionSheetControllerDismissTransition.swift in Sources */, C75D877D1FC997F0003592E3 /* UIViewExtension.swift in Sources */, C75D87801FCBD996003592E3 /* UIAlertControllerStyleExtension.swift in Sources */, + C78B849A22B4980600DC995A /* UIStackViewExtension.swift in Sources */, C75D877A1FC99730003592E3 /* AlertControllerDismissTransition.swift in Sources */, C75D87631FC88148003592E3 /* CGFloatExtension.swift in Sources */, - C75D87761FC99726003592E3 /* ActionSheetControllerTransition.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -342,7 +358,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -394,7 +410,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/SimpleAlert/ActionSheetControllerPresentTransition.swift b/SimpleAlert/ActionSheetControllerPresentTransition.swift deleted file mode 100644 index 41e160b..0000000 --- a/SimpleAlert/ActionSheetControllerPresentTransition.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// ActionSheetControllerPresentTransition.swift -// SimpleAlert -// -// Created by Kyohei Ito on 2017/11/25. -// Copyright © 2017年 kyohei_ito. All rights reserved. -// - -import UIKit - -class ActionSheetControllerPresentTransition: ActionSheetControllerTransition { - override func animateTransition(_ from: UIViewController, to: UIViewController, container: UIView, completion: @escaping (Bool) -> Void) { - to.view.frame = container.bounds - to.view.backgroundColor = backgroundColor.withAlphaComponent(0) - container.addSubview(to.view) - - topSpace().constant = to.view.bounds.height - bottomSpace().constant = -to.view.bounds.height - to.view.layoutIfNeeded() - - UIView.animate(withDuration: duration, animations: { - self.topSpace().constant = 0 - self.bottomSpace().constant = 0 - to.view.backgroundColor = to.view.backgroundColor?.withAlphaComponent(0.4) - to.view.layoutIfNeeded() - }, completion: completion) - } -} diff --git a/SimpleAlert/ActionSheetControllerTransition.swift b/SimpleAlert/ActionSheetControllerTransition.swift deleted file mode 100644 index 3e44e4e..0000000 --- a/SimpleAlert/ActionSheetControllerTransition.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ActionSheetControllerTransition.swift -// SimpleAlert -// -// Created by Kyohei Ito on 2017/11/25. -// Copyright © 2017年 kyohei_ito. All rights reserved. -// - -import UIKit - -class ActionSheetControllerTransition: ViewControllerAnimatedTransition { - let topSpace: () -> NSLayoutConstraint - let bottomSpace: () -> NSLayoutConstraint - init(backgroundColor: UIColor, topSpace: @autoclosure @escaping () -> NSLayoutConstraint, bottomSpace: @autoclosure @escaping () -> NSLayoutConstraint) { - self.topSpace = topSpace - self.bottomSpace = bottomSpace - super.init(backgroundColor: backgroundColor) - } -} diff --git a/SimpleAlert/AlertAction.swift b/SimpleAlert/AlertAction.swift index 5d5f71a..2535231 100644 --- a/SimpleAlert/AlertAction.swift +++ b/SimpleAlert/AlertAction.swift @@ -23,44 +23,24 @@ open class AlertAction { } } - public init(title: String, style: Style, dismissesAlert: Bool = true, handler: ((AlertAction?) -> Void)? = nil) { + public init(title: String, style: Style, shouldDismisses: Bool = true, handler: ((AlertAction?) -> Void)? = nil) { self.title = title self.handler = handler self.style = style - self.dismissesAlert = dismissesAlert + self.shouldDismisses = shouldDismisses button.setTitle(title, for: .normal) - button.autoresizingMask = .flexibleWidth - addHorizontalBorder() - } - - func addHorizontalBorder() { - let borderView = UIView(frame: CGRect(x: 0, y: -CGFloat.thinWidth, width: button.bounds.width, height: CGFloat.thinWidth)) - borderView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.4) - borderView.autoresizingMask = .flexibleWidth - button.addSubview(borderView) - } - - func addVerticalBorder() { - let borderView = UIView(frame: CGRect(x: -CGFloat.thinWidth, y: 0, width: CGFloat.thinWidth, height: button.bounds.height)) - borderView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.4) - borderView.autoresizingMask = .flexibleHeight - button.addSubview(borderView) + button.setTitleColor(button.titleColor(for: .normal), for: .highlighted) + button.setBackgroundImage(UIImage(color: .lightGray), for: .highlighted) } let title: String let handler: ((AlertAction) -> Void)? let style: Style - let dismissesAlert: Bool + let shouldDismisses: Bool public var button = UIButton(type: .system) public var isEnabled: Bool { get { return button.isEnabled } set { button.isEnabled = newValue } } } - -extension UIView { - func addAction(_ action: AlertAction) { - addSubview(action.button) - } -} diff --git a/SimpleAlert/AlertContentView.swift b/SimpleAlert/AlertContentView.swift index 4720127..90f5d09 100644 --- a/SimpleAlert/AlertContentView.swift +++ b/SimpleAlert/AlertContentView.swift @@ -9,67 +9,56 @@ import UIKit open class AlertContentView: UIView { - @IBOutlet public weak var baseView: UIView! - @IBOutlet public weak var titleLabel: UILabel! - @IBOutlet public weak var messageLabel: UILabel! - @IBOutlet public weak var textBackgroundView: UIView! - @IBOutlet public weak var containerView: UIView! + @IBOutlet public private(set) weak var contentStackView: UIStackView! + @IBOutlet public private(set) weak var titleLabel: UILabel! + @IBOutlet public private(set) weak var messageLabel: UILabel! + @IBOutlet public private(set) weak var textBackgroundView: UIView! + @IBOutlet private weak var textFieldView: UIView! + @IBOutlet private weak var textFieldStackView: UIStackView! - @IBOutlet var verticalSpaceConstraint: NSLayoutConstraint! - @IBOutlet var titleSpaceConstraint: NSLayoutConstraint! - @IBOutlet var messageSpaceConstraint: NSLayoutConstraint! - @IBOutlet var textViewHeightConstraint: NSLayoutConstraint! - @IBOutlet var containerViewHeight: NSLayoutConstraint! + var textFields: [UITextField] { + return textFieldStackView?.arrangedSubviews.compactMap { $0 as? UITextField } ?? [] + } - var textFields: [UITextField] = [] + open override func layoutSubviews() { + super.layoutSubviews() - func addTextField() -> UITextField { - let textField = TextField(frame: textBackgroundView.bounds) - textFields.append(textField) - textBackgroundView.addSubview(textField) - return textField + titleLabel.isHidden = titleLabel.text?.isEmpty ?? true + messageLabel.isHidden = messageLabel.text?.isEmpty ?? true + textFieldView.isHidden = textFields.isEmpty + superview?.isHidden = titleLabel.isHidden && messageLabel.isHidden && textFieldView.isHidden } - func layoutContents() { - textViewHeightConstraint.constant = 0 - for textField in textFields { - textField.frame.origin.y = textViewHeightConstraint.constant - if textField.frame.height <= 0 { - textField.frame.size.height = 25 - } - textViewHeightConstraint.constant += textField.frame.height - } - - titleLabel.preferredMaxLayoutWidth = baseView.bounds.width - messageLabel.preferredMaxLayoutWidth = baseView.bounds.width + func append(_ textField: UITextField) { + textFieldStackView.addArrangedSubview(textField) + } - if textBackgroundView.subviews.isEmpty { - messageSpaceConstraint.constant = 0 - } + func removeAllTextField() { + textFieldStackView.removeAllArrangedSubviews() + } - if titleLabel.text == nil && messageLabel.text == nil { - titleSpaceConstraint.constant = 0 - messageSpaceConstraint.constant = 0 + public override init(frame: CGRect) { + super.init(frame: frame) + loadNibContent() + } - if textBackgroundView.subviews.isEmpty { - verticalSpaceConstraint.constant = 0 - } - } else if titleLabel.text == nil || messageLabel.text == nil { - titleSpaceConstraint.constant = 0 - } + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + loadNibContent() + } - if let view = containerView.subviews.sorted(by: { $0.frame.maxY > $1.frame.maxY }).first { - containerViewHeight.constant = view.frame.maxY + private func loadNibContent() { + let type = AlertContentView.self + let nib = UINib(nibName: String(describing: type), bundle: Bundle(for: type)) + if let view = nib.instantiate(withOwner: self, options: nil).first as? UIView { + addSubview(view) + view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + view.topAnchor.constraint(equalTo: topAnchor), + view.leftAnchor.constraint(equalTo: leftAnchor), + view.rightAnchor.constraint(equalTo: rightAnchor), + view.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) } - baseView.layoutIfNeeded() - - frame.size.height = baseView.bounds.height + (verticalSpaceConstraint.constant * 2) + containerViewHeight.constant - } - - func addHorizontalBorder() { - let borderView = UIView(frame: CGRect(x: 0, y: -CGFloat.thinWidth, width: containerView.bounds.width, height: CGFloat.thinWidth)) - borderView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.4) - borderView.autoresizingMask = .flexibleWidth - containerView.addSubview(borderView) } } diff --git a/SimpleAlert/AlertContentView.xib b/SimpleAlert/AlertContentView.xib new file mode 100644 index 0000000..9fb3bb8 --- /dev/null +++ b/SimpleAlert/AlertContentView.xib @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SimpleAlert/AlertController.swift b/SimpleAlert/AlertController.swift new file mode 100644 index 0000000..83f9931 --- /dev/null +++ b/SimpleAlert/AlertController.swift @@ -0,0 +1,385 @@ +// +// AlertController.swift +// SimpleAlert +// +// Created by Kyohei Ito on 2019/06/13. +// Copyright © 2019 kyohei_ito. All rights reserved. +// + +import UIKit + +open class AlertController: UIViewController { + enum Const { + static let alertWidth: CGFloat = 270 + static let actionSheetMargin: CGFloat = 16 + static let cornerRadius: CGFloat = 12 + static let textFieldHeight: CGFloat = 25 + } + + @IBOutlet weak var backgroundView: UIView! { + didSet { + if preferredStyle == .actionSheet { + tapGesture.addTarget(self, action: #selector(AlertController.backgroundViewTapAction(_:))) + backgroundView.addGestureRecognizer(tapGesture) + } + } + } + + @IBOutlet weak var containerView: UIView! + @IBOutlet weak var containerStackView: UIStackView! + + @IBOutlet weak var contentEffectView: UIVisualEffectView! { + didSet { + contentEffectView.clipsToBounds = true + } + } + + @IBOutlet weak var cancelEffectView: UIVisualEffectView! { + didSet { + cancelEffectView.clipsToBounds = true + cancelEffectView.isHidden = preferredStyle == .alert + } + } + + @IBOutlet weak var contentStackView: UIStackView! + + @IBOutlet weak var contentScrollView: UIScrollView! { + didSet { + contentScrollView.showsVerticalScrollIndicator = false + contentScrollView.showsHorizontalScrollIndicator = false + if #available(iOS 11.0, *) { + contentScrollView.contentInsetAdjustmentBehavior = .never + } + } + } + + @IBOutlet weak var alertButtonScrollView: UIScrollView! { + didSet { + alertButtonScrollView.showsVerticalScrollIndicator = false + alertButtonScrollView.showsHorizontalScrollIndicator = false + if #available(iOS 11.0, *) { + alertButtonScrollView.contentInsetAdjustmentBehavior = .never + } + } + } + + @IBOutlet weak var cancelButtonScrollView: UIScrollView! { + didSet { + alertButtonScrollView.showsVerticalScrollIndicator = false + alertButtonScrollView.showsHorizontalScrollIndicator = false + if #available(iOS 11.0, *) { + cancelButtonScrollView.contentInsetAdjustmentBehavior = .never + } + } + } + + @IBOutlet weak var alertContentView: AlertContentView! + @IBOutlet weak var alertButtonStackView: UIStackView! + @IBOutlet weak var cancelButtonStackView: UIStackView! + + @IBOutlet weak var containerViewBottom: NSLayoutConstraint! + @IBOutlet weak var containerStackViewWidth: NSLayoutConstraint! + + public private(set) var actions: [AlertAction] = [] + public private(set) var textFields: [UITextField] = [] + + open var contentWidth: CGFloat? + open var contentColor: UIColor? + open var contentCornerRadius: CGFloat? + open var coverColor: UIColor = .black + open var message: String? + + private var contentViewHandler: ((AlertContentView) -> Void)? + private var customView: UIView? + private var preferredStyle: UIAlertController.Style = .alert + private let tapGesture = UITapGestureRecognizer() + + private weak var customViewHeightConstraint: NSLayoutConstraint? { + didSet { + oldValue?.isActive = false + } + } + + private weak var containerStackViewYAxisConstraint: NSLayoutConstraint? { + didSet { + oldValue?.isActive = false + } + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + private convenience init() { + let type = AlertController.self + self.init(nibName: String(describing: type), bundle: Bundle(for: type)) + } + + public convenience init(title: String?, message: String?, style: UIAlertController.Style) { + self.init() + self.title = title + self.message = message + self.preferredStyle = style + } + + public convenience init(title: String? = nil, message: String? = nil, view: UIView?, style: UIAlertController.Style) { + self.init() + self.title = title + self.message = message + customView = view + preferredStyle = style + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + + modalPresentationStyle = .custom + modalTransitionStyle = .crossDissolve + transitioningDelegate = self + } + + open override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + if let color = contentColor { + contentEffectView.backgroundColor = color + cancelEffectView.backgroundColor = color + contentEffectView.effect = nil + cancelEffectView.effect = nil + } else { + contentEffectView.backgroundColor = .white + cancelEffectView.backgroundColor = .white + } + + contentEffectView.layer.cornerRadius = contentCornerRadius ?? Const.cornerRadius + cancelEffectView.layer.cornerRadius = contentCornerRadius ?? Const.cornerRadius + + alertContentView.titleLabel.text = title + alertContentView.messageLabel.text = message + + if preferredStyle == .alert { + textFields.forEach { textField in + alertContentView.append(textField) + (textField as? TextField)?.handler?(textField) + } + + textFields.first?.becomeFirstResponder() + } + + if let view = customView { + alertContentView.contentStackView.addArrangedSubview(view) + } + + configureContentView(alertContentView) + + layoutButtons() + + view.layoutIfNeeded() + NotificationCenter.default.addObserver(self, selector: #selector(AlertController.keyboardFrameWillChange), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(AlertController.keyboardFrameWillChange), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + open override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + NotificationCenter.default.removeObserver(self) + alertContentView.endEditing(true) + } + + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + alertContentView.removeAllTextField() + if let view = customView { + alertContentView.contentStackView.removeArrangedSubview(view) + view.removeFromSuperview() + } + alertButtonStackView.removeAllArrangedSubviews() + cancelButtonStackView.removeAllArrangedSubviews() + contentStackView.arrangedSubviews + .filter { !($0 is UIScrollView) } + .forEach { view in + contentStackView.removeArrangedSubview(view) + view.removeFromSuperview() + } + } + + open override func updateViewConstraints() { + super.updateViewConstraints() + + customViewHeightConstraint?.isActive = false + containerStackViewYAxisConstraint?.isActive = false + + let minWidth = min(view.bounds.width, view.bounds.height) + containerStackViewWidth.constant = contentWidth ?? (preferredStyle == .alert ? Const.alertWidth : minWidth - Const.actionSheetMargin) + + let constraint: NSLayoutConstraint + switch preferredStyle { + case .alert: + constraint = containerStackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor) + + case .actionSheet: + let bottomAnchor: NSLayoutYAxisAnchor + if #available(iOS 11.0, *) { + bottomAnchor = containerView.safeAreaLayoutGuide.bottomAnchor + } else { + bottomAnchor = containerView.bottomAnchor + } + constraint = containerStackView.bottomAnchor.constraint(equalTo: bottomAnchor) + + @unknown default: + constraint = containerStackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor) + } + + constraint.isActive = true + containerStackViewYAxisConstraint = constraint + + if let view = customView { + let constraint = view.heightAnchor.constraint(equalToConstant: view.bounds.height) + constraint.isActive = true + customViewHeightConstraint = constraint + } + } + + open func addTextField(configurationHandler: ((UITextField) -> Void)? = nil) { + let textField = TextField() + textField.handler = configurationHandler + textField.heightAnchor.constraint(equalToConstant: Const.textFieldHeight).isActive = true + textFields.append(textField) + } + + open func addAction(_ action: AlertAction) { + action.button.frame.size.height = preferredStyle.buttonHeight + action.button.addTarget(self, action: #selector(AlertController.buttonDidTap), for: .touchUpInside) + + configureActionButton(action.button, at: action.style) + actions.append(action) + } + + open func configureActionButton(_ button: UIButton, at style: AlertAction.Style) { + if style == .destructive { + button.setTitleColor(.red, for: .normal) + } + button.titleLabel?.font = style.font(of: preferredStyle) + } + + open func configureContentView(_ contentView: AlertContentView) { + contentViewHandler?(contentView) + } + + @discardableResult + public func configureContentView(configurationHandler: @escaping (AlertContentView) -> Void) -> Self { + contentViewHandler = configurationHandler + return self + } +} + +extension AlertController { + func layoutButtons() { + alertButtonStackView.axis = preferredStyle == .alert && actions.count == 2 ? .horizontal : .vertical + + switch preferredStyle { + case .actionSheet: + actions.lazy + .filter { $0.style != .cancel } + .forEach(alertButtonStackView.addAction) + actions.lazy + .filter { $0.style == .cancel } + .forEach(cancelButtonStackView.addAction) + + case .alert: + actions.forEach(alertButtonStackView.addAction) + + @unknown default: + break + } + + zip(actions, actions.dropFirst()).forEach { top, bottom in + top.button.widthAnchor.constraint(equalTo: bottom.button.widthAnchor).isActive = true + } + + if alertButtonStackView.axis == .horizontal, let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + contentStackView.insertArrangedSubview(contentStackView.makeBorderView(), at: 1) + } + } + + func dismiss(with sender: UIButton) { + guard let action = actions.filter({ $0.button == sender }).first else { + dismiss() + return + } + if action.shouldDismisses { + dismiss { + action.handler?(action) + } + } else { + action.handler?(action) + } + } + + func dismiss(withCompletion block: @escaping () -> Void = {}) { + dismiss(animated: true) { + block() + self.actions.removeAll() + self.textFields.removeAll() + } + } +} + +// MARK: - Action Methods +extension AlertController { + @objc func buttonDidTap(_ button: UIButton) { + dismiss(with: button) + } + + @objc func backgroundViewTapAction(_ gesture: UITapGestureRecognizer) { + dismiss() + } +} + +// MARK: - NSNotificationCenter Methods +extension AlertController { + @objc func keyboardFrameWillChange(_ notification: Notification) { + let info = notification.info + if let frame = info.keyboardFrameEnd, + let duration = info.duration, + let curve = info.curve { + + UIView.animate(withDuration: duration, delay: 0, options: curve, animations: { + self.containerViewBottom.constant = self.view.bounds.height - frame.origin.y + self.view.layoutIfNeeded() + }) + } + } +} + +// MARK: - UIViewControllerTransitioningDelegate Methods +extension AlertController: UIViewControllerTransitioningDelegate { + public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + switch preferredStyle { + case .alert: + return AlertControllerPresentTransition(backgroundColor: coverColor) + case .actionSheet: + return ActionSheetControllerPresentTransition(backgroundColor: coverColor) + @unknown default: + fatalError() + } + } + + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + switch preferredStyle { + case .alert: + return AlertControllerDismissTransition(backgroundColor: coverColor) + case .actionSheet: + return ActionSheetControllerDismissTransition(backgroundColor: coverColor) + @unknown default: + fatalError() + } + } +} diff --git a/SimpleAlert/AlertController.xib b/SimpleAlert/AlertController.xib new file mode 100644 index 0000000..1d7aa53 --- /dev/null +++ b/SimpleAlert/AlertController.xib @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SimpleAlert/CGFloatExtension.swift b/SimpleAlert/Extension/CGFloatExtension.swift similarity index 100% rename from SimpleAlert/CGFloatExtension.swift rename to SimpleAlert/Extension/CGFloatExtension.swift diff --git a/SimpleAlert/UIAlertControllerStyleExtension.swift b/SimpleAlert/Extension/UIAlertControllerStyleExtension.swift similarity index 100% rename from SimpleAlert/UIAlertControllerStyleExtension.swift rename to SimpleAlert/Extension/UIAlertControllerStyleExtension.swift diff --git a/SimpleAlert/Extension/UIImageExtension.swift b/SimpleAlert/Extension/UIImageExtension.swift new file mode 100644 index 0000000..b98c0f3 --- /dev/null +++ b/SimpleAlert/Extension/UIImageExtension.swift @@ -0,0 +1,25 @@ +// +// UIImageExtension.swift +// SimpleAlert +// +// Created by Kyohei Ito on 2019/06/15. +// Copyright © 2019 kyohei_ito. All rights reserved. +// + +import UIKit + +extension UIImage { + convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) { + UIGraphicsBeginImageContextWithOptions(size, false, 1) + let context = UIGraphicsGetCurrentContext() + context?.setFillColor(color.cgColor) + context?.fill(CGRect(origin: .zero, size: size)) + + defer { + UIGraphicsEndImageContext() + } + + guard let image = UIGraphicsGetImageFromCurrentImageContext(), let data = image.pngData() else { return nil } + self.init(data: data, scale: image.scale) + } +} diff --git a/SimpleAlert/Extension/UIStackViewExtension.swift b/SimpleAlert/Extension/UIStackViewExtension.swift new file mode 100644 index 0000000..63a9000 --- /dev/null +++ b/SimpleAlert/Extension/UIStackViewExtension.swift @@ -0,0 +1,42 @@ +// +// UIStackViewExtension.swift +// SimpleAlert +// +// Created by Kyohei Ito on 2019/06/15. +// Copyright © 2019 kyohei_ito. All rights reserved. +// + +import UIKit + +extension UIStackView { + func makeBorderView() -> UIView { + let borderView = UIView() + borderView.translatesAutoresizingMaskIntoConstraints = false + borderView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.4) + + switch axis { + case .horizontal: + borderView.widthAnchor.constraint(equalToConstant: CGFloat.thinWidth).isActive = true + case .vertical: + borderView.heightAnchor.constraint(equalToConstant: CGFloat.thinWidth).isActive = true + @unknown default: + borderView.heightAnchor.constraint(equalToConstant: CGFloat.thinWidth).isActive = true + } + + return borderView + } + + func addAction(_ action: AlertAction) { + addArrangedSubview(makeBorderView()) + + action.button.heightAnchor.constraint(equalToConstant: action.button.bounds.height).isActive = true + addArrangedSubview(action.button) + } + + func removeAllArrangedSubviews() { + arrangedSubviews.forEach { view in + removeArrangedSubview(view) + view.removeFromSuperview() + } + } +} diff --git a/SimpleAlert/UIViewExtension.swift b/SimpleAlert/Extension/UIViewExtension.swift similarity index 100% rename from SimpleAlert/UIViewExtension.swift rename to SimpleAlert/Extension/UIViewExtension.swift diff --git a/SimpleAlert/Info.swift b/SimpleAlert/Info.swift index 4c6d780..84844eb 100644 --- a/SimpleAlert/Info.swift +++ b/SimpleAlert/Info.swift @@ -13,10 +13,18 @@ struct Info { self.userInfo = userInfo } - var endFrame: CGRect? { + var keyboardFrameEnd: CGRect? { return userInfoRect(UIResponder.keyboardFrameEndUserInfoKey) } + var duration: TimeInterval? { + return userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval + } + + var curve: UIView.AnimationOptions? { + return (userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt).map(UIView.AnimationOptions.init) + } + private func userInfoRect(_ infoKey: String) -> CGRect? { let frame = (userInfo?[infoKey] as? NSValue)?.cgRectValue if let rect = frame, rect.origin.x.isInfinite || rect.origin.y.isInfinite { diff --git a/SimpleAlert/SimpleAlert.swift b/SimpleAlert/SimpleAlert.swift deleted file mode 100644 index e25fa49..0000000 --- a/SimpleAlert/SimpleAlert.swift +++ /dev/null @@ -1,395 +0,0 @@ -// -// SimpleAlert.swift -// SimpleAlert -// -// Created by Kyohei Ito on 2015/01/09. -// Copyright (c) 2015年 kyohei_ito. All rights reserved. -// - -import UIKit - -open class AlertController: UIViewController { - @IBOutlet weak var containerView: UIView! - @IBOutlet weak var backgroundView: UIView! { - didSet { - if preferredStyle == .actionSheet { - tapGesture.addTarget(self, action: #selector(AlertController.backgroundViewTapAction(_:))) - backgroundView.addGestureRecognizer(tapGesture) - } - } - } - @IBOutlet weak var marginView: UIView! - @IBOutlet weak var contentBaseView: UIVisualEffectView! - @IBOutlet weak var cancelBaseView: UIVisualEffectView! - @IBOutlet weak var contentScrollView: UIScrollView! - @IBOutlet weak var alertButtonScrollView: UIScrollView! - @IBOutlet weak var cancelButtonScrollView: UIScrollView! - @IBOutlet weak var contentView: UIView! - @IBOutlet weak var alertButtonView: UIView! - @IBOutlet weak var cancelButtonView: UIView! - @IBOutlet weak var alertContentView: AlertContentView! - - @IBOutlet weak var containerViewWidth: NSLayoutConstraint! - @IBOutlet weak var containerViewBottomSpace: NSLayoutConstraint! - @IBOutlet weak var backgroundViewTopSpace: NSLayoutConstraint! - @IBOutlet weak var backgroundViewBottomSpace: NSLayoutConstraint! - - @IBOutlet weak var contentViewHeight: NSLayoutConstraint! - @IBOutlet weak var alertButtonViewHeight: NSLayoutConstraint! - @IBOutlet weak var cancelbuttonViewHeight: NSLayoutConstraint! - @IBOutlet weak var spaceBetweenAlertAndCancel: NSLayoutConstraint! - - @IBOutlet weak var marginViewTopSpace: NSLayoutConstraint! - @IBOutlet weak var marginViewLeftSpace: NSLayoutConstraint! - @IBOutlet weak var marginViewBottomSpace: NSLayoutConstraint! - @IBOutlet weak var marginViewRightSpace: NSLayoutConstraint! - - public private(set) var actions: [AlertAction] = [] - public var textFields: [UITextField] { - return alertContentView?.textFields ?? [] - } - open var contentWidth: CGFloat = 270 - open var contentColor: UIColor? - open var contentCornerRadius: CGFloat? - open var coverColor: UIColor = .black - open var message: String? - - private var contentViewHandler: ((AlertContentView) -> Void)? - private var textFieldHandlers: [((UITextField) -> Void)?] = [] - private var customView: UIView? - private var preferredStyle: UIAlertController.Style = .alert - private let tapGesture = UITapGestureRecognizer() - - private var marginInsets: UIEdgeInsets { - set { - marginViewLeftSpace.constant = newValue.left - marginViewRightSpace.constant = newValue.right - if #available(iOS 11.0, *) { - marginViewTopSpace.constant = view.safeAreaInsets.top - marginViewBottomSpace.constant = max(view.safeAreaInsets.bottom, newValue.bottom) - } else { - marginViewTopSpace.constant = newValue.top - marginViewBottomSpace.constant = newValue.bottom - } - } - get { - let top = marginViewTopSpace.constant - let left = marginViewLeftSpace.constant - let bottom = marginViewBottomSpace.constant - let right = marginViewRightSpace.constant - return UIEdgeInsets(top: top, left: left, bottom: bottom, right: right) - } - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - private convenience init() { - self.init(nibName: "SimpleAlert", bundle: Bundle(for: AlertController.self)) - } - - public convenience init(title: String?, message: String?, style: UIAlertController.Style) { - self.init() - self.title = title - self.message = message - self.preferredStyle = style - } - - public convenience init(title: String? = nil, message: String? = nil, view: UIView?, style: UIAlertController.Style) { - self.init() - self.title = title - self.message = message - customView = view - preferredStyle = style - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - - modalPresentationStyle = .custom - modalTransitionStyle = .crossDissolve - transitioningDelegate = self - - NotificationCenter.default.addObserver(self, selector: #selector(AlertController.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(AlertController.keyboardDidHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil) - } - - open override func viewDidLoad() { - super.viewDidLoad() - - contentView.addSubview(alertContentView) - - if let view = customView { - view.autoresizingMask = .flexibleWidth - view.frame.size.width = alertContentView.containerView.bounds.width - alertContentView.containerView.addSubview(view) - alertContentView.addHorizontalBorder() - } - - contentBaseView.clipsToBounds = true - cancelBaseView.clipsToBounds = true - - if #available(iOS 11.0, *) { - contentScrollView.contentInsetAdjustmentBehavior = .never - alertButtonScrollView.contentInsetAdjustmentBehavior = .never - cancelButtonScrollView.contentInsetAdjustmentBehavior = .never - } - - view.autoresizingMask = [.flexibleWidth, .flexibleHeight] - containerViewBottomSpace.isActive = preferredStyle == .actionSheet - } - - open override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if let color = contentColor { - contentBaseView.backgroundColor = color - cancelBaseView.backgroundColor = color - contentBaseView.effect = nil - cancelBaseView.effect = nil - } else { - contentBaseView.backgroundColor = .white - cancelBaseView.backgroundColor = .white - } - - if let cornerRadius = contentCornerRadius { - contentBaseView.layer.cornerRadius = cornerRadius - cancelBaseView.layer.cornerRadius = cornerRadius - } else { - if #available(iOS 9.0, *) { - contentBaseView.layer.cornerRadius = 12 - cancelBaseView.layer.cornerRadius = 12 - } else { - contentBaseView.layer.cornerRadius = 4 - cancelBaseView.layer.cornerRadius = 4 - } - } - - alertContentView.titleLabel.text = title - alertContentView.messageLabel.text = message - - if preferredStyle == .alert { - for handler in textFieldHandlers { - let textField = alertContentView.addTextField() - handler?(textField) - } - - textFieldHandlers.removeAll() - textFields.first?.becomeFirstResponder() - } - - configureContentView(alertContentView) - } - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - - layoutContainer() - layoutContents() - layoutButtons() - - let margin = marginInsets.top + marginInsets.bottom - let backgroundViewHeight = view.bounds.size.height - backgroundViewBottomSpace.constant - margin - - if cancelButtonScrollView.contentSize.height > cancelbuttonViewHeight.constant { - cancelbuttonViewHeight.constant = cancelButtonScrollView.contentSize.height - } - - if cancelbuttonViewHeight.constant > backgroundViewHeight { - cancelButtonScrollView.contentSize.height = cancelbuttonViewHeight.constant - cancelbuttonViewHeight.constant = backgroundViewHeight - - contentViewHeight.constant = 0 - alertButtonViewHeight.constant = 0 - } else { - let baseViewHeight = backgroundViewHeight - cancelbuttonViewHeight.constant - spaceBetweenAlertAndCancel.constant - if alertButtonScrollView.contentSize.height > alertButtonViewHeight.constant { - alertButtonViewHeight.constant = alertButtonScrollView.contentSize.height - } - - if alertButtonViewHeight.constant > baseViewHeight { - alertButtonScrollView.contentSize.height = alertButtonViewHeight.constant - alertButtonViewHeight.constant = baseViewHeight - contentViewHeight.constant = 0 - } else { - let mainViewHeight = baseViewHeight - alertButtonViewHeight.constant - if contentViewHeight.constant > mainViewHeight { - contentScrollView.contentSize.height = contentViewHeight.constant - contentViewHeight.constant = mainViewHeight - } - } - } - } - - open func addTextField(configurationHandler: ((UITextField) -> Void)? = nil) { - textFieldHandlers.append(configurationHandler) - } - - open func addAction(_ action: AlertAction) { - action.button.frame.size.height = preferredStyle.buttonHeight - action.button.addTarget(self, action: #selector(AlertController.buttonWasTapped(_:)), for: .touchUpInside) - - configureActionButton(action.button, at: action.style) - actions.append(action) - } - - open func configureActionButton(_ button: UIButton, at style: AlertAction.Style) { - if style == .destructive { - button.setTitleColor(.red, for: .normal) - } - button.titleLabel?.font = style.font(of: preferredStyle) - } - - open func configureContentView(_ contentView: AlertContentView) { - contentViewHandler?(contentView) - } - - @discardableResult - public func configureContentView(configurationHandler: @escaping (AlertContentView) -> Void) -> Self { - contentViewHandler = configurationHandler - return self - } -} - -extension AlertController { - func layoutContainer() { - let containerWidth: CGFloat - if preferredStyle == .actionSheet { - let margin: CGFloat = 8 - spaceBetweenAlertAndCancel.constant = margin - marginInsets = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin) - containerWidth = min(view.bounds.width, view.bounds.height) - marginInsets.left - marginInsets.right - } else { - containerWidth = contentWidth - } - - containerViewWidth.constant = containerWidth - containerView.layoutIfNeeded() - } - - func layoutContents() { - alertContentView.frame.size.width = contentScrollView.frame.size.width - alertContentView.layoutContents() - - contentViewHeight.constant = alertContentView.bounds.height - } - - func layoutButtons() { - let containerWidth = containerViewWidth.constant - let buttonActions: [AlertAction] - - if preferredStyle == .actionSheet { - buttonActions = actions.filter { $0.style != .cancel } - - let cancelActions = actions.filter { $0.style == .cancel } - layoutButtonVertically(with: cancelActions, width: containerWidth).forEach(cancelButtonView.addAction) - cancelbuttonViewHeight.constant = cancelActions.last?.button.frame.maxY ?? 0 - } else { - buttonActions = actions - } - - if preferredStyle == .alert && actions.count == 2 { - layoutButtonHorizontally(with: buttonActions, width: containerWidth).forEach(alertButtonView.addAction) - buttonActions.last?.addVerticalBorder() - } else { - layoutButtonVertically(with: buttonActions, width: containerWidth).forEach(alertButtonView.addAction) - } - alertButtonViewHeight.constant = buttonActions.last?.button.frame.maxY ?? 0 - } - - private func layoutButtonVertically(with actions: [AlertAction], width: CGFloat) -> [AlertAction] { - return actions - .reduce([]) { actions, action in - action.button.frame.size.width = width - action.button.frame.origin.y = actions.last?.button.frame.maxY ?? 0 - return actions + [action] - } - } - - private func layoutButtonHorizontally(with actions: [AlertAction], width: CGFloat) -> [AlertAction] { - return actions - .reduce([]) { actions, action in - action.button.frame.size.width = width / 2 - action.button.frame.origin.x = actions.last?.button.frame.maxX ?? 0 - return actions + [action] - } - } - - func dismiss(with sender: UIButton) { - guard let action = actions.filter({ $0.button == sender }).first else { - dismiss() - return - } - if action.dismissesAlert { - dismiss { - action.handler?(action) - } - } else { - action.handler?(action) - } - } - - func dismiss(withCompletion block: @escaping () -> Void = {}) { - dismiss(animated: true) { - block() - self.actions.removeAll() - self.alertContentView.textFields.removeAll() - } - } -} - -// MARK: - Action Methods -extension AlertController { - @objc func buttonWasTapped(_ button: UIButton) { - dismiss(with: button) - } - - @objc func backgroundViewTapAction(_ gesture: UITapGestureRecognizer) { - if containerView.frame.contains(gesture.location(in: marginView)) == false { - dismiss() - } - } -} - -// MARK: - NSNotificationCenter Methods -extension AlertController { - @objc func keyboardDidHide(_ notification: Notification) { - backgroundViewBottomSpace?.constant = 0 - } - - @objc func keyboardWillShow(_ notification: Notification) { - if let frame = notification.info.endFrame, let rect = view.window?.convert(frame, to: view) { - backgroundViewBottomSpace?.constant = view.bounds.size.height - rect.origin.y - } - } -} - -// MARK: - UIViewControllerTransitioningDelegate Methods -extension AlertController: UIViewControllerTransitioningDelegate { - public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { - switch preferredStyle { - case .alert: - return AlertControllerPresentTransition(backgroundColor: coverColor) - case .actionSheet: - return ActionSheetControllerPresentTransition(backgroundColor: coverColor, topSpace: self.backgroundViewTopSpace, bottomSpace: self.backgroundViewBottomSpace) - @unknown default: - fatalError() - } - } - - public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - switch preferredStyle { - case .alert: - return AlertControllerDismissTransition(backgroundColor: coverColor) - case .actionSheet: - return ActionSheetControllerDismissTransition(backgroundColor: coverColor, topSpace: self.backgroundViewTopSpace, bottomSpace: self.backgroundViewBottomSpace) - @unknown default: - fatalError() - } - } -} diff --git a/SimpleAlert/SimpleAlert.xib b/SimpleAlert/SimpleAlert.xib deleted file mode 100644 index 3053710..0000000 --- a/SimpleAlert/SimpleAlert.xib +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SimpleAlert/TextField.swift b/SimpleAlert/TextField.swift index d884667..3e4f76e 100644 --- a/SimpleAlert/TextField.swift +++ b/SimpleAlert/TextField.swift @@ -7,9 +7,10 @@ // final class TextField: UITextField { - override init(frame: CGRect) { - super.init(frame: frame) - autoresizingMask = .flexibleWidth + var handler: ((UITextField) -> Void)? + + init() { + super.init(frame: .zero) font = .systemFont(ofSize: 14) backgroundColor = UIColor.white layer.borderColor = UIColor.gray.cgColor @@ -19,6 +20,7 @@ final class TextField: UITextField { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + override func textRect(forBounds bounds: CGRect) -> CGRect { return bounds.offsetBy(dx: 4, dy: 0) } @@ -27,4 +29,3 @@ final class TextField: UITextField { return bounds.offsetBy(dx: 4, dy: 0) } } - diff --git a/SimpleAlert/ActionSheetControllerDismissTransition.swift b/SimpleAlert/Transition/ActionSheetControllerDismissTransition.swift similarity index 53% rename from SimpleAlert/ActionSheetControllerDismissTransition.swift rename to SimpleAlert/Transition/ActionSheetControllerDismissTransition.swift index cfca991..80b68cd 100644 --- a/SimpleAlert/ActionSheetControllerDismissTransition.swift +++ b/SimpleAlert/Transition/ActionSheetControllerDismissTransition.swift @@ -8,15 +8,19 @@ import UIKit -class ActionSheetControllerDismissTransition: ActionSheetControllerTransition { +class ActionSheetControllerDismissTransition: ViewControllerAnimatedTransition { override func animateTransition(_ from: UIViewController, to: UIViewController, container: UIView, completion: @escaping (Bool) -> Void) { container.addSubview(from.view) UIView.animate(withDuration: duration, animations: { - self.bottomSpace().constant = -from.view.bounds.height - self.topSpace().constant = from.view.bounds.height - from.view.backgroundColor = from.view.backgroundColor?.withAlphaComponent(0) - from.view.layoutIfNeeded() - }, completion: completion) + from.view.transform = CGAffineTransform(translationX: 0, y: from.view.bounds.height) + container.subviews.forEach { view in + if view !== from.view { + view.alpha = 0 + } + } + }) { _ in + completion(true) + } } } diff --git a/SimpleAlert/Transition/ActionSheetControllerPresentTransition.swift b/SimpleAlert/Transition/ActionSheetControllerPresentTransition.swift new file mode 100644 index 0000000..87e27a1 --- /dev/null +++ b/SimpleAlert/Transition/ActionSheetControllerPresentTransition.swift @@ -0,0 +1,31 @@ +// +// ActionSheetControllerPresentTransition.swift +// SimpleAlert +// +// Created by Kyohei Ito on 2017/11/25. +// Copyright © 2017年 kyohei_ito. All rights reserved. +// + +import UIKit + +class ActionSheetControllerPresentTransition: ViewControllerAnimatedTransition { + override func animateTransition(_ from: UIViewController, to: UIViewController, container: UIView, completion: @escaping (Bool) -> Void) { + let backgroundView = UIView(frame: container.bounds) + backgroundView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + backgroundView.alpha = 0 + backgroundView.backgroundColor = backgroundColor.withAlphaComponent(0.4) + container.addSubview(backgroundView) + + to.view.frame = container.bounds + container.addSubview(to.view) + + to.view.transform = CGAffineTransform(translationX: 0, y: to.view.bounds.height) + + UIView.animate(withDuration: duration, animations: { + to.view.transform = .identity + backgroundView.alpha = 1 + }) { _ in + completion(true) + } + } +} diff --git a/SimpleAlert/AlertControllerDismissTransition.swift b/SimpleAlert/Transition/AlertControllerDismissTransition.swift similarity index 77% rename from SimpleAlert/AlertControllerDismissTransition.swift rename to SimpleAlert/Transition/AlertControllerDismissTransition.swift index f1bf05e..9025dfc 100644 --- a/SimpleAlert/AlertControllerDismissTransition.swift +++ b/SimpleAlert/Transition/AlertControllerDismissTransition.swift @@ -13,7 +13,11 @@ class AlertControllerDismissTransition: ViewControllerAnimatedTransition { container.addSubview(from.view) UIView.animate(withDuration: duration, animations: { - from.view.alpha = 0 - }, completion: completion) + container.subviews.forEach { view in + view.alpha = 0 + } + }) { _ in + completion(true) + } } } diff --git a/SimpleAlert/AlertControllerPresentTransition.swift b/SimpleAlert/Transition/AlertControllerPresentTransition.swift similarity index 61% rename from SimpleAlert/AlertControllerPresentTransition.swift rename to SimpleAlert/Transition/AlertControllerPresentTransition.swift index 3c6bef5..e3663f3 100644 --- a/SimpleAlert/AlertControllerPresentTransition.swift +++ b/SimpleAlert/Transition/AlertControllerPresentTransition.swift @@ -10,15 +10,24 @@ import UIKit class AlertControllerPresentTransition: ViewControllerAnimatedTransition { override func animateTransition(_ from: UIViewController, to: UIViewController, container: UIView, completion: @escaping (Bool) -> Void) { + let backgroundView = UIView(frame: container.bounds) + backgroundView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + backgroundView.alpha = 0 + backgroundView.backgroundColor = backgroundColor.withAlphaComponent(0.4) + container.addSubview(backgroundView) + to.view.frame = container.bounds - to.view.backgroundColor = backgroundColor.withAlphaComponent(0.4) to.view.transform = from.view.transform.concatenating(CGAffineTransform(scaleX: 1.2, y: 1.2)) to.view.alpha = 0 container.addSubview(to.view) UIView.animate(withDuration: duration, animations: { to.view.transform = from.view.transform - to.view.alpha = 1 - }, completion: completion) + container.subviews.forEach { view in + view.alpha = 1 + } + }) { _ in + completion(true) + } } } diff --git a/SimpleAlert/ViewControllerAnimatedTransition.swift b/SimpleAlert/Transition/ViewControllerAnimatedTransition.swift similarity index 100% rename from SimpleAlert/ViewControllerAnimatedTransition.swift rename to SimpleAlert/Transition/ViewControllerAnimatedTransition.swift From 92c3923b642152addc001188ef20fbf0e4f7181b Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 17 Jun 2019 16:13:55 +0900 Subject: [PATCH 07/14] Support iOS 9.0 or later --- README.md | 4 ++-- SimpleAlert.podspec | 4 ++-- .../SimpleAlertExample.xcodeproj/project.pbxproj | 4 ++-- SimpleAlertExample/SimpleAlertExample/AlertController.swift | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bb26d7c..803261d 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Can be used as `UIAlertController`. ## Requirements -- Swift 4.2 -- iOS 8.0 or later +- Swift 5.0 +- iOS 9.0 or later ## How to Install SimpleAlert diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index 7ca2e40..4d57ef9 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "4.0.1" + s.version = "5.0.0" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { "Kyohei Ito" => "je.suis.kyohei@gmail.com" } s.swift_version = '5.0' - s.platform = :ios, "8.0" + s.platform = :ios, "9.0" s.source = { :git => "https://github.com/KyoheiG3/SimpleAlert.git", :tag => s.version.to_s } s.source_files = "SimpleAlert/**/*.{h,swift,xib}" s.requires_arc = true diff --git a/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj b/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj index 1c483be..c27ddf1 100644 --- a/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj +++ b/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj @@ -408,7 +408,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -456,7 +456,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/SimpleAlertExample/SimpleAlertExample/AlertController.swift b/SimpleAlertExample/SimpleAlertExample/AlertController.swift index 8d0460a..1cb67b5 100644 --- a/SimpleAlertExample/SimpleAlertExample/AlertController.swift +++ b/SimpleAlertExample/SimpleAlertExample/AlertController.swift @@ -47,5 +47,6 @@ class CustomAlertController: AlertController { contentView.messageLabel.font = UIFont.boldSystemFont(ofSize: 16) contentView.textBackgroundView.layer.cornerRadius = 10.0 contentView.textBackgroundView.clipsToBounds = true + contentView.textBackgroundView.backgroundColor = .lightGray } } From 711dd4afb17053026ca7e70ccd5c950b9d1b2e9b Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 17 Jun 2019 17:24:54 +0900 Subject: [PATCH 08/14] Remove highlighted title color --- SimpleAlert/AlertAction.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/SimpleAlert/AlertAction.swift b/SimpleAlert/AlertAction.swift index 2535231..b3e78f6 100644 --- a/SimpleAlert/AlertAction.swift +++ b/SimpleAlert/AlertAction.swift @@ -30,7 +30,6 @@ open class AlertAction { self.shouldDismisses = shouldDismisses button.setTitle(title, for: .normal) - button.setTitleColor(button.titleColor(for: .normal), for: .highlighted) button.setBackgroundImage(UIImage(color: .lightGray), for: .highlighted) } From 09a4a6f823d8ddd141f77d98e6b16d0815a26cb2 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 17 Jun 2019 17:25:31 +0900 Subject: [PATCH 09/14] Update podspec --- SimpleAlert.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index 4d57ef9..eecb8cb 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "5.0.0" + s.version = "5.0.1" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } From a88a8dfcaba2f5d73c6e1b5c47499b2e4d9d7e44 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 17 Jun 2019 18:12:35 +0900 Subject: [PATCH 10/14] Fix action sheet --- SimpleAlert/AlertController.swift | 29 ++++++++++++++++++----------- SimpleAlert/AlertController.xib | 9 +-------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/SimpleAlert/AlertController.swift b/SimpleAlert/AlertController.swift index 83f9931..2ae80d9 100644 --- a/SimpleAlert/AlertController.swift +++ b/SimpleAlert/AlertController.swift @@ -12,20 +12,19 @@ open class AlertController: UIViewController { enum Const { static let alertWidth: CGFloat = 270 static let actionSheetMargin: CGFloat = 16 - static let cornerRadius: CGFloat = 12 + static let cornerRadius: CGFloat = 13 static let textFieldHeight: CGFloat = 25 } - @IBOutlet weak var backgroundView: UIView! { + @IBOutlet weak var containerView: UIView! { didSet { if preferredStyle == .actionSheet { tapGesture.addTarget(self, action: #selector(AlertController.backgroundViewTapAction(_:))) - backgroundView.addGestureRecognizer(tapGesture) + containerView.addGestureRecognizer(tapGesture) } } } - @IBOutlet weak var containerView: UIView! @IBOutlet weak var containerStackView: UIStackView! @IBOutlet weak var contentEffectView: UIVisualEffectView! { @@ -235,6 +234,7 @@ open class AlertController: UIViewController { constraint = containerStackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor) } + constraint.priority = .defaultHigh constraint.isActive = true containerStackViewYAxisConstraint = constraint @@ -291,9 +291,20 @@ extension AlertController { .filter { $0.style == .cancel } .forEach(cancelButtonStackView.addAction) + if let borderView = cancelButtonStackView.arrangedSubviews.first { + cancelButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } + case .alert: actions.forEach(alertButtonStackView.addAction) + if alertButtonStackView.axis == .horizontal, let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + contentStackView.insertArrangedSubview(contentStackView.makeBorderView(), at: 1) + } + @unknown default: break } @@ -301,12 +312,6 @@ extension AlertController { zip(actions, actions.dropFirst()).forEach { top, bottom in top.button.widthAnchor.constraint(equalTo: bottom.button.widthAnchor).isActive = true } - - if alertButtonStackView.axis == .horizontal, let borderView = alertButtonStackView.arrangedSubviews.first { - alertButtonStackView.removeArrangedSubview(borderView) - borderView.removeFromSuperview() - contentStackView.insertArrangedSubview(contentStackView.makeBorderView(), at: 1) - } } func dismiss(with sender: UIButton) { @@ -339,7 +344,9 @@ extension AlertController { } @objc func backgroundViewTapAction(_ gesture: UITapGestureRecognizer) { - dismiss() + if !containerStackView.frame.contains(gesture.location(in: containerView)) { + dismiss() + } } } diff --git a/SimpleAlert/AlertController.xib b/SimpleAlert/AlertController.xib index 1d7aa53..ed00fbc 100644 --- a/SimpleAlert/AlertController.xib +++ b/SimpleAlert/AlertController.xib @@ -15,7 +15,6 @@ - @@ -34,9 +33,6 @@ - - - @@ -139,6 +135,7 @@ + @@ -147,12 +144,8 @@ - - - - From 399e4e3caa7f38909a8bafba0cf12a23000dcabf Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Mon, 17 Jun 2019 18:12:55 +0900 Subject: [PATCH 11/14] Update podspec --- SimpleAlert.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index eecb8cb..bd965c3 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "5.0.1" + s.version = "5.0.2" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } From 6379d53835b72fd4214be4e137a523bd3cda1730 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Thu, 1 Aug 2019 11:19:42 +0900 Subject: [PATCH 12/14] Remove top border when content is empty --- SimpleAlert/AlertContentView.swift | 3 ++- SimpleAlert/AlertController.swift | 25 +++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/SimpleAlert/AlertContentView.swift b/SimpleAlert/AlertContentView.swift index 90f5d09..f75b121 100644 --- a/SimpleAlert/AlertContentView.swift +++ b/SimpleAlert/AlertContentView.swift @@ -26,7 +26,8 @@ open class AlertContentView: UIView { titleLabel.isHidden = titleLabel.text?.isEmpty ?? true messageLabel.isHidden = messageLabel.text?.isEmpty ?? true textFieldView.isHidden = textFields.isEmpty - superview?.isHidden = titleLabel.isHidden && messageLabel.isHidden && textFieldView.isHidden + isHidden = titleLabel.isHidden && messageLabel.isHidden && textFieldView.isHidden + superview?.isHidden = isHidden } func append(_ textField: UITextField) { diff --git a/SimpleAlert/AlertController.swift b/SimpleAlert/AlertController.swift index 2ae80d9..9bea3e8 100644 --- a/SimpleAlert/AlertController.swift +++ b/SimpleAlert/AlertController.swift @@ -174,6 +174,7 @@ open class AlertController: UIViewController { } configureContentView(alertContentView) + alertContentView.layoutIfNeeded() layoutButtons() @@ -282,8 +283,8 @@ extension AlertController { func layoutButtons() { alertButtonStackView.axis = preferredStyle == .alert && actions.count == 2 ? .horizontal : .vertical - switch preferredStyle { - case .actionSheet: + switch (preferredStyle, alertButtonStackView.axis) { + case (.actionSheet, _): actions.lazy .filter { $0.style != .cancel } .forEach(alertButtonStackView.addAction) @@ -291,20 +292,36 @@ extension AlertController { .filter { $0.style == .cancel } .forEach(cancelButtonStackView.addAction) + if alertContentView.isHidden, let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } + if let borderView = cancelButtonStackView.arrangedSubviews.first { cancelButtonStackView.removeArrangedSubview(borderView) borderView.removeFromSuperview() } - case .alert: + case (.alert, .horizontal): actions.forEach(alertButtonStackView.addAction) - if alertButtonStackView.axis == .horizontal, let borderView = alertButtonStackView.arrangedSubviews.first { + if let borderView = alertButtonStackView.arrangedSubviews.first { alertButtonStackView.removeArrangedSubview(borderView) borderView.removeFromSuperview() + } + + if !alertContentView.isHidden { contentStackView.insertArrangedSubview(contentStackView.makeBorderView(), at: 1) } + case (.alert, .vertical): + actions.forEach(alertButtonStackView.addAction) + + if alertContentView.isHidden, let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } + @unknown default: break } From 057e8330941c8cac798489ce3fbebe8441543476 Mon Sep 17 00:00:00 2001 From: Kyohei Ito Date: Thu, 1 Aug 2019 11:21:27 +0900 Subject: [PATCH 13/14] Update podspec --- SimpleAlert.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleAlert.podspec b/SimpleAlert.podspec index bd965c3..393bda2 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "5.0.2" + s.version = "5.0.3" s.summary = "Simply Alert for Swift" s.homepage = "https://github.com/KyoheiG3/SimpleAlert" s.license = { :type => 'MIT', :file => 'LICENSE' } From 37e379c0a2942d560e1f3792917f3b7b201e8d03 Mon Sep 17 00:00:00 2001 From: Zaim Ramlan Date: Fri, 13 Mar 2020 11:49:14 +0800 Subject: [PATCH 14/14] rename alert controller to simple alert to see diff --- SimpleAlert/{AlertController.swift => SimpleAlert.swift} | 0 SimpleAlert/{AlertController.xib => SimpleAlert.xib} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename SimpleAlert/{AlertController.swift => SimpleAlert.swift} (100%) rename SimpleAlert/{AlertController.xib => SimpleAlert.xib} (99%) diff --git a/SimpleAlert/AlertController.swift b/SimpleAlert/SimpleAlert.swift similarity index 100% rename from SimpleAlert/AlertController.swift rename to SimpleAlert/SimpleAlert.swift diff --git a/SimpleAlert/AlertController.xib b/SimpleAlert/SimpleAlert.xib similarity index 99% rename from SimpleAlert/AlertController.xib rename to SimpleAlert/SimpleAlert.xib index ed00fbc..deacbf2 100644 --- a/SimpleAlert/AlertController.xib +++ b/SimpleAlert/SimpleAlert.xib @@ -10,7 +10,7 @@ - +