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 7d93e98..393bda2 100644 --- a/SimpleAlert.podspec +++ b/SimpleAlert.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |s| s.name = "SimpleAlert" - s.version = "3.1.1" + s.version = "5.0.3" 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.platform = :ios, "8.0" + s.swift_version = '5.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/SimpleAlert.xcodeproj/project.pbxproj b/SimpleAlert.xcodeproj/project.pbxproj index b7f2fea..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 */ @@ -204,12 +218,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 +233,7 @@ }; buildConfigurationList = C70C0C321A5FBEC8002BC071 /* Build configuration list for PBXProject "SimpleAlert" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -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; }; @@ -298,6 +314,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; @@ -341,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; @@ -356,6 +373,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; @@ -392,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"; @@ -419,7 +437,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -438,7 +456,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 @@ 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 193ada6..b3e78f6 100644 --- a/SimpleAlert/AlertAction.swift +++ b/SimpleAlert/AlertAction.swift @@ -23,44 +23,23 @@ 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.setBackgroundImage(UIImage(color: .lightGray), for: .highlighted) } let title: String let handler: ((AlertAction) -> Void)? let style: Style - let dismissesAlert: Bool - public let button = UIButton(type: .system) + 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..f75b121 100644 --- a/SimpleAlert/AlertContentView.swift +++ b/SimpleAlert/AlertContentView.swift @@ -9,67 +9,57 @@ 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 + isHidden = titleLabel.isHidden && messageLabel.isHidden && textFieldView.isHidden + superview?.isHidden = 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/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 87% rename from SimpleAlert/UIAlertControllerStyleExtension.swift rename to SimpleAlert/Extension/UIAlertControllerStyleExtension.swift index a9b6c6d..3deb5b3 100644 --- a/SimpleAlert/UIAlertControllerStyleExtension.swift +++ b/SimpleAlert/Extension/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 } } } 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 index 9d2e82b..9bea3e8 100644 --- a/SimpleAlert/SimpleAlert.swift +++ b/SimpleAlert/SimpleAlert.swift @@ -1,80 +1,107 @@ // -// SimpleAlert.swift +// AlertController.swift // SimpleAlert // -// Created by Kyohei Ito on 2015/01/09. -// Copyright (c) 2015年 kyohei_ito. All rights reserved. +// Created by Kyohei Ito on 2019/06/13. +// Copyright © 2019 kyohei_ito. All rights reserved. // import UIKit open class AlertController: UIViewController { - @IBOutlet weak var containerView: UIView! - @IBOutlet weak var backgroundView: UIView! { + enum Const { + static let alertWidth: CGFloat = 270 + static let actionSheetMargin: CGFloat = 16 + static let cornerRadius: CGFloat = 13 + static let textFieldHeight: CGFloat = 25 + } + + @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 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 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 containerStackView: UIStackView! - @IBOutlet weak var contentViewHeight: NSLayoutConstraint! - @IBOutlet weak var alertButtonViewHeight: NSLayoutConstraint! - @IBOutlet weak var cancelbuttonViewHeight: NSLayoutConstraint! - @IBOutlet weak var spaceBetweenAlertAndCancel: NSLayoutConstraint! + @IBOutlet weak var contentEffectView: UIVisualEffectView! { + didSet { + contentEffectView.clipsToBounds = true + } + } - @IBOutlet weak var marginViewTopSpace: NSLayoutConstraint! - @IBOutlet weak var marginViewLeftSpace: NSLayoutConstraint! - @IBOutlet weak var marginViewBottomSpace: NSLayoutConstraint! - @IBOutlet weak var marginViewRightSpace: NSLayoutConstraint! + @IBOutlet weak var cancelEffectView: UIVisualEffectView! { + didSet { + cancelEffectView.clipsToBounds = true + cancelEffectView.isHidden = preferredStyle == .alert + } + } - public private(set) var actions: [AlertAction] = [] - public var textFields: [UITextField] { - return alertContentView?.textFields ?? [] + @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 + } + } } - open var contentWidth: CGFloat = 270 + + @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 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 - } + private weak var customViewHeightConstraint: NSLayoutConstraint? { + didSet { + oldValue?.isActive = false } - 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) + } + + private weak var containerStackViewYAxisConstraint: NSLayoutConstraint? { + didSet { + oldValue?.isActive = false } } @@ -83,7 +110,8 @@ open class AlertController: UIViewController { } private convenience init() { - self.init(nibName: "SimpleAlert", bundle: Bundle(for: AlertController.self)) + let type = AlertController.self + self.init(nibName: String(describing: type), bundle: Bundle(for: type)) } public convenience init(title: String?, message: String?, style: UIAlertController.Style) { @@ -111,125 +139,123 @@ open class AlertController: UIViewController { 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, *) { - contentView.contentInsetAdjustmentBehavior = .never - alertButtonView.contentInsetAdjustmentBehavior = .never - cancelButtonView.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 + contentEffectView.backgroundColor = color + cancelEffectView.backgroundColor = color + contentEffectView.effect = nil + cancelEffectView.effect = nil } else { - contentBaseView.backgroundColor = .white - cancelBaseView.backgroundColor = .white + contentEffectView.backgroundColor = .white + cancelEffectView.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 - } - } + contentEffectView.layer.cornerRadius = contentCornerRadius ?? Const.cornerRadius + cancelEffectView.layer.cornerRadius = contentCornerRadius ?? Const.cornerRadius alertContentView.titleLabel.text = title alertContentView.messageLabel.text = message if preferredStyle == .alert { - for handler in textFieldHandlers { - let textField = alertContentView.addTextField() - handler?(textField) + textFields.forEach { textField in + alertContentView.append(textField) + (textField as? TextField)?.handler?(textField) } - textFieldHandlers.removeAll() textFields.first?.becomeFirstResponder() } + if let view = customView { + alertContentView.contentStackView.addArrangedSubview(view) + } + configureContentView(alertContentView) + alertContentView.layoutIfNeeded() + + 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 viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() + open override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) - layoutContainer() - layoutContents() - layoutButtons() + NotificationCenter.default.removeObserver(self) + alertContentView.endEditing(true) + } - let margin = marginInsets.top + marginInsets.bottom - let backgroundViewHeight = view.bounds.size.height - backgroundViewBottomSpace.constant - margin + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) - if cancelButtonView.contentSize.height > cancelbuttonViewHeight.constant { - cancelbuttonViewHeight.constant = cancelButtonView.contentSize.height + 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() } + } - if cancelbuttonViewHeight.constant > backgroundViewHeight { - cancelButtonView.contentSize.height = cancelbuttonViewHeight.constant - cancelbuttonViewHeight.constant = backgroundViewHeight + open override func updateViewConstraints() { + super.updateViewConstraints() - 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 - } + 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) - if alertButtonViewHeight.constant > baseViewHeight { - alertButtonView.contentSize.height = alertButtonViewHeight.constant - alertButtonViewHeight.constant = baseViewHeight - contentViewHeight.constant = 0 + 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 { - let mainViewHeight = baseViewHeight - alertButtonViewHeight.constant - if contentViewHeight.constant > mainViewHeight { - contentView.contentSize.height = contentViewHeight.constant - contentViewHeight.constant = mainViewHeight - } + bottomAnchor = containerView.bottomAnchor } + constraint = containerStackView.bottomAnchor.constraint(equalTo: bottomAnchor) + + @unknown default: + constraint = containerStackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor) + } + + constraint.priority = .defaultHigh + 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) { - textFieldHandlers.append(configurationHandler) + 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.buttonWasTapped(_:)), for: .touchUpInside) + action.button.addTarget(self, action: #selector(AlertController.buttonDidTap), for: .touchUpInside) configureActionButton(action.button, at: action.style) actions.append(action) @@ -254,67 +280,55 @@ open class AlertController: UIViewController { } 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 - } + func layoutButtons() { + alertButtonStackView.axis = preferredStyle == .alert && actions.count == 2 ? .horizontal : .vertical + + switch (preferredStyle, alertButtonStackView.axis) { + case (.actionSheet, _): + actions.lazy + .filter { $0.style != .cancel } + .forEach(alertButtonStackView.addAction) + actions.lazy + .filter { $0.style == .cancel } + .forEach(cancelButtonStackView.addAction) + + if alertContentView.isHidden, let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } - containerViewWidth.constant = containerWidth - containerView.layoutIfNeeded() - } + if let borderView = cancelButtonStackView.arrangedSubviews.first { + cancelButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } - func layoutContents() { - alertContentView.frame.size.width = contentView.frame.size.width - alertContentView.layoutContents() + case (.alert, .horizontal): + actions.forEach(alertButtonStackView.addAction) - contentViewHeight.constant = alertContentView.bounds.height - } + if let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } - func layoutButtons() { - let containerWidth = containerViewWidth.constant - let buttonActions: [AlertAction] + if !alertContentView.isHidden { + contentStackView.insertArrangedSubview(contentStackView.makeBorderView(), at: 1) + } - if preferredStyle == .actionSheet { - buttonActions = actions.filter { $0.style != .cancel } + case (.alert, .vertical): + actions.forEach(alertButtonStackView.addAction) - 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 alertContentView.isHidden, let borderView = alertButtonStackView.arrangedSubviews.first { + alertButtonStackView.removeArrangedSubview(borderView) + borderView.removeFromSuperview() + } - 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) + @unknown default: + break } - 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] - } + zip(actions, actions.dropFirst()).forEach { top, bottom in + top.button.widthAnchor.constraint(equalTo: bottom.button.widthAnchor).isActive = true + } } func dismiss(with sender: UIButton) { @@ -322,7 +336,7 @@ extension AlertController { dismiss() return } - if action.dismissesAlert { + if action.shouldDismisses { dismiss { action.handler?(action) } @@ -335,19 +349,19 @@ extension AlertController { dismiss(animated: true) { block() self.actions.removeAll() - self.alertContentView.textFields.removeAll() + self.textFields.removeAll() } } } // MARK: - Action Methods extension AlertController { - @objc func buttonWasTapped(_ button: UIButton) { + @objc func buttonDidTap(_ button: UIButton) { dismiss(with: button) } @objc func backgroundViewTapAction(_ gesture: UITapGestureRecognizer) { - if containerView.frame.contains(gesture.location(in: marginView)) == false { + if !containerStackView.frame.contains(gesture.location(in: containerView)) { dismiss() } } @@ -355,13 +369,16 @@ extension AlertController { // 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 + @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() + }) } } } @@ -373,7 +390,9 @@ extension AlertController: UIViewControllerTransitioningDelegate { case .alert: return AlertControllerPresentTransition(backgroundColor: coverColor) case .actionSheet: - return ActionSheetControllerPresentTransition(backgroundColor: coverColor, topSpace: self.backgroundViewTopSpace, bottomSpace: self.backgroundViewBottomSpace) + return ActionSheetControllerPresentTransition(backgroundColor: coverColor) + @unknown default: + fatalError() } } @@ -382,7 +401,9 @@ extension AlertController: UIViewControllerTransitioningDelegate { case .alert: return AlertControllerDismissTransition(backgroundColor: coverColor) case .actionSheet: - return ActionSheetControllerDismissTransition(backgroundColor: coverColor, topSpace: self.backgroundViewTopSpace, bottomSpace: self.backgroundViewBottomSpace) + return ActionSheetControllerDismissTransition(backgroundColor: coverColor) + @unknown default: + fatalError() } } } diff --git a/SimpleAlert/SimpleAlert.xib b/SimpleAlert/SimpleAlert.xib index 6d674d1..deacbf2 100644 --- a/SimpleAlert/SimpleAlert.xib +++ b/SimpleAlert/SimpleAlert.xib @@ -1,210 +1,153 @@ - - + + - + + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - + + + - - + + - - + + - - - - - - - - + + + + + + + + - - + + + + + + + - + + + + + + - - + + + + + + + - + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - + + + - - - - + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - + + + + - - - - - - - - - - - - - - - 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 diff --git a/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj b/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj index 690253d..c27ddf1 100644 --- a/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj +++ b/SimpleAlertExample/SimpleAlertExample.xcodeproj/project.pbxproj @@ -229,23 +229,23 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = kyohei_ito; TargetAttributes = { C70C0C601A5FC018002BC071 = { CreatedOnToolsVersion = 6.2; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; C70C0C751A5FC019002BC071 = { CreatedOnToolsVersion = 6.2; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; TestTargetID = C70C0C601A5FC018002BC071; }; }; }; buildConfigurationList = C70C0C5C1A5FC018002BC071 /* Build configuration list for PBXProject "SimpleAlertExample" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -365,6 +365,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; @@ -407,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; @@ -419,6 +420,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; @@ -454,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"; @@ -471,7 +473,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kyoheiito.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -484,7 +486,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kyoheiito.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -500,7 +502,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kyoheiito.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SimpleAlertExample.app/SimpleAlertExample"; }; name = Debug; @@ -513,7 +515,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kyoheiito.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SimpleAlertExample.app/SimpleAlertExample"; }; name = Release; 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 } }