From 2802672a1de4b26e8f71d3d27bac904f411689ed Mon Sep 17 00:00:00 2001 From: Scott Berrevoets Date: Fri, 30 Jun 2017 23:20:26 -0700 Subject: [PATCH 1/5] Migrate to Swift 4 --- .swift-version | 2 +- SDCAlertView.podspec | 4 ++-- SDCAlertView.xcodeproj/project.pbxproj | 4 ++-- Source/AlertController.swift | 5 +++-- Source/Views/AlertControllerView.swift | 1 + Source/Views/AlertView.swift | 18 ++++++++++++------ 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.swift-version b/.swift-version index 9f55b2cc..5186d070 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.0 +4.0 diff --git a/SDCAlertView.podspec b/SDCAlertView.podspec index 1304cc78..39f67217 100644 --- a/SDCAlertView.podspec +++ b/SDCAlertView.podspec @@ -8,8 +8,8 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/sberrevoets/SDCAlertView.git", :tag => "v#{s.version}" } s.social_media_url = "https://twitter.com/ScottBerrevoets" - s.source_files = "Source/**/*.{swift,xib}", "Source/Supporting Files/UIView+SDCAutoLayout.{h,m}" + s.source_files = "Source/**/*.{swift,xib}" - s.ios.deployment_target = 8.0 + s.ios.deployment_target = 9.0 s.requires_arc = true end diff --git a/SDCAlertView.xcodeproj/project.pbxproj b/SDCAlertView.xcodeproj/project.pbxproj index b781211f..eea4e261 100644 --- a/SDCAlertView.xcodeproj/project.pbxproj +++ b/SDCAlertView.xcodeproj/project.pbxproj @@ -302,7 +302,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -332,7 +332,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.ScottyDoesntCode.SDCAlertView; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/Source/AlertController.swift b/Source/AlertController.swift index 354ebc23..40e2aeb4 100644 --- a/Source/AlertController.swift +++ b/Source/AlertController.swift @@ -30,7 +30,7 @@ public enum ActionLayout: Int { @objc(SDCAlertController) public class AlertController: UIViewController { - private lazy var assignResponder: () -> Bool = { [weak self] _ in + private lazy var assignResponder: () -> Bool = { [weak self] in self?.textFields?.first?.becomeFirstResponder() ?? false } @@ -301,7 +301,8 @@ public class AlertController: UIViewController { self.alertView.sdc_centerInSuperview() let maximumHeightOffset = -(margins.top + margins.bottom) self.alertView.sdc_setMaximumHeightToSuperviewHeight(withOffset: maximumHeightOffset) - self.alertView.setContentCompressionResistancePriority(500, for: .vertical) + let priority = UILayoutPriority(rawValue: 500) + self.alertView.setContentCompressionResistancePriority(priority, for: .vertical) } } diff --git a/Source/Views/AlertControllerView.swift b/Source/Views/AlertControllerView.swift index 55c7f3cd..80dac11a 100644 --- a/Source/Views/AlertControllerView.swift +++ b/Source/Views/AlertControllerView.swift @@ -90,6 +90,7 @@ class AlertControllerView: UIView, AlertControllerViewRepresentable { self.actionsCollectionView.visualStyle = self.visualStyle } + @objc func highlightAction(for sender: UIPanGestureRecognizer) { self.actionsCollectionView.highlightAction(for: sender) } diff --git a/Source/Views/AlertView.swift b/Source/Views/AlertView.swift index 183b215c..736962e9 100644 --- a/Source/Views/AlertView.swift +++ b/Source/Views/AlertView.swift @@ -121,7 +121,7 @@ class AlertView: AlertControllerView { let insets = UIEdgeInsets(top: 0, left: contentPadding.left, bottom: 0, right: -contentPadding.right) self.titleLabel.sdc_alignEdges([.left, .right], with: self, insets: insets) - self.pinBottomOfScrollView(to: self.titleLabel, withPriority: UILayoutPriorityDefaultLow) + self.pinBottomOfScrollView(to: self.titleLabel, withPriority: .defaultLow) } private func createMessageLabelConstraints() { @@ -132,7 +132,7 @@ class AlertView: AlertControllerView { let insets = UIEdgeInsets(top: 0, left: contentPadding.left, bottom: 0, right: -contentPadding.right) self.messageLabel.sdc_alignEdges([.left, .right], with: self, insets: insets) - self.pinBottomOfScrollView(to: self.messageLabel, withPriority: UILayoutPriorityDefaultLow + 1) + self.pinBottomOfScrollView(to: self.messageLabel, withPriority: .defaultLow + 1.0) } private func createTextFieldsConstraints() { @@ -155,7 +155,7 @@ class AlertView: AlertControllerView { textFieldsView.sdc_alignHorizontalCenter(with: self) textFieldsView.sdc_pinHeight(height) - self.pinBottomOfScrollView(to: textFieldsView, withPriority: UILayoutPriorityDefaultLow + 2) + self.pinBottomOfScrollView(to: textFieldsView, withPriority: .defaultLow + 2.0) } private func createCustomContentViewConstraints() { @@ -169,14 +169,14 @@ class AlertView: AlertControllerView { self.contentView.sdc_alignHorizontalCenter(with: self) self.contentView.sdc_pinWidth(toWidthOf: self, offset: -widthOffset) - self.pinBottomOfScrollView(to: self.contentView, withPriority: UILayoutPriorityDefaultLow + 3) + self.pinBottomOfScrollView(to: self.contentView, withPriority: .defaultLow + 3.0) } private func createCollectionViewConstraints() { let height = self.actionsCollectionView.displayHeight let heightConstraint = NSLayoutConstraint(item: self.actionsCollectionView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height) - heightConstraint.priority = UILayoutPriorityDefaultHigh + heightConstraint.priority = .defaultHigh self.actionsCollectionView.addConstraint(heightConstraint) self.actionsCollectionView.sdc_pinWidth(toWidthOf: self) self.actionsCollectionView.sdc_alignEdge(.top, with: .bottom, of: self.scrollView) @@ -191,7 +191,7 @@ class AlertView: AlertControllerView { let scrollViewHeight = self.scrollView.contentSize.height let constraint = NSLayoutConstraint(item: self.scrollView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: scrollViewHeight) - constraint.priority = UILayoutPriorityDefaultHigh + constraint.priority = .defaultHigh self.scrollView.addConstraint(constraint) } @@ -203,3 +203,9 @@ class AlertView: AlertControllerView { self.addConstraint(bottomAnchor) } } + +private extension UILayoutPriority { + static func + (lhs: UILayoutPriority, rhs: Float) -> UILayoutPriority { + return UILayoutPriority(rawValue: lhs.rawValue + rhs) + } +} From 10f7ae72df4e6534f4e0daecba9d04d24a100da5 Mon Sep 17 00:00:00 2001 From: Scott Berrevoets Date: Mon, 10 Jul 2017 11:03:46 -0700 Subject: [PATCH 2/5] Change internals for Swift 4 goodies --- Example.xcodeproj/project.pbxproj | 4 +- Example/Base.lproj/Main.storyboard | 153 ++++---- Example/DemoViewController.swift | 6 +- Example/TestsViewController.swift | 1 - SDCAlertView.xcodeproj/project.pbxproj | 20 +- Source/Actions/ActionsCollectionView.swift | 1 + Source/AlertBehaviors.swift | 29 +- Source/AlertController.swift | 161 +++++---- Source/AlertVisualStyle.swift | 29 +- Source/Presentation/AnimationController.swift | 4 +- Source/Supporting Files/SDCAlertView.h | 2 - .../Supporting Files/UIView+SDCAutoLayout.h | 85 ----- .../Supporting Files/UIView+SDCAutoLayout.m | 329 ------------------ Source/Text Fields/TextFieldCell.swift | 12 +- Source/Views/ActionSheetView.swift | 28 +- Source/Views/ActionSheetView.xib | 31 +- Source/Views/AlertControllerView.swift | 97 ------ .../AlertControllerViewRepresentable.swift | 45 +++ Source/Views/AlertView.swift | 140 +++++--- 19 files changed, 370 insertions(+), 807 deletions(-) delete mode 100644 Source/Supporting Files/UIView+SDCAutoLayout.h delete mode 100644 Source/Supporting Files/UIView+SDCAutoLayout.m delete mode 100644 Source/Views/AlertControllerView.swift create mode 100644 Source/Views/AlertControllerViewRepresentable.swift diff --git a/Example.xcodeproj/project.pbxproj b/Example.xcodeproj/project.pbxproj index c5e83ced..a76861f7 100644 --- a/Example.xcodeproj/project.pbxproj +++ b/Example.xcodeproj/project.pbxproj @@ -384,7 +384,7 @@ DEVELOPMENT_TEAM = ""; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = Example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.ScottyDoesntCode.Example; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -403,7 +403,7 @@ DEVELOPMENT_TEAM = ""; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = Example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.ScottyDoesntCode.Example; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Example/Base.lproj/Main.storyboard b/Example/Base.lproj/Main.storyboard index 3a384d1a..826e5410 100644 --- a/Example/Base.lproj/Main.storyboard +++ b/Example/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - - + + - + @@ -16,7 +16,7 @@ - + @@ -32,22 +32,22 @@ - - + + - + - + - + @@ -61,14 +61,14 @@ - + - + - + @@ -86,14 +86,14 @@ - + - + - + @@ -107,14 +107,14 @@ - + - + - + @@ -132,14 +132,14 @@ - + - + - + @@ -161,14 +161,14 @@ - + - + - + @@ -190,21 +190,21 @@ - + - + - + - - + - + - + @@ -236,8 +236,8 @@ - @@ -515,7 +516,7 @@ running Xcode 7's UI tests - + diff --git a/Example/DemoViewController.swift b/Example/DemoViewController.swift index acfb3b45..72f987a3 100644 --- a/Example/DemoViewController.swift +++ b/Example/DemoViewController.swift @@ -45,14 +45,10 @@ final class DemoViewController: UITableViewController { } alert.actionLayout = ActionLayout(rawValue: self.buttonLayoutControl.selectedSegmentIndex)! - - if #available(iOS 9, *) { - addContentToAlert(alert) - } + addContentToAlert(alert) alert.present() } - @available(iOS 9, *) private func addContentToAlert(_ alert: AlertController) { switch self.contentControl.selectedSegmentIndex { case 1: diff --git a/Example/TestsViewController.swift b/Example/TestsViewController.swift index 7b1525ab..f2f21f3e 100644 --- a/Example/TestsViewController.swift +++ b/Example/TestsViewController.swift @@ -1,6 +1,5 @@ import SDCAlertView -@available(iOS 9, *) class TestsViewController: UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { diff --git a/SDCAlertView.xcodeproj/project.pbxproj b/SDCAlertView.xcodeproj/project.pbxproj index eea4e261..2525a5d7 100644 --- a/SDCAlertView.xcodeproj/project.pbxproj +++ b/SDCAlertView.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ D23B53751BF473BC00AB5BBE /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23B53741BF473BC00AB5BBE /* AlertView.swift */; }; D23B53771BF473CC00AB5BBE /* ActionSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23B53761BF473CC00AB5BBE /* ActionSheetView.swift */; }; D2515EC41BC1A7D500ED606F /* SDCAlertView.h in Headers */ = {isa = PBXBuildFile; fileRef = D2515EC31BC1A7D500ED606F /* SDCAlertView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2515ECB1BC1A83800ED606F /* UIView+SDCAutoLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C30AA71BC19F9300984652 /* UIView+SDCAutoLayout.m */; }; D2515ECD1BC1A84D00ED606F /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A701BC120C900984652 /* ActionCell.swift */; }; D2515ECE1BC1A84D00ED606F /* ActionCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2C30A711BC120C900984652 /* ActionCell.xib */; }; D2515ECF1BC1A84D00ED606F /* ActionsCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A721BC120C900984652 /* ActionsCollectionView.swift */; }; @@ -24,10 +23,9 @@ D2515ED61BC1A85800ED606F /* TextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A7E1BC120C900984652 /* TextFieldCell.swift */; }; D2515ED71BC1A85800ED606F /* TextFieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2C30A7F1BC120C900984652 /* TextFieldCell.xib */; }; D2515ED81BC1A85800ED606F /* TextFieldsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A801BC120C900984652 /* TextFieldsViewController.swift */; }; - D2515EDA1BC1A85800ED606F /* AlertControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A761BC120C900984652 /* AlertControllerView.swift */; }; + D2515EDA1BC1A85800ED606F /* AlertControllerViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A761BC120C900984652 /* AlertControllerViewRepresentable.swift */; }; D2515EDB1BC1A85800ED606F /* AlertLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A771BC120C900984652 /* AlertLabel.swift */; }; D2515EDC1BC1A85800ED606F /* AlertVisualStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C30A811BC120C900984652 /* AlertVisualStyle.swift */; }; - D2515EDE1BC1A94A00ED606F /* UIView+SDCAutoLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C30AA61BC19F9300984652 /* UIView+SDCAutoLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; D253B1C81C2AC78D00C37E0F /* AlertBehaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D253B1C71C2AC78D00C37E0F /* AlertBehaviors.swift */; }; D2A48B441C2E09CE009DD715 /* AlertController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A48B431C2E09CE009DD715 /* AlertController+Convenience.swift */; }; D2DE585A1C08261E0041DFDE /* ActionSheetView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2DE58591C08261E0041DFDE /* ActionSheetView.xib */; }; @@ -49,7 +47,7 @@ D2C30A731BC120C900984652 /* ActionsCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionsCollectionViewFlowLayout.swift; sourceTree = ""; }; D2C30A741BC120C900984652 /* AlertAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertAction.swift; sourceTree = ""; }; D2C30A751BC120C900984652 /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = ""; }; - D2C30A761BC120C900984652 /* AlertControllerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertControllerView.swift; sourceTree = ""; }; + D2C30A761BC120C900984652 /* AlertControllerViewRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertControllerViewRepresentable.swift; sourceTree = ""; }; D2C30A771BC120C900984652 /* AlertLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertLabel.swift; sourceTree = ""; }; D2C30A791BC120C900984652 /* AnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationController.swift; sourceTree = ""; }; D2C30A7A1BC120C900984652 /* PresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationController.swift; sourceTree = ""; }; @@ -59,8 +57,6 @@ D2C30A7F1BC120C900984652 /* TextFieldCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextFieldCell.xib; sourceTree = ""; }; D2C30A801BC120C900984652 /* TextFieldsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldsViewController.swift; sourceTree = ""; }; D2C30A811BC120C900984652 /* AlertVisualStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertVisualStyle.swift; sourceTree = ""; }; - D2C30AA61BC19F9300984652 /* UIView+SDCAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDCAutoLayout.h"; sourceTree = ""; }; - D2C30AA71BC19F9300984652 /* UIView+SDCAutoLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDCAutoLayout.m"; sourceTree = ""; }; D2DE58591C08261E0041DFDE /* ActionSheetView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ActionSheetView.xib; sourceTree = ""; }; /* End PBXFileReference section */ @@ -80,7 +76,7 @@ children = ( D23B53761BF473CC00AB5BBE /* ActionSheetView.swift */, D2DE58591C08261E0041DFDE /* ActionSheetView.xib */, - D2C30A761BC120C900984652 /* AlertControllerView.swift */, + D2C30A761BC120C900984652 /* AlertControllerViewRepresentable.swift */, D2C30A771BC120C900984652 /* AlertLabel.swift */, D23B53741BF473BC00AB5BBE /* AlertView.swift */, 0BA53BDA1F31D23D003854F3 /* UIView+Accessibility.swift */, @@ -101,8 +97,6 @@ children = ( D2515EC51BC1A7D500ED606F /* Info.plist */, D2515EC31BC1A7D500ED606F /* SDCAlertView.h */, - D2C30AA61BC19F9300984652 /* UIView+SDCAutoLayout.h */, - D2C30AA71BC19F9300984652 /* UIView+SDCAutoLayout.m */, ); path = "Supporting Files"; sourceTree = ""; @@ -173,7 +167,6 @@ buildActionMask = 2147483647; files = ( D2515EC41BC1A7D500ED606F /* SDCAlertView.h in Headers */, - D2515EDE1BC1A94A00ED606F /* UIView+SDCAutoLayout.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -259,12 +252,11 @@ D2515ED11BC1A84D00ED606F /* AlertAction.swift in Sources */, D2515ED31BC1A85200ED606F /* PresentationController.swift in Sources */, D23B53771BF473CC00AB5BBE /* ActionSheetView.swift in Sources */, - D2515EDA1BC1A85800ED606F /* AlertControllerView.swift in Sources */, + D2515EDA1BC1A85800ED606F /* AlertControllerViewRepresentable.swift in Sources */, D2515ECF1BC1A84D00ED606F /* ActionsCollectionView.swift in Sources */, D23B53751BF473BC00AB5BBE /* AlertView.swift in Sources */, D2DE585B1C082F460041DFDE /* AlertController.swift in Sources */, D2A48B441C2E09CE009DD715 /* AlertController+Convenience.swift in Sources */, - D2515ECB1BC1A83800ED606F /* UIView+SDCAutoLayout.m in Sources */, D2515ED01BC1A84D00ED606F /* ActionsCollectionViewFlowLayout.swift in Sources */, D2515EDC1BC1A85800ED606F /* AlertVisualStyle.swift in Sources */, D2515ED41BC1A85200ED606F /* Transition.swift in Sources */, @@ -295,7 +287,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "$(SRCROOT)/Source/Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = com.ScottyDoesntCode.SDCAlertView; @@ -326,7 +318,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "$(SRCROOT)/Source/Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = com.ScottyDoesntCode.SDCAlertView; diff --git a/Source/Actions/ActionsCollectionView.swift b/Source/Actions/ActionsCollectionView.swift index f163bae6..3d669243 100644 --- a/Source/Actions/ActionsCollectionView.swift +++ b/Source/Actions/ActionsCollectionView.swift @@ -40,6 +40,7 @@ class ActionsCollectionView: UICollectionView { self.delegate = self self.backgroundColor = .clear self.delaysContentTouches = false + self.translatesAutoresizingMaskIntoConstraints = false self.collectionViewLayout.register(ActionSeparatorView.self, forDecorationViewOfKind: kHorizontalActionSeparator) diff --git a/Source/AlertBehaviors.swift b/Source/AlertBehaviors.swift index 383c2cc0..5c563b8c 100644 --- a/Source/AlertBehaviors.swift +++ b/Source/AlertBehaviors.swift @@ -2,32 +2,25 @@ public struct AlertBehaviors: OptionSet { /// When applied, the user can dismiss the alert or action sheet by tapping outside of it. Enabled for /// action sheets by default. - public static let DismissOnOutsideTap = AlertBehaviors(rawValue: 1) + public static let dismissOnOutsideTap = AlertBehaviors(rawValue: 1) /// Applies the "drag tap" behavior, meaning when the user taps on an action and then drags their finger - /// to another action the new action will be selected. Enabled on iOS 9 by default for both alerts and - /// action sheets. - public static let DragTap = AlertBehaviors(rawValue: 1 << 1) - - /// Adds a parallax effect to the alert. Does not apply to action sheets. Enabled on iOS 8 by default. - public static let Parallax = AlertBehaviors(rawValue: 1 << 2) + /// to another action the new action will be selected. + public static let dragTap = AlertBehaviors(rawValue: 1 << 1) /// Automatically focuses the first text field in an alert. This doesn't work for text fields added to an /// alert's content view. - public static let AutomaticallyFocusTextField = AlertBehaviors(rawValue: 1 << 3) - - static func defaultBehaviorsForAlert(with style: AlertControllerStyle) -> AlertBehaviors { - var behaviors: AlertBehaviors = [] + public static let automaticallyFocusTextField = AlertBehaviors(rawValue: 1 << 3) - if #available(iOS 9, *) { - behaviors.insert([.DragTap]) - } else if style == .alert { - behaviors.insert([.Parallax]) - } + static func defaultBehaviors(forStyle style: AlertControllerStyle) -> AlertBehaviors { + let behaviors: AlertBehaviors = [.dragTap] switch style { - case .actionSheet: return behaviors.union(.DismissOnOutsideTap) - case .alert: return behaviors.union(.AutomaticallyFocusTextField) + case .actionSheet: + return behaviors.union(.dismissOnOutsideTap) + + case .alert: + return behaviors.union(.automaticallyFocusTextField) } } diff --git a/Source/AlertController.swift b/Source/AlertController.swift index 40e2aeb4..0fe0baa8 100644 --- a/Source/AlertController.swift +++ b/Source/AlertController.swift @@ -3,8 +3,7 @@ import UIKit /// The alert controller's style /// /// - actionSheet: An action sheet style alert that slides in from the bottom and presents the user with a -/// list of possible actions to perform. Only available on iOS 9, and does not show as expected -/// on iPad. +/// list of possible actions to perform. Does not show as expected on iPad. /// - alert: The standard alert style that asks the user for information or confirmation. @objc(SDCAlertControllerStyle) public enum AlertControllerStyle: Int { @@ -13,9 +12,8 @@ public enum AlertControllerStyle: Int { } -/// The layout of the alert's actions. Only applies to the Alert style alerts, not ActionSheet (see +/// The layout of the alert's actions. Only applies to AlertControllerStyle.alert, not .actionSheet (see /// `AlertControllerStyle`). - /// /// - automatic: If the alert has 2 actions, display them horizontally. Otherwise, display them vertically. /// - vertical: Display the actions vertically. @@ -30,9 +28,7 @@ public enum ActionLayout: Int { @objc(SDCAlertController) public class AlertController: UIViewController { - private lazy var assignResponder: () -> Bool = { [weak self] in - self?.textFields?.first?.becomeFirstResponder() ?? false - } + private var verticalCenter: NSLayoutConstraint? /// The alert's title. Directly uses `attributedTitle` without any attributes. override public var title: String? { @@ -48,32 +44,31 @@ public class AlertController: UIViewController { /// A stylized title for the alert. public var attributedTitle: NSAttributedString? { - get { return self.alertView.title } - set { self.alertView.title = newValue } + get { return self.alert.title } + set { self.alert.title = newValue } } /// A stylized message for the alert. public var attributedMessage: NSAttributedString? { - get { return self.alertView.message } - set { self.alertView.message = newValue } + get { return self.alert.message } + set { self.alert.message = newValue } } /// The alert's content view. This can be used to add custom views to your alert. The width of the content /// view is equal to the width of the alert, minus padding. The height must be defined manually since it /// depends on the size of the subviews. public var contentView: UIView { - return self.alertView.contentView + return self.alert.contentView } /// The alert's actions (buttons). private(set) public var actions = [AlertAction]() { - didSet { self.alertView.actions = self.actions } + didSet { self.alert.actions = self.actions } } /// The alert's preferred action, if one is set. Setting this value to an action that wasn't already added - /// to the array will add it and override its style to `.Preferred`. Setting this value to `nil` will + /// to the array will add it and override its style to `.preferred`. Setting this value to `nil` will /// remove the preferred style from all actions. - @available(iOS 9, *) public var preferredAction: AlertAction? { get { let index = self.actions.index { $0.style == .preferred } @@ -94,16 +89,15 @@ public class AlertController: UIViewController { /// The layout of the actions in the alert. public var actionLayout: ActionLayout { - get { return (self.alertView as? AlertView)?.actionLayout ?? .automatic } - set { (self.alertView as? AlertView)?.actionLayout = newValue } + get { return (self.alert as? AlertView)?.actionLayout ?? .automatic } + set { (self.alert as? AlertView)?.actionLayout = newValue } } /// The text fields that are added to the alert. Does nothing when used with an action sheet. private(set) public var textFields: [UITextField]? /// The alert's custom behaviors. See `AlertBehaviors` for possible options. - public lazy var behaviors: AlertBehaviors? = - AlertBehaviors.defaultBehaviorsForAlert(with: self.preferredStyle) + public lazy var behaviors: AlertBehaviors = AlertBehaviors.defaultBehaviors(forStyle: self.preferredStyle) /// A closure that, when set, returns whether the alert or action sheet should dismiss after the user taps /// on an action. If it returns false, the AlertAction handler will not be executed. @@ -118,7 +112,7 @@ public class AlertController: UIViewController { /// The alert's presentation style. private(set) public var preferredStyle: AlertControllerStyle = .alert - @IBOutlet private var alertView: AlertControllerView! = AlertView() + private let alert: UIView & AlertControllerViewRepresentable private lazy var transitionDelegate: Transition = Transition(alertStyle: self.preferredStyle) // MARK: - Initialization @@ -133,7 +127,7 @@ public class AlertController: UIViewController { public convenience init(attributedTitle: NSAttributedString?, attributedMessage: NSAttributedString?, preferredStyle: AlertControllerStyle = .alert) { - self.init() + self.init(preferredStyle: preferredStyle) self.preferredStyle = preferredStyle self.commonInit() @@ -149,7 +143,7 @@ public class AlertController: UIViewController { /// - parameter message: An optional message /// - parameter preferredStyle: The preferred presentation style of the alert. Default is `alert`. public convenience init(title: String?, message: String?, preferredStyle: AlertControllerStyle = .alert) { - self.init() + self.init(preferredStyle: preferredStyle) self.preferredStyle = preferredStyle self.commonInit() @@ -157,14 +151,36 @@ public class AlertController: UIViewController { self.message = message } + private init(preferredStyle: AlertControllerStyle) { + switch preferredStyle { + case .alert: + self.alert = AlertView() + + case .actionSheet: + let nibName = String(describing: ActionSheetView.self) + let objects = Bundle(for: ActionSheetView.self).loadNibNamed(nibName, owner: nil, options: nil) + if let actionSheet = objects?.first as? ActionSheetView { + self.alert = actionSheet + } else { + self.alert = AlertView() + } + } + + super.init(nibName: nil, bundle: nil) + } + + public required init?(coder aDecoder: NSCoder) { + preconditionFailure("Please use one of the provided AlertController initializers") + } + private func commonInit() { self.modalPresentationStyle = .custom self.transitioningDelegate = self.transitionDelegate + } - if self.preferredStyle == .actionSheet { - let nibName = String(describing: ActionSheetView.self) - Bundle(for: type(of: self)).loadNibNamed(nibName, owner: self, options: nil) - } + public override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + self.textFields?.first?.resignFirstResponder() } // MARK: - Public @@ -186,10 +202,8 @@ public class AlertController: UIViewController { let textField = UITextField() textField.autocorrectionType = .no configurationHandler?(textField) - - if self.textFields?.append(textField) == nil { - self.textFields = [textField] - } + let currentTextFields = self.textFields ?? [] + self.textFields = currentTextFields + [textField] } /// Presents the alert. @@ -219,17 +233,6 @@ public class AlertController: UIViewController { self.configureAlertView() } - public override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - - // Explanation of why the first responder is set here: - // http://stackoverflow.com/a/19580888/751268 - - if self.behaviors?.contains(.AutomaticallyFocusTextField) == true { - _ = self.assignResponder() - } - } - public override var preferredStatusBarStyle: UIStatusBarStyle { return self.presentingViewController?.preferredStatusBarStyle ?? .default } @@ -242,7 +245,7 @@ public class AlertController: UIViewController { private func listenForKeyboardChanges() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardChange), - name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) + name: .UIKeyboardWillChangeFrame, object: nil) } @objc @@ -252,36 +255,39 @@ public class AlertController: UIViewController { return } - self.view.frame.size = CGSize(width: self.view.frame.width, height: newFrame.minY) + self.verticalCenter?.constant = -newFrame.height / 2 + self.alert.layoutIfNeeded() + } - if !self.isBeingPresented { - self.view.layoutIfNeeded() + public override func becomeFirstResponder() -> Bool { + if self.behaviors.contains(.automaticallyFocusTextField) { + return self.textFields?.first?.becomeFirstResponder() ?? super.becomeFirstResponder() } + + return super.becomeFirstResponder() } private func configureAlertView() { - self.alertView.translatesAutoresizingMaskIntoConstraints = false - self.alertView.visualStyle = self.visualStyle - if let behaviors = self.behaviors { - self.alertView.add(behaviors) - } + self.alert.translatesAutoresizingMaskIntoConstraints = false + self.alert.visualStyle = self.visualStyle + self.alert.add(self.behaviors) self.addTextFieldsIfNecessary() self.addChromeTapHandlerIfNecessary() - self.view.addSubview(self.alertView) + self.view.addSubview(self.alert) self.createViewConstraints() - self.alertView.prepareLayout() - self.alertView.actionTappedHandler = { [weak self] action in - guard self?.shouldDismissHandler?(action) != false else { - return - } - - self?.dismiss(animated: true) { - action.handler?(action) + self.alert.prepareLayout() + self.alert.actionTappedHandler = { [weak self] action in + if self?.shouldDismissHandler?(action) != false { + self?.dismiss(animated: true) { + action.handler?(action) + } } } + + self.alert.layoutIfNeeded() } private func createViewConstraints() { @@ -291,23 +297,34 @@ public class AlertController: UIViewController { case .actionSheet: let bounds = self.presentingViewController?.view.bounds ?? self.view.bounds let width = min(bounds.width, bounds.height) - margins.left - margins.right - self.alertView.sdc_pinWidth(width * self.visualStyle.width) - self.alertView.sdc_horizontallyCenterInSuperview() - self.alertView.sdc_alignEdges(withSuperview: [.bottom], insets: margins) - self.alertView.sdc_setMaximumHeightToSuperviewHeight(withOffset: -margins.top) + NSLayoutConstraint.activate([ + self.alert.widthAnchor.constraint(equalToConstant: width * self.visualStyle.width), + self.alert.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + self.alert.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, + constant: margins.bottom), + self.alert.heightAnchor.constraint(lessThanOrEqualTo: self.view.heightAnchor, + constant: -margins.top) + ]) case .alert: - self.alertView.sdc_pinWidth(self.visualStyle.width) - self.alertView.sdc_centerInSuperview() + self.alert.widthAnchor.constraint(equalToConstant: self.visualStyle.width).isActive = true + self.verticalCenter = self.alert.centerYAnchor.constraint(equalTo: self.view.centerYAnchor) let maximumHeightOffset = -(margins.top + margins.bottom) - self.alertView.sdc_setMaximumHeightToSuperviewHeight(withOffset: maximumHeightOffset) + + NSLayoutConstraint.activate([ + self.verticalCenter!, + self.alert.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + self.alert.heightAnchor.constraint(lessThanOrEqualTo: self.view.heightAnchor, + multiplier: 1, constant: maximumHeightOffset), + ]) + let priority = UILayoutPriority(rawValue: 500) - self.alertView.setContentCompressionResistancePriority(priority, for: .vertical) + self.alert.setContentCompressionResistancePriority(priority, for: .vertical) } } private func addTextFieldsIfNecessary() { - guard let textFields = self.textFields, let alert = self.alertView as? AlertView else { + guard let textFields = self.textFields, let alert = self.alert as? AlertView else { return } @@ -319,7 +336,7 @@ public class AlertController: UIViewController { } private func addChromeTapHandlerIfNecessary() { - if self.behaviors?.contains(.DismissOnOutsideTap) != true { + if self.behaviors.contains(.dismissOnOutsideTap) { return } @@ -330,14 +347,10 @@ public class AlertController: UIViewController { @objc private func chromeTapped(_ sender: UITapGestureRecognizer) { - if !self.alertView.frame.contains(sender.location(in: self.view)) { + if !self.alert.frame.contains(sender.location(in: self.view)) { self.dismiss() { self.outsideTapHandler?() } } } - - deinit { - NotificationCenter.default.removeObserver(self) - } } diff --git a/Source/AlertVisualStyle.swift b/Source/AlertVisualStyle.swift index f4273954..f4af9fb8 100644 --- a/Source/AlertVisualStyle.swift +++ b/Source/AlertVisualStyle.swift @@ -16,9 +16,6 @@ open class AlertVisualStyle: NSObject { /// The minimum distance between the alert and its superview public var margins: UIEdgeInsets - /// The parallax magnitude - public var parallax = UIOffset(horizontal: 15.75, vertical: 15.75) - /// The background color of the alert. The standard blur effect will be added if nil. public var backgroundColor: UIColor? @@ -80,29 +77,15 @@ open class AlertVisualStyle: NSObject { switch alertStyle { case .alert: self.width = 270 - - if #available(iOS 9, *) { - self.cornerRadius = 13 - self.margins = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) - self.actionViewSize = CGSize(width: 90, height: 44) - } else { - self.cornerRadius = 7 - self.margins = UIEdgeInsets.zero - self.actionViewSize = CGSize(width: 90, height: 44) - } + self.cornerRadius = 13 + self.margins = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) + self.actionViewSize = CGSize(width: 90, height: 44) case .actionSheet: self.width = 1 - - if #available(iOS 9, *) { - self.cornerRadius = 13 - self.margins = UIEdgeInsets(top: 30, left: 10, bottom: -10, right: 10) - self.actionViewSize = CGSize(width: 90, height: 57) - } else { - self.cornerRadius = 4 - self.margins = UIEdgeInsets(top: 10, left: 10, bottom: -8, right: 10) - self.actionViewSize = CGSize(width: 90, height: 44) - } + self.cornerRadius = 13 + self.margins = UIEdgeInsets(top: 30, left: 10, bottom: -10, right: 10) + self.actionViewSize = CGSize(width: 90, height: 57) } } diff --git a/Source/Presentation/AnimationController.swift b/Source/Presentation/AnimationController.swift index 4f9e6d75..2675206d 100644 --- a/Source/Presentation/AnimationController.swift +++ b/Source/Presentation/AnimationController.swift @@ -40,7 +40,7 @@ class AnimationController: NSObject, UIViewControllerAnimatedTransitioning { animatingView?.transform = CGAffineTransform(scaleX: kInitialScale, y: kInitialScale) animatingView?.alpha = 0 - animate({ + self.animate({ animatingView?.transform = CGAffineTransform(scaleX: 1, y: 1) animatingView?.alpha = 1 }, inContext: transitionContext, withCompletion: { finished in @@ -48,7 +48,7 @@ class AnimationController: NSObject, UIViewControllerAnimatedTransitioning { }) } else { - animate({ + self.animate({ animatingView?.alpha = 0 }, inContext: transitionContext, withCompletion: { finished in fromView.removeFromSuperview() diff --git a/Source/Supporting Files/SDCAlertView.h b/Source/Supporting Files/SDCAlertView.h index 2a9add6e..8e30ca9a 100644 --- a/Source/Supporting Files/SDCAlertView.h +++ b/Source/Supporting Files/SDCAlertView.h @@ -7,5 +7,3 @@ FOUNDATION_EXPORT double SDCAlertViewVersionNumber; FOUNDATION_EXPORT const unsigned char SDCAlertViewVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import - -#import "UIView+SDCAutoLayout.h" diff --git a/Source/Supporting Files/UIView+SDCAutoLayout.h b/Source/Supporting Files/UIView+SDCAutoLayout.h deleted file mode 100644 index 21c850d7..00000000 --- a/Source/Supporting Files/UIView+SDCAutoLayout.h +++ /dev/null @@ -1,85 +0,0 @@ -// -// UIView+SDCAutoLayout.h -// AutoLayout -// -// Created by Scott Berrevoets on 10/18/13. -// Copyright (c) 2013 Scotty Doesn't Code. All rights reserved. -// - -#import - -FOUNDATION_EXPORT CGFloat const SDCAutoLayoutStandardSiblingDistance; -FOUNDATION_EXPORT CGFloat const SDCAutoLayoutStandardParentChildDistance; - -@interface UIView (SDCAutoLayout) - -// Helper method that returns the first common ancestor of self and view -- (UIView *)sdc_commonAncestorWithView:(UIView *)view; - -// Aligning a view's edges with its superview -- (NSArray *)sdc_alignEdgesWithSuperview:(UIRectEdge)edges; -- (NSArray *)sdc_alignEdgesWithSuperview:(UIRectEdge)edges insets:(UIEdgeInsets)insets; - -// Aligning a view's edges with another view -- (NSArray *)sdc_alignEdges:(UIRectEdge)edges withView:(UIView *)view; -- (NSArray *)sdc_alignEdges:(UIRectEdge)edges withView:(UIView *)view insets:(UIEdgeInsets)insets; - -- (NSLayoutConstraint *)sdc_alignEdge:(UIRectEdge)edge withEdge:(UIRectEdge)otherEdge ofView:(UIView *)view; -- (NSLayoutConstraint *)sdc_alignEdge:(UIRectEdge)edge withEdge:(UIRectEdge)otherEdge ofView:(UIView *)view inset:(CGFloat)inset; - -// Aligning a view's center with another view -- (NSArray *)sdc_alignCentersWithView:(UIView *)view; -- (NSArray *)sdc_alignCentersWithView:(UIView *)view offset:(UIOffset)offset; -- (NSLayoutConstraint *)sdc_alignHorizontalCenterWithView:(UIView *)view; -- (NSLayoutConstraint *)sdc_alignHorizontalCenterWithView:(UIView *)view offset:(CGFloat)offset; -- (NSLayoutConstraint *)sdc_alignVerticalCenterWithView:(UIView *)view; -- (NSLayoutConstraint *)sdc_alignVerticalCenterWithView:(UIView *)view offset:(CGFloat)offset; - -// Centering a view in its superview -- (NSArray *)sdc_centerInSuperview; -- (NSArray *)sdc_centerInSuperviewWithOffset:(UIOffset)offset; -- (NSLayoutConstraint *)sdc_horizontallyCenterInSuperview; -- (NSLayoutConstraint *)sdc_horizontallyCenterInSuperviewWithOffset:(CGFloat)offset; -- (NSLayoutConstraint *)sdc_verticallyCenterInSuperview; -- (NSLayoutConstraint *)sdc_verticallyCenterInSuperviewWithOffset:(CGFloat)offset; - -// Align a view's baseline with another view -- (NSLayoutConstraint *)sdc_alignBaselineWithView:(UIView *)view; -- (NSLayoutConstraint *)sdc_alignBaselineWithView:(UIView *)view offset:(CGFloat)offset; - -// Pinning a view's dimensions with constants -- (NSLayoutConstraint *)sdc_pinWidth:(CGFloat)width; - -- (NSLayoutConstraint *)sdc_setMinimumWidth:(CGFloat)minimumWidth; -- (NSLayoutConstraint *)sdc_setMaximumWidth:(CGFloat)maximumWidth; -- (NSLayoutConstraint *)sdc_setMaximumWidthToSuperviewWidth; -- (NSLayoutConstraint *)sdc_setMaximumWidthToSuperviewWidthWithOffset:(CGFloat)offset; - -- (NSLayoutConstraint *)sdc_pinHeight:(CGFloat)height; -- (NSLayoutConstraint *)sdc_setMinimumHeight:(CGFloat)minimumHeight; -- (NSLayoutConstraint *)sdc_setMaximumHeight:(CGFloat)maximumHeight; -- (NSLayoutConstraint *)sdc_setMaximumHeightToSuperviewHeight; -- (NSLayoutConstraint *)sdc_setMaximumHeightToSuperviewHeightWithOffset:(CGFloat)offset; - -- (NSArray *)sdc_pinSize:(CGSize)size; - -// Pinning a view's dimensions to another view -- (NSLayoutConstraint *)sdc_pinWidthToWidthOfView:(UIView *)view; -- (NSLayoutConstraint *)sdc_pinWidthToWidthOfView:(UIView *)view offset:(CGFloat)offset; -- (NSLayoutConstraint *)sdc_pinHeightToHeightOfView:(UIView *)view; -- (NSLayoutConstraint *)sdc_pinHeightToHeightOfView:(UIView *)view offset:(CGFloat)offset; -- (NSArray *)sdc_pinSizeToSizeOfView:(UIView *)view; -- (NSArray *)sdc_pinSizeToSizeOfView:(UIView *)view offset:(UIOffset)offset; - -// Setting the spacing between a view and other view -// A positive spacing (or 0) means self will be placed to the right of view -// A negative spacing means self will be placed to the left of view -- (NSLayoutConstraint *)sdc_pinHorizontalSpacing:(CGFloat)spacing toView:(UIView *)view; - -// A positive spacing (or 0) means self will be placed below view -// A negative spacing means self will be placed above view -- (NSLayoutConstraint *)sdc_pinVerticalSpacing:(CGFloat)spacing toView:(UIView *)view; - -- (NSArray *)sdc_pinSpacing:(UIOffset)spacing toView:(UIView *)view; - -@end diff --git a/Source/Supporting Files/UIView+SDCAutoLayout.m b/Source/Supporting Files/UIView+SDCAutoLayout.m deleted file mode 100644 index fabf0c90..00000000 --- a/Source/Supporting Files/UIView+SDCAutoLayout.m +++ /dev/null @@ -1,329 +0,0 @@ -// -// UIView+SDCAutoLayout.m -// AutoLayout -// -// Created by Scott Berrevoets on 10/18/13. -// Copyright (c) 2013 Scotty Doesn't Code. All rights reserved. -// - -#import "UIView+SDCAutoLayout.h" - -CGFloat const SDCAutoLayoutStandardSiblingDistance = 8; -CGFloat const SDCAutoLayoutStandardParentChildDistance = 20; - -@implementation UIView (SDCAutoLayout) - -- (UIView *)sdc_commonAncestorWithView:(UIView *)view { - if ([view isDescendantOfView:self]) - return self; - - if ([self isDescendantOfView:view]) - return view; - - UIView *commonAncestor; - - UIView *superview = self.superview; - while (![view isDescendantOfView:superview]) { - superview = superview.superview; - - if (!superview) - break; - } - - commonAncestor = superview; - - return commonAncestor; -} - -#pragma mark - Edge Alignment - -- (NSArray *)sdc_alignEdgesWithSuperview:(UIRectEdge)edges { - return [self sdc_alignEdgesWithSuperview:edges insets:UIEdgeInsetsZero]; -} - -- (NSArray *)sdc_alignEdgesWithSuperview:(UIRectEdge)edges insets:(UIEdgeInsets)insets { - NSAssert(self.superview != nil, @"View does not have a super view"); - return [self sdc_alignEdges:edges withView:self.superview insets:insets]; -} - -- (NSArray *)sdc_alignEdges:(UIRectEdge)edges withView:(UIView *)view { - return [self sdc_alignEdges:edges withView:view insets:UIEdgeInsetsZero]; -} - -- (NSArray *)sdc_alignEdges:(UIRectEdge)edges withView:(UIView *)view insets:(UIEdgeInsets)insets { - NSMutableArray *constraints = [NSMutableArray array]; - - if (edges & UIRectEdgeTop) [constraints addObject:[self sdc_alignEdge:UIRectEdgeTop withView:view inset:insets.top]]; - if (edges & UIRectEdgeRight) [constraints addObject:[self sdc_alignEdge:UIRectEdgeRight withView:view inset:insets.right]]; - if (edges & UIRectEdgeBottom) [constraints addObject:[self sdc_alignEdge:UIRectEdgeBottom withView:view inset:insets.bottom]]; - if (edges & UIRectEdgeLeft) [constraints addObject:[self sdc_alignEdge:UIRectEdgeLeft withView:view inset:insets.left]]; - - return constraints; -} - -- (NSLayoutConstraint *)sdc_alignEdge:(UIRectEdge)edge withView:(UIView *)view inset:(CGFloat)inset { - return [self sdc_alignEdge:edge withEdge:edge ofView:view inset:inset]; -} - -- (NSLayoutAttribute)sdc_layoutAttributeFromRectEdge:(UIRectEdge)edge { - NSLayoutAttribute attribute = NSLayoutAttributeNotAnAttribute; - switch (edge) { - case UIRectEdgeTop: attribute = NSLayoutAttributeTop; break; - case UIRectEdgeRight: attribute = NSLayoutAttributeRight; break; - case UIRectEdgeBottom: attribute = NSLayoutAttributeBottom; break; - case UIRectEdgeLeft: attribute = NSLayoutAttributeLeft; break; - default: break; - } - - return attribute; -} - -- (NSLayoutConstraint *)sdc_alignEdge:(UIRectEdge)edge withEdge:(UIRectEdge)otherEdge ofView:(UIView *)view { - return [self sdc_alignEdge:edge withEdge:otherEdge ofView:view inset:0]; -} - -- (NSLayoutConstraint *)sdc_alignEdge:(UIRectEdge)edge withEdge:(UIRectEdge)otherEdge ofView:(UIView *)view inset:(CGFloat)inset { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutAttribute attribute = [self sdc_layoutAttributeFromRectEdge:edge]; - NSLayoutAttribute otherAttribute = [self sdc_layoutAttributeFromRectEdge:otherEdge]; - - if (attribute == NSLayoutAttributeNotAnAttribute || otherAttribute == NSLayoutAttributeNotAnAttribute) - return nil; - - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:attribute relatedBy:NSLayoutRelationEqual toItem:view attribute:otherAttribute multiplier:1 constant:inset]; - [commonAncestor addConstraint:constraint]; - - return constraint; -} - -#pragma mark - Center Alignment - -- (NSArray *)sdc_alignCentersWithView:(UIView *)view { - return [self sdc_alignCentersWithView:view offset:UIOffsetZero]; -} - -- (NSArray *)sdc_alignCentersWithView:(UIView *)view offset:(UIOffset)offset { - return @[[self sdc_alignHorizontalCenterWithView:view offset:offset.horizontal], [self sdc_alignVerticalCenterWithView:view offset:offset.vertical]]; -} - -- (NSLayoutConstraint *)sdc_alignHorizontalCenterWithView:(UIView *)view { - return [self sdc_alignHorizontalCenterWithView:view offset:UIOffsetZero.horizontal]; -} - -- (NSLayoutConstraint *)sdc_alignHorizontalCenterWithView:(UIView *)view offset:(CGFloat)offset { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:offset]; - [commonAncestor addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_alignVerticalCenterWithView:(UIView *)view { - return [self sdc_alignVerticalCenterWithView:view offset:UIOffsetZero.vertical]; -} - -- (NSLayoutConstraint *)sdc_alignVerticalCenterWithView:(UIView *)view offset:(CGFloat)offset { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:offset]; - [commonAncestor addConstraint:constraint]; - - return constraint; -} - -#pragma mark Superview - -- (NSArray *)sdc_centerInSuperview { - return [self sdc_centerInSuperviewWithOffset:UIOffsetZero]; -} - -- (NSArray *)sdc_centerInSuperviewWithOffset:(UIOffset)offset { - return @[[self sdc_horizontallyCenterInSuperviewWithOffset:offset.horizontal], - [self sdc_verticallyCenterInSuperviewWithOffset:offset.vertical]]; -} - -- (NSLayoutConstraint *)sdc_horizontallyCenterInSuperview { - return [self sdc_horizontallyCenterInSuperviewWithOffset:0]; -} - -- (NSLayoutConstraint *)sdc_horizontallyCenterInSuperviewWithOffset:(CGFloat)offset { - NSAssert(self.superview != nil, @"View does not have a super view"); - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterX multiplier:1 constant:offset]; - [self.superview addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_verticallyCenterInSuperview { - return [self sdc_verticallyCenterInSuperviewWithOffset:0]; -} - -- (NSLayoutConstraint *)sdc_verticallyCenterInSuperviewWithOffset:(CGFloat)offset { - NSAssert(self.superview != nil, @"View does not have a super view"); - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:offset]; - - [self.superview addConstraint:constraint]; - return constraint; -} - -#pragma mark - Baseline Alignment - -- (NSLayoutConstraint *)sdc_alignBaselineWithView:(UIView *)view { - return [self sdc_alignBaselineWithView:view offset:0]; -} -- (NSLayoutConstraint *)sdc_alignBaselineWithView:(UIView *)view offset:(CGFloat)offset { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeBaseline multiplier:1 constant:offset]; - [commonAncestor addConstraint:constraint]; - - return constraint; -} - -#pragma mark - Pinning Constants - -- (NSLayoutConstraint *)sdc_pinWidth:(CGFloat)width { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:width]; - [self addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_setMinimumWidth:(CGFloat)minimumWidth { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:minimumWidth]; - [self addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_setMaximumWidth:(CGFloat)maximumWidth { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:maximumWidth]; - [self addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_setMaximumWidthToSuperviewWidth { - return [self sdc_setMaximumWidthToSuperviewWidthWithOffset:0]; -} - -- (NSLayoutConstraint *)sdc_setMaximumWidthToSuperviewWidthWithOffset:(CGFloat)offset { - NSAssert(self.superview != nil, @"View does not have a super view"); - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.superview attribute:NSLayoutAttributeWidth multiplier:1 constant:offset]; - [self.superview addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_pinHeight:(CGFloat)height { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:height]; - [self addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_setMinimumHeight:(CGFloat)minimumHeight { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:minimumHeight]; - [self addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_setMaximumHeight:(CGFloat)maximumHeight { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:maximumHeight]; - [self addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_setMaximumHeightToSuperviewHeight { - return [self sdc_setMaximumHeightToSuperviewHeightWithOffset:0]; -} - -- (NSLayoutConstraint *)sdc_setMaximumHeightToSuperviewHeightWithOffset:(CGFloat)offset { - NSAssert(self.superview != nil, @"View does not have a super view"); - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.superview attribute:NSLayoutAttributeHeight multiplier:1 constant:offset]; - [self.superview addConstraint:constraint]; - - return constraint; -} - -- (NSArray *)sdc_pinSize:(CGSize)size { - return @[[self sdc_pinWidth:size.width], [self sdc_pinHeight:size.height]]; -} - -#pragma mark - Pinning Views - -- (NSLayoutConstraint *)sdc_pinWidthToWidthOfView:(UIView *)view { - return [self sdc_pinWidthToWidthOfView:view offset:0]; -} - -- (NSLayoutConstraint *)sdc_pinWidthToWidthOfView:(UIView *)view offset:(CGFloat)offset { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeWidth multiplier:1 constant:offset]; - [commonAncestor addConstraint:constraint]; - - return constraint; -} - -- (NSLayoutConstraint *)sdc_pinHeightToHeightOfView:(UIView *)view { - return [self sdc_pinHeightToHeightOfView:view offset:0]; -} - -- (NSLayoutConstraint *)sdc_pinHeightToHeightOfView:(UIView *)view offset:(CGFloat)offset { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeHeight multiplier:1 constant:offset]; - [commonAncestor addConstraint:constraint]; - - return constraint; -} - -- (NSArray *)sdc_pinSizeToSizeOfView:(UIView *)view { - return [self sdc_pinSizeToSizeOfView:view offset:UIOffsetZero]; -} - -- (NSArray *)sdc_pinSizeToSizeOfView:(UIView *)view offset:(UIOffset)offset { - return @[[self sdc_pinWidthToWidthOfView:view offset:offset.horizontal], [self sdc_pinHeightToHeightOfView:view offset:offset.vertical]]; -} - -#pragma mark - Pinning Spacing - -- (NSLayoutConstraint *)sdc_pinHorizontalSpacing:(CGFloat)spacing toView:(UIView *)view { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint; - - if (spacing >= 0) { - constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeRight multiplier:1 constant:spacing]; - } else { - constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeLeft multiplier:1 constant:fabs(spacing)]; - } - - [commonAncestor addConstraint:constraint]; - return constraint; -} - -- (NSLayoutConstraint *)sdc_pinVerticalSpacing:(CGFloat)spacing toView:(UIView *)view { - UIView *commonAncestor = [self sdc_commonAncestorWithView:view]; - - NSLayoutConstraint *constraint; - - if (spacing >= 0) { - constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeBottom multiplier:1 constant:spacing]; - } else { - constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTop multiplier:1 constant:fabs(spacing)]; - } - - [commonAncestor addConstraint:constraint]; - return constraint; -} - -- (NSArray *)sdc_pinSpacing:(UIOffset)spacing toView:(UIView *)view { - return @[[self sdc_pinHorizontalSpacing:spacing.horizontal toView:view], [self sdc_pinVerticalSpacing:spacing.vertical toView:view]]; -} - -@end diff --git a/Source/Text Fields/TextFieldCell.swift b/Source/Text Fields/TextFieldCell.swift index a9e34ada..07fff27a 100644 --- a/Source/Text Fields/TextFieldCell.swift +++ b/Source/Text Fields/TextFieldCell.swift @@ -39,10 +39,14 @@ final class TextFieldCell: UITableViewCell { textField.translatesAutoresizingMaskIntoConstraints = false let insets = self.visualStyle?.textFieldMargins ?? UIEdgeInsets.zero - let constraints = textField.sdc_alignEdges(withSuperview: .all, insets: insets) as! [NSLayoutConstraint] - // Assumes array order to be: top, right, bottom, left (compatible with SDCAutoLayout 2.0) - self.paddingConstraints = (leading: constraints[3], trailing: constraints[1], top: constraints[0], - bottom: constraints[2]) + let leading = textField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: insets.left) + let trailing = textField.trailingAnchor.constraint(equalTo: self.trailingAnchor, + constant: insets.right) + let top = textField.topAnchor.constraint(equalTo: self.topAnchor, constant: insets.top) + let bottom = textField.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: insets.bottom) + self.paddingConstraints = (leading: leading, trailing: trailing, top: top, bottom: bottom) + + NSLayoutConstraint.activate([leading, trailing, top, bottom]) } } diff --git a/Source/Views/ActionSheetView.swift b/Source/Views/ActionSheetView.swift index a13a0600..d6554060 100644 --- a/Source/Views/ActionSheetView.swift +++ b/Source/Views/ActionSheetView.swift @@ -1,5 +1,9 @@ -final class ActionSheetView: AlertControllerView { +final class ActionSheetView: UIView, AlertControllerViewRepresentable { + @IBOutlet var titleLabel: AlertLabel! + @IBOutlet var messageLabel: AlertLabel! + @IBOutlet var actionsCollectionView: ActionsCollectionView! + @IBOutlet var contentView: UIView! @IBOutlet private var primaryView: UIView! @IBOutlet private var cancelActionView: UIView! @IBOutlet private var cancelLabel: UILabel! @@ -9,11 +13,13 @@ final class ActionSheetView: AlertControllerView { @IBOutlet private var cancelHeightConstraint: NSLayoutConstraint! @IBOutlet private var titleWidthConstraint: NSLayoutConstraint! - override var actionTappedHandler: ((AlertAction) -> Void)? { + var actions: [AlertAction] = [] + + var actionTappedHandler: ((AlertAction) -> Void)? { didSet { self.actionsCollectionView.actionTapped = self.actionTappedHandler } } - override var visualStyle: AlertVisualStyle! { + var visualStyle: AlertVisualStyle! { didSet { let widthOffset = self.visualStyle.contentPadding.left + self.visualStyle.contentPadding.right self.titleWidthConstraint.constant -= widthOffset @@ -24,10 +30,11 @@ final class ActionSheetView: AlertControllerView { didSet { self.cancelLabel.attributedText = self.cancelAction?.attributedTitle } } - override func prepareLayout() { + func prepareLayout() { self.assignCancelAction() - super.prepareLayout() + self.actionsCollectionView.actions = self.actions + self.actionsCollectionView.visualStyle = self.visualStyle self.collectionViewHeightConstraint.constant = self.actionsCollectionView.displayHeight self.collectionViewHeightConstraint.isActive = true @@ -59,8 +66,15 @@ final class ActionSheetView: AlertControllerView { self.contentViewConstraints.forEach { $0.isActive = showContentView } } - override func highlightAction(for sender: UIPanGestureRecognizer) { - super.highlightAction(for: sender) + func addDragTapBehavior() { + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.highlightAction(for:))) + self.addGestureRecognizer(panGesture) + } + + @objc + private func highlightAction(for sender: UIPanGestureRecognizer) { + self.actionsCollectionView.highlightAction(for: sender) + let cancelIsSelected = self.cancelActionView.frame.contains(sender.location(in: self)) self.cancelButton.isHighlighted = cancelIsSelected diff --git a/Source/Views/ActionSheetView.xib b/Source/Views/ActionSheetView.xib index de30106b..6a7df7b0 100644 --- a/Source/Views/ActionSheetView.xib +++ b/Source/Views/ActionSheetView.xib @@ -1,17 +1,16 @@ - - + + + + + - + - + - - - - - + @@ -23,18 +22,18 @@ - + diff --git a/Source/Views/AlertControllerView.swift b/Source/Views/AlertControllerView.swift deleted file mode 100644 index 80dac11a..00000000 --- a/Source/Views/AlertControllerView.swift +++ /dev/null @@ -1,97 +0,0 @@ -import UIKit - -protocol AlertControllerViewRepresentable { - - var title: NSAttributedString? { get set } - var message: NSAttributedString? { get set } - - var actions: [AlertAction] { get set } - var actionTappedHandler: ((AlertAction) -> Void)? { get set } - - var contentView: UIView! { get } - var visualStyle: AlertVisualStyle! { get set } - - var topView: UIView { get } - - var titleLabel: AlertLabel! { get } - var messageLabel: AlertLabel! { get } - var actionsCollectionView: ActionsCollectionView! { get } - - func add(_ behaviors: AlertBehaviors) - func prepareLayout() -} - -extension AlertControllerViewRepresentable where Self: UIView { - - var title: NSAttributedString? { - get { return self.titleLabel.attributedText } - set { self.titleLabel.attributedText = newValue } - } - - var message: NSAttributedString? { - get { return self.messageLabel.attributedText } - set { self.messageLabel.attributedText = newValue } - } - - var topView: UIView { return self } - - func add(_ behaviors: AlertBehaviors) { - if behaviors.contains(.DragTap) { - let panGesture = UIPanGestureRecognizer(target: self, - action: #selector(AlertControllerView.highlightAction(for:))) - self.addGestureRecognizer(panGesture) - } - - if behaviors.contains(.Parallax) { - self.addParallax() - } - } - - private func addParallax() { - let parallax = self.visualStyle.parallax - - let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) - horizontal.minimumRelativeValue = NSNumber(value: Float(-parallax.horizontal)) - horizontal.maximumRelativeValue = NSNumber(value: Float(parallax.horizontal)) - - let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis) - vertical.minimumRelativeValue = NSNumber(value: Float(-parallax.vertical)) - vertical.maximumRelativeValue = NSNumber(value: Float(parallax.vertical)) - - let group = UIMotionEffectGroup() - group.motionEffects = [horizontal, vertical] - - self.addMotionEffect(group) - } -} - -class AlertControllerView: UIView, AlertControllerViewRepresentable { - - @IBOutlet var titleLabel: AlertLabel! = AlertLabel() { - didSet { self.titleLabel.translatesAutoresizingMaskIntoConstraints = false } - } - - @IBOutlet var messageLabel: AlertLabel! = AlertLabel() { - didSet { self.messageLabel.translatesAutoresizingMaskIntoConstraints = false } - } - - @IBOutlet var actionsCollectionView: ActionsCollectionView! = ActionsCollectionView() { - didSet { self.actionsCollectionView.translatesAutoresizingMaskIntoConstraints = false } - } - - @IBOutlet var contentView: UIView! = UIView() - - var actions: [AlertAction] = [] - var visualStyle: AlertVisualStyle! - var actionTappedHandler: ((AlertAction) -> Void)? - - func prepareLayout() { - self.actionsCollectionView.actions = self.actions - self.actionsCollectionView.visualStyle = self.visualStyle - } - - @objc - func highlightAction(for sender: UIPanGestureRecognizer) { - self.actionsCollectionView.highlightAction(for: sender) - } -} diff --git a/Source/Views/AlertControllerViewRepresentable.swift b/Source/Views/AlertControllerViewRepresentable.swift new file mode 100644 index 00000000..2853fecc --- /dev/null +++ b/Source/Views/AlertControllerViewRepresentable.swift @@ -0,0 +1,45 @@ +import UIKit + +protocol AlertControllerViewRepresentable: class { + + var title: NSAttributedString? { get set } + var message: NSAttributedString? { get set } + + var actions: [AlertAction] { get set } + var actionTappedHandler: ((AlertAction) -> Void)? { get set } + + var contentView: UIView! { get } + var visualStyle: AlertVisualStyle! { get set } + + var topView: UIView { get } + + var titleLabel: AlertLabel! { get } + var messageLabel: AlertLabel! { get } + var actionsCollectionView: ActionsCollectionView! { get } + + func add(_ behaviors: AlertBehaviors) + func addDragTapBehavior() + + func prepareLayout() +} + +extension AlertControllerViewRepresentable where Self: UIView { + + var title: NSAttributedString? { + get { return self.titleLabel.attributedText } + set { self.titleLabel.attributedText = newValue } + } + + var message: NSAttributedString? { + get { return self.messageLabel.attributedText } + set { self.messageLabel.attributedText = newValue } + } + + var topView: UIView { return self } + + func add(_ behaviors: AlertBehaviors) { + if behaviors.contains(.dragTap) { + self.addDragTapBehavior() + } + } +} diff --git a/Source/Views/AlertView.swift b/Source/Views/AlertView.swift index 736962e9..1d6ea19d 100644 --- a/Source/Views/AlertView.swift +++ b/Source/Views/AlertView.swift @@ -1,19 +1,27 @@ -class AlertView: AlertControllerView { +final class AlertView: UIView, AlertControllerViewRepresentable { + + var titleLabel: AlertLabel! = AlertLabel() + var messageLabel: AlertLabel! = AlertLabel() + var actionsCollectionView: ActionsCollectionView! = ActionsCollectionView() + var contentView: UIView! = UIView() + var actions: [AlertAction] = [] + var actionLayout = ActionLayout.automatic - var actionLayout: ActionLayout = .automatic var textFieldsViewController: TextFieldsViewController? { didSet { self.textFieldsViewController?.visualStyle = self.visualStyle } } - var topView: UIView { return self.scrollView } + var visualStyle: AlertVisualStyle! { + didSet { self.textFieldsViewController?.visualStyle = self.visualStyle } + } - override var actionTappedHandler: ((AlertAction) -> Void)? { + var actionTappedHandler: ((AlertAction) -> Void)? { get { return self.actionsCollectionView.actionTapped } set { self.actionsCollectionView.actionTapped = newValue } } - override var visualStyle: AlertVisualStyle! { - didSet { self.textFieldsViewController?.visualStyle = self.visualStyle } + var topView: UIView { + return self.scrollView } private let scrollView = UIScrollView() @@ -44,8 +52,9 @@ class AlertView: AlertControllerView { self.messageLabel.font = UIFont.systemFont(ofSize: 13) } - override func prepareLayout() { - super.prepareLayout() + func prepareLayout() { + self.actionsCollectionView.actions = self.actions + self.actionsCollectionView.visualStyle = self.visualStyle self.scrollView.translatesAutoresizingMaskIntoConstraints = false self.addSubview(self.scrollView) @@ -58,6 +67,12 @@ class AlertView: AlertControllerView { self.updateUI() } + func addDragTapBehavior() { + let panGesture = UIPanGestureRecognizer(target: self, + action: #selector(self.highlightAction(for:))) + self.addGestureRecognizer(panGesture) + } + // MARK: - Private methods private func createBackground() { @@ -68,7 +83,10 @@ class AlertView: AlertControllerView { backgroundView.translatesAutoresizingMaskIntoConstraints = false self.insertSubview(backgroundView, belowSubview: self.scrollView) - backgroundView.sdc_alignEdges(.all, with: self) + backgroundView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true + backgroundView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true + backgroundView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + backgroundView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true } } @@ -103,6 +121,11 @@ class AlertView: AlertControllerView { return CGSize(width: UIViewNoIntrinsicMetric, height: totalHeight) } + @objc + private func highlightAction(for sender: UIPanGestureRecognizer) { + self.actionsCollectionView.highlightAction(for: sender) + } + // MARK: - Constraints private func createContentConstraints() { @@ -116,91 +139,104 @@ class AlertView: AlertControllerView { private func createTitleLabelConstraints() { let contentPadding = self.visualStyle.contentPadding - self.addConstraint(NSLayoutConstraint(item: self.titleLabel, attribute: .firstBaseline, - relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: contentPadding.top)) let insets = UIEdgeInsets(top: 0, left: contentPadding.left, bottom: 0, right: -contentPadding.right) - self.titleLabel.sdc_alignEdges([.left, .right], with: self, insets: insets) - self.pinBottomOfScrollView(to: self.titleLabel, withPriority: .defaultLow) + NSLayoutConstraint.activate([ + self.titleLabel.firstBaselineAnchor.constraint(equalTo: self.topAnchor, + constant: contentPadding.top), + self.titleLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: insets.left), + self.titleLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: insets.right), + ]) + + self.pinBottomOfScrollView(to: self.messageLabel, withPriority: .defaultLow) + } private func createMessageLabelConstraints() { - self.addConstraint(NSLayoutConstraint(item: self.messageLabel, attribute: .firstBaseline, - relatedBy: .equal, toItem: self.titleLabel, attribute: .lastBaseline , multiplier: 1, - constant: self.visualStyle.verticalElementSpacing)) let contentPadding = self.visualStyle.contentPadding - let insets = UIEdgeInsets(top: 0, left: contentPadding.left, bottom: 0, right: -contentPadding.right) - self.messageLabel.sdc_alignEdges([.left, .right], with: self, insets: insets) + NSLayoutConstraint.activate([ + self.messageLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: contentPadding.left), + self.messageLabel.rightAnchor.constraint(equalTo: self.rightAnchor, + constant: -contentPadding.right), + self.messageLabel.firstBaselineAnchor.constraint(equalTo: self.titleLabel.lastBaselineAnchor, + constant: self.visualStyle.verticalElementSpacing) + ]) self.pinBottomOfScrollView(to: self.messageLabel, withPriority: .defaultLow + 1.0) } private func createTextFieldsConstraints() { + self.textFieldsViewController?.visualStyle = self.visualStyle + guard let textFieldsView = self.textFieldsViewController?.view, let height = self.textFieldsViewController?.requiredHeight else { return } - // The text fields view controller needs the visual style to calculate its height - self.textFieldsViewController?.visualStyle = self.visualStyle - let widthOffset = self.visualStyle.contentPadding.left + self.visualStyle.contentPadding.right - self.addConstraint(NSLayoutConstraint(item: textFieldsView, attribute: .top, relatedBy: .equal, - toItem: self.messageLabel, attribute: .lastBaseline, multiplier: 1, - constant: self.visualStyle.verticalElementSpacing)) - - textFieldsView.sdc_pinWidth(toWidthOf: self, offset: -widthOffset) - textFieldsView.sdc_alignHorizontalCenter(with: self) - textFieldsView.sdc_pinHeight(height) + NSLayoutConstraint.activate([ + textFieldsView.topAnchor.constraint(equalTo: self.messageLabel.lastBaselineAnchor, + constant: self.visualStyle.verticalElementSpacing), + textFieldsView.widthAnchor.constraint(equalTo: self.widthAnchor, constant: -widthOffset), + textFieldsView.centerXAnchor.constraint(equalTo: self.centerXAnchor), + textFieldsView.heightAnchor.constraint(equalToConstant: height), + ]) self.pinBottomOfScrollView(to: textFieldsView, withPriority: .defaultLow + 2.0) } private func createCustomContentViewConstraints() { - if !self.elements.contains(self.contentView) { return } + if !self.elements.contains(self.contentView) { + return + } - let aligningView = self.textFieldsViewController?.view ?? self.messageLabel + let aligningView = self.textFieldsViewController?.view ?? self.messageLabel! let widthOffset = self.visualStyle.contentPadding.left + self.visualStyle.contentPadding.right - let topSpacing = self.visualStyle.verticalElementSpacing - self.contentView.sdc_alignEdge(.top, with: .bottom, of: aligningView, inset: topSpacing) - self.contentView.sdc_alignHorizontalCenter(with: self) - self.contentView.sdc_pinWidth(toWidthOf: self, offset: -widthOffset) + NSLayoutConstraint.activate([ + self.contentView.topAnchor.constraint(equalTo: aligningView.bottomAnchor, + constant: self.visualStyle.verticalElementSpacing), + self.contentView.centerXAnchor.constraint(equalTo: self.centerXAnchor), + self.contentView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: -widthOffset), + ]) self.pinBottomOfScrollView(to: self.contentView, withPriority: .defaultLow + 3.0) } private func createCollectionViewConstraints() { let height = self.actionsCollectionView.displayHeight - let heightConstraint = NSLayoutConstraint(item: self.actionsCollectionView, attribute: .height, - relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height) + let heightConstraint = self.actionsCollectionView.heightAnchor.constraint(equalToConstant: height) heightConstraint.priority = .defaultHigh - self.actionsCollectionView.addConstraint(heightConstraint) - self.actionsCollectionView.sdc_pinWidth(toWidthOf: self) - self.actionsCollectionView.sdc_alignEdge(.top, with: .bottom, of: self.scrollView) - self.actionsCollectionView.sdc_alignHorizontalCenter(with: self) - self.actionsCollectionView.sdc_alignEdges(.bottom, with: self) + + NSLayoutConstraint.activate([ + heightConstraint, + self.actionsCollectionView.widthAnchor.constraint(equalTo: self.widthAnchor), + self.actionsCollectionView.topAnchor.constraint(equalTo: self.scrollView.bottomAnchor), + self.actionsCollectionView.centerXAnchor.constraint(equalTo: self.centerXAnchor), + self.actionsCollectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor), + ]) } private func createScrollViewConstraints() { - self.scrollView.sdc_alignEdges([.left, .right, .top], with: self) + self.scrollView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true + self.scrollView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true + self.scrollView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true self.scrollView.layoutIfNeeded() - let scrollViewHeight = self.scrollView.contentSize.height - let constraint = NSLayoutConstraint(item: self.scrollView, attribute: .height, relatedBy: .equal, - toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: scrollViewHeight) - constraint.priority = .defaultHigh - self.scrollView.addConstraint(constraint) + let height = self.scrollView.contentSize.height + let heightConstraint = self.scrollView.heightAnchor.constraint(equalToConstant: height) + heightConstraint.priority = .defaultHigh + heightConstraint.isActive = true + } private func pinBottomOfScrollView(to view: UIView, withPriority priority: UILayoutPriority) { - let bottomAnchor = NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, - toItem: self.scrollView, attribute: .bottom, multiplier: 1, - constant: -self.visualStyle.contentPadding.bottom) - bottomAnchor.priority = priority - self.addConstraint(bottomAnchor) + let bottom = view.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor) + bottom.constant = -self.visualStyle.contentPadding.bottom + bottom.priority = priority + bottom.isActive = true } } From 422924fcd69faf8f2482536523fe30f575f9123c Mon Sep 17 00:00:00 2001 From: Scott Berrevoets Date: Fri, 22 Sep 2017 00:15:54 -0700 Subject: [PATCH 3/5] Mark relevant public API with @objc --- Source/Actions/AlertAction.swift | 8 ++++++++ Source/AlertBehaviors.swift | 1 - Source/AlertController+Convenience.swift | 4 +++- Source/AlertController.swift | 19 ++++++++++++++++- Source/AlertVisualStyle.swift | 26 ++++++++++++++++++++++-- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Source/Actions/AlertAction.swift b/Source/Actions/AlertAction.swift index 1ff595af..36577732 100644 --- a/Source/Actions/AlertAction.swift +++ b/Source/Actions/AlertAction.swift @@ -24,6 +24,7 @@ public class AlertAction: NSObject { - parameter style: The action's style - parameter handler: An optional closure that's called when the user taps on this action */ + @objc public convenience init(title: String?, style: AlertActionStyle, handler: ((AlertAction) -> Void)? = nil) { self.init() @@ -39,6 +40,7 @@ public class AlertAction: NSObject { - parameter style: The action's style - parameter handler: An optional closure that is called when the user taps on this action */ + @objc public convenience init(attributedTitle: NSAttributedString?, style: AlertActionStyle, handler: ((AlertAction) -> Void)? = nil) { @@ -49,24 +51,30 @@ public class AlertAction: NSObject { } /// A closure that gets executed when the user taps on this actions in the UI + @objc public var handler: ((AlertAction) -> Void)? /// The plain title for the action. Uses `attributedTitle` directly. + @objc private(set) public var title: String? { get { return self.attributedTitle?.string } set { self.attributedTitle = newValue.map(NSAttributedString.init) } } /// The stylized title for the action. + @objc private(set) public var attributedTitle: NSAttributedString? /// The action's style. + @objc internal(set) public var style: AlertActionStyle = .normal /// The action's button accessibility identifier + @objc public var accessibilityIdentifier: String? /// Whether this action can be interacted with by the user. + @objc public var isEnabled = true { didSet { self.actionView?.isEnabled = self.isEnabled } } diff --git a/Source/AlertBehaviors.swift b/Source/AlertBehaviors.swift index 5c563b8c..4b04af9c 100644 --- a/Source/AlertBehaviors.swift +++ b/Source/AlertBehaviors.swift @@ -1,5 +1,4 @@ public struct AlertBehaviors: OptionSet { - /// When applied, the user can dismiss the alert or action sheet by tapping outside of it. Enabled for /// action sheets by default. public static let dismissOnOutsideTap = AlertBehaviors(rawValue: 1) diff --git a/Source/AlertController+Convenience.swift b/Source/AlertController+Convenience.swift index 5baa9914..24198f74 100644 --- a/Source/AlertController+Convenience.swift +++ b/Source/AlertController+Convenience.swift @@ -1,5 +1,4 @@ public extension AlertController { - /// Convenience method to quickly display a basic alert. /// /// - parameter title: An optional title for the alert. @@ -8,6 +7,7 @@ public extension AlertController { /// - parameter customView: An optional view that will be displayed in the alert's `contentView`. /// /// - returns: The alert that was presented. + @objc @discardableResult public class func alert(withTitle title: String? = nil, message: String? = nil, actionTitle: String? = nil, customView: UIView? = nil) -> AlertController @@ -30,6 +30,7 @@ public extension AlertController { /// - parameter actions: The titles of the actions in the action sheet. /// /// - returns: The action sheet that was presented. + @objc @discardableResult public class func sheet(withTitle title: String? = nil, message: String? = nil, actions: [String]) -> AlertController @@ -46,6 +47,7 @@ public extension AlertController { /// - parameter actions: The titles of the actions in the action sheet. /// /// - returns: The action sheet that was presented. + @objc public class func sheet(with view: UIView, actions: [String]) -> AlertController { let alertController = AlertController(title: nil, message: nil, preferredStyle: .actionSheet) actions.forEach { alertController.add(AlertAction(title: $0, style: .normal)) } diff --git a/Source/AlertController.swift b/Source/AlertController.swift index 0fe0baa8..0f26f55b 100644 --- a/Source/AlertController.swift +++ b/Source/AlertController.swift @@ -37,18 +37,21 @@ public class AlertController: UIViewController { } /// The alert's message. Directly uses `attributedMessage` without any attributes. + @objc public var message: String? { get { return self.attributedMessage?.string } set { self.attributedMessage = newValue.map(NSAttributedString.init) } } /// A stylized title for the alert. + @objc public var attributedTitle: NSAttributedString? { get { return self.alert.title } set { self.alert.title = newValue } } /// A stylized message for the alert. + @objc public var attributedMessage: NSAttributedString? { get { return self.alert.message } set { self.alert.message = newValue } @@ -57,11 +60,13 @@ public class AlertController: UIViewController { /// The alert's content view. This can be used to add custom views to your alert. The width of the content /// view is equal to the width of the alert, minus padding. The height must be defined manually since it /// depends on the size of the subviews. + @objc public var contentView: UIView { return self.alert.contentView } /// The alert's actions (buttons). + @objc private(set) public var actions = [AlertAction]() { didSet { self.alert.actions = self.actions } } @@ -69,6 +74,7 @@ public class AlertController: UIViewController { /// The alert's preferred action, if one is set. Setting this value to an action that wasn't already added /// to the array will add it and override its style to `.preferred`. Setting this value to `nil` will /// remove the preferred style from all actions. + @objc public var preferredAction: AlertAction? { get { let index = self.actions.index { $0.style == .preferred } @@ -88,12 +94,14 @@ public class AlertController: UIViewController { } /// The layout of the actions in the alert. + @objc public var actionLayout: ActionLayout { get { return (self.alert as? AlertView)?.actionLayout ?? .automatic } set { (self.alert as? AlertView)?.actionLayout = newValue } } /// The text fields that are added to the alert. Does nothing when used with an action sheet. + @objc private(set) public var textFields: [UITextField]? /// The alert's custom behaviors. See `AlertBehaviors` for possible options. @@ -101,15 +109,20 @@ public class AlertController: UIViewController { /// A closure that, when set, returns whether the alert or action sheet should dismiss after the user taps /// on an action. If it returns false, the AlertAction handler will not be executed. + @objc public var shouldDismissHandler: ((AlertAction?) -> Bool)? - /// A closure called when the alert is dismissed after an outside tap (when `DismissOnOutsideTap` behavior is enabled) + /// A closure called when the alert is dismissed after an outside tap (when `dismissOnOutsideTap` behavior + /// is enabled) + @objc public var outsideTapHandler: (() -> Void)? /// The visual style that applies to the alert or action sheet. + @objc public lazy var visualStyle: AlertVisualStyle = AlertVisualStyle(alertStyle: self.preferredStyle) /// The alert's presentation style. + @objc private(set) public var preferredStyle: AlertControllerStyle = .alert private let alert: UIView & AlertControllerViewRepresentable @@ -124,6 +137,7 @@ public class AlertController: UIViewController { /// - parameter attributedTitle: An optional stylized title /// - parameter attributedMessage: An optional stylized message /// - parameter preferredStyle: The preferred presentation style of the alert. Default is `alert`. + @objc public convenience init(attributedTitle: NSAttributedString?, attributedMessage: NSAttributedString?, preferredStyle: AlertControllerStyle = .alert) { @@ -142,6 +156,7 @@ public class AlertController: UIViewController { /// - parameter title: An optional title /// - parameter message: An optional message /// - parameter preferredStyle: The preferred presentation style of the alert. Default is `alert`. + @objc public convenience init(title: String?, message: String?, preferredStyle: AlertControllerStyle = .alert) { self.init(preferredStyle: preferredStyle) self.preferredStyle = preferredStyle @@ -190,6 +205,7 @@ public class AlertController: UIViewController { /// any position. /// /// - parameter action: The action to add. + @objc public func add(_ action: AlertAction) { self.actions.append(action) } @@ -198,6 +214,7 @@ public class AlertController: UIViewController { /// /// - parameter configurationHandler: An optional closure that can be used to configure the text field, /// which is provided as a parameter to the closure. + @objc public func addTextField(withHandler configurationHandler: ((UITextField) -> Void)? = nil) { let textField = UITextField() textField.autocorrectionType = .no diff --git a/Source/AlertVisualStyle.swift b/Source/AlertVisualStyle.swift index f4af9fb8..3eacefc4 100644 --- a/Source/AlertVisualStyle.swift +++ b/Source/AlertVisualStyle.swift @@ -2,75 +2,95 @@ import UIKit @objc(SDCAlertVisualStyle) open class AlertVisualStyle: NSObject { - /// The width of the alert. A value of 1 or below is interpreted as a percentage of the width of the view /// controller that presents the alert. + @objc public var width: CGFloat /// The corner radius of the alert + @objc public var cornerRadius: CGFloat /// The minimum distance between alert elements and the alert itself + @objc public var contentPadding = UIEdgeInsets(top: 36, left: 16, bottom: 12, right: 16) /// The minimum distance between the alert and its superview + @objc public var margins: UIEdgeInsets - /// The background color of the alert. The standard blur effect will be added if nil. + /// The background color of the alert. The standard blur effect will be added if nil. + @objc public var backgroundColor: UIColor? /// The vertical spacing between elements + @objc public var verticalElementSpacing: CGFloat = 24 /// The size of an action. The specified width is treated as a minimum width. The actual width is /// automatically determined. + @objc public var actionViewSize: CGSize /// The color of an action when the user is tapping it + @objc public var actionHighlightColor = UIColor(white: 0.8, alpha: 0.7) /// The color of the separators between actions + @objc public var actionViewSeparatorColor = UIColor(white: 0.5, alpha: 0.5) /// The thickness of the separators between actions + @objc public var actionViewSeparatorThickness: CGFloat = 1 / UIScreen.main.scale /// The font used in text fields + @objc public var textFieldFont = UIFont.systemFont(ofSize: 13) /// The height of a text field if added using the standard method call. Won't affect text fields added /// directly to the alert's content view. + @objc public var textFieldHeight: CGFloat = 25 /// The border color of a text field if added using the standard method call. Won't affect text fields /// added directly to the alert's content view. + @objc public var textFieldBorderColor = UIColor(red: 64/255, green: 64/255, blue: 64/255, alpha: 1) /// The inset of the text within the text field if added using the standard method call. Won't affect text /// fields added directly to the alert's content view. + @objc public var textFieldMargins = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4) /// The color for a nondestructive action's text + @objc public var normalTextColor: UIColor? /// The color for a destructive action's text + @objc public var destructiveTextColor = UIColor.red /// The font for an alert's preferred action + @objc public var alertPreferredFont = UIFont.boldSystemFont(ofSize: 17) /// The font for an alert's other actions + @objc public var alertNormalFont = UIFont.systemFont(ofSize: 17) /// The font for an action sheet's preferred action + @objc public var actionSheetPreferredFont = UIFont.boldSystemFont(ofSize: 20) /// The font for an action sheet's other actions + @objc public var actionSheetNormalFont = UIFont.systemFont(ofSize: 20) /// The style of the alert. private let alertStyle: AlertControllerStyle + @objc public init(alertStyle: AlertControllerStyle) { self.alertStyle = alertStyle @@ -94,6 +114,7 @@ open class AlertVisualStyle: NSObject { /// - parameter action: The action that determines the text color. /// /// - returns: The text color, or nil to use the alert's `tintColor`. + @objc open func textColor(for action: AlertAction?) -> UIColor? { return action?.style == .destructive ? self.destructiveTextColor : self.normalTextColor } @@ -103,6 +124,7 @@ open class AlertVisualStyle: NSObject { /// - parameter action: The action for which to return the font. /// /// - returns: The font. + @objc open func font(for action: AlertAction?) -> UIFont { switch (self.alertStyle, action?.style) { case (.alert, let style) where style == .preferred: From 6a3a254c35a50edb62f388fdb53142a7476f4d34 Mon Sep 17 00:00:00 2001 From: Scott Berrevoets Date: Fri, 22 Sep 2017 00:22:03 -0700 Subject: [PATCH 4/5] Change add() to addAction() This is what UIAlertController does and makes it a little clearer what you're adding. --- Example/DemoViewController.swift | 8 +++---- Example/TestsViewController.swift | 30 ++++++++++++------------ Source/AlertController+Convenience.swift | 6 ++--- Source/AlertController.swift | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Example/DemoViewController.swift b/Example/DemoViewController.swift index 72f987a3..6e3a9fae 100644 --- a/Example/DemoViewController.swift +++ b/Example/DemoViewController.swift @@ -34,13 +34,13 @@ final class DemoViewController: UITableViewController { let buttons = Int(self.buttonCountTextField.content ?? "0")! for i in 0.. AlertController { let alertController = AlertController(title: title, message: message) - alertController.add(AlertAction(title: actionTitle, style: .preferred)) + alertController.addAction(AlertAction(title: actionTitle, style: .preferred)) if let customView = customView { alertController.contentView.addSubview(customView) @@ -36,7 +36,7 @@ public extension AlertController { -> AlertController { let alertController = AlertController(title: title, message: message, preferredStyle: .actionSheet) - actions.forEach { alertController.add(AlertAction(title: $0, style: .normal)) } + actions.forEach { alertController.addAction(AlertAction(title: $0, style: .normal)) } alertController.present() return alertController } @@ -50,7 +50,7 @@ public extension AlertController { @objc public class func sheet(with view: UIView, actions: [String]) -> AlertController { let alertController = AlertController(title: nil, message: nil, preferredStyle: .actionSheet) - actions.forEach { alertController.add(AlertAction(title: $0, style: .normal)) } + actions.forEach { alertController.addAction(AlertAction(title: $0, style: .normal)) } alertController.contentView.addSubview(view) alertController.present() return alertController diff --git a/Source/AlertController.swift b/Source/AlertController.swift index 0f26f55b..eb9c750d 100644 --- a/Source/AlertController.swift +++ b/Source/AlertController.swift @@ -206,7 +206,7 @@ public class AlertController: UIViewController { /// /// - parameter action: The action to add. @objc - public func add(_ action: AlertAction) { + public func addAction(_ action: AlertAction) { self.actions.append(action) } From 8f084c5ab4337292ff3a881c0640348a664ed579 Mon Sep 17 00:00:00 2001 From: Scott Berrevoets Date: Sat, 9 Sep 2017 18:08:24 -0700 Subject: [PATCH 5/5] Update to 8.0 --- .travis.yml | 2 +- CHANGELOG.md | 158 +++++++++++++++++++++++++++++++------------ README.md | 18 ++--- SDCAlertView.podspec | 2 +- 4 files changed, 121 insertions(+), 59 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96a42f69..d54a4235 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8.1 +osx_image: xcode9 before_install: - gem install xcpretty diff --git a/CHANGELOG.md b/CHANGELOG.md index 39bca3cc..b509cff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # CHANGELOG +## 8.0 + +This release brings Swift 4 and iOS 11 compatibility. It also increases the +deployment target to iOS 9.0. + +**What's New:** +- Adds a closure for handling taps in the outer (chrome) area of the alert +- Improves accessibility for alert actions +- `AlertController.add()` has been renamed to `AlertController.addAction()` for + clarity +- The `AlertBehaviors` constants are now lowercased, following Swift 3 + conventions. + +**Bug Fixes**: +- Fixes an issue that could lead the alert to be shown in an unsupported + orientation + ### 7.1.2 **Bug Fixes**: - Fixes a bug that could incorrectly set cancel button attributes on action @@ -19,12 +36,14 @@ **Bug Fixes:** - The dismissal animation looks like the system one again -- Action sheets without an explicit cancel button won't show the inferred cancel button twice anymore +- Action sheets without an explicit cancel button won't show the inferred cancel + button twice anymore - Button labels size and truncate as expected now, instead of being cut off ### 7.0.1 **Bug Fixes:** -- Avoids an infinite loop/crash when using an action sheet without explicit cancel buttons +- Avoids an infinite loop/crash when using an action sheet without explicit + cancel buttons # 7.0 This is a compatibility update for Swift 3. @@ -41,13 +60,18 @@ This is a compatibility update for Swift 2.3. ## 5.1 **Bug Fixes:** -- Fixes the inability to override visual style properties in a subclass of `DefaultVisualStyle`. The `VisualStyle` protocol has been removed and the conforming class been renamed to `AlertVisualStyle`. The old class name is still available, but marked as deprecated and will be removed in the future. +- Fixes the inability to override visual style properties in a subclass of + `DefaultVisualStyle`. The `VisualStyle` protocol has been removed and the + conforming class been renamed to `AlertVisualStyle`. The old class name is + still available, but marked as deprecated and will be removed in the future. **What's New:** - Makes an `AlertAction`'s `handler` public. # 5.0 -5.0 is a compatibility update so the project builds in Swift 2.2 and doesn't generate warnings. It also changes the Objective-C names of the public enums, which Swift now supports. +5.0 is a compatibility update so the project builds in Swift 2.2 and +doesn't generate warnings. It also changes the Objective-C names of the public +enums, which Swift now supports. **Bug Fixes:** - Prioritize `textColorForAction()` over the `tintColor` of an action @@ -67,14 +91,23 @@ This is a compatibility update for Swift 2.3. - Implements alert behaviors such as parallax and "tap outside to dismiss" - Action highlight colors can be changed with custom visual styles -**Changes:** -This version introduces other changes that are not compatible with previous -versions of `SDCAlertView`. -- The `automaticallyFocusFirstTextField` property is now implemented as an alert behavior -- In Objective-C, the presentation and dismissal methods are now named `presentAnimated:completion:` and `dismissAnimated:completion:` to follow Objective-C nomenclature more closely -- The title and message label fonts are removed from `VisualStyle`. To change the labels' fonts, please use `attributedTitle` and `attributedMessage` with `NSFontAttributeName` instead. -- `setShouldDismissHandler()` and `setVisualStyle()` are now properties named `shouldDismissHandler` and `visualStyle` respectively. Their functionality is unchanged. -- The convenience method `showWithTitle(_:message:actionTitle:customView:)` has been renamed to `alertWithTitle(_:message:actionTitle:customView:)` to provide more consistency with the action sheet's counterpart +**Changes:** +This version introduces other changes that are not compatible with +previous versions of `SDCAlertView`. +- The `automaticallyFocusFirstTextField` property is now implemented as an alert + behavior +- In Objective-C, the presentation and dismissal methods are now named + `presentAnimated:completion:` and `dismissAnimated:completion:` to follow + Objective-C nomenclature more closely +- The title and message label fonts are removed from `VisualStyle`. To change + the labels' fonts, please use `attributedTitle` and `attributedMessage` with + `NSFontAttributeName` instead. +- `setShouldDismissHandler()` and `setVisualStyle()` are now properties named + `shouldDismissHandler` and `visualStyle` respectively. Their functionality is + unchanged. +- The convenience method `showWithTitle(_:message:actionTitle:customView:)` has + been renamed to `alertWithTitle(_:message:actionTitle:customView:)` to provide + more consistency with the action sheet's counterpart ### 3.1.1 **Bug Fixes:** @@ -92,9 +125,10 @@ versions of `SDCAlertView`. **Bug Fixes:** - Fixes a crash on iOS 8 when adding text fields to the alert -### 3.0.3 +### 3.0.3 **Bug Fixes:** -- Resolves an issue that would never use the white status bar color if it was specified +- Resolves an issue that would never use the white status bar color if it was + specified ### 3.0.2 **Bug Fixes:** @@ -123,12 +157,14 @@ versions of `SDCAlertView`. ### 2.5.2 **Bug Fixes:** -- Solves an accessibility-related incompatibility between `UIAlertController` and `SDCAlertController` (#105) +- Solves an accessibility-related incompatibility between `UIAlertController` + and `SDCAlertController` (#105) ### 2.5.1 **Bug Fixes:** -- Fixes a bug that would show the wrong cancel button title in a legacy alert in some cases +- Fixes a bug that would show the wrong cancel button title in a legacy alert in + some cases - Fixes a retain cycle between `SDCAlertController` and `SDCAlertView` ### 2.5 @@ -153,10 +189,12 @@ versions of `SDCAlertView`. ### 2.4 **Bug Fixes:** -- Fixes an issue that would prevent legacy alerts from calling the `shouldDismissHandler` (#90) +- Fixes an issue that would prevent legacy alerts from calling the + `shouldDismissHandler` (#90) **What's New:** -- Adds 2 new customizable properties: `titleLabelColor` and `messageLabelColor` (#92) +- Adds 2 new customizable properties: `titleLabelColor` and `messageLabelColor` + (#92) ### 2.3.2 @@ -168,12 +206,14 @@ versions of `SDCAlertView`. **Bug Fixes:** - Fixes crash when using attributed title in legacy alert (#85 & #86) - Fixes a stack overflow on iPad 2 (#87) -- Fixes an issue that would not reflect the attributed of an attributed string after creating an alert (#88) +- Fixes an issue that would not reflect the attributed of an attributed string + after creating an alert (#88) ### 2.3.0 **What's New:** -- Improved semantics for alert action styles. See the discussion in #81 for more information. +- Improved semantics for alert action styles. See the discussion in #81 for more + information. **Bug Fixes:** - Fixes an animation issue in `SDCAlertView` on iOS 7 (#79) @@ -182,7 +222,8 @@ versions of `SDCAlertView`. ### 2.2.0 **Bug Fixes:** -- Fixes an incompatibility issue that would not correctly fetch text fields on iOS 7 (#67) +- Fixes an incompatibility issue that would not correctly fetch text fields on + iOS 7 (#67) ### 2.1.1 @@ -195,16 +236,19 @@ versions of `SDCAlertView`. - The `usesLegacyAlert` property is now made public **Bug Fixes:** -- Updates the import in SDCAlertController.h to not depend on any precompiled headers +- Updates the import in SDCAlertController.h to not depend on any precompiled + headers - Fixes several issues with the legacy alert (#62, #63, #64, #68) -- Improves upon `UIAlertController` so that when an alert button is quickly tapped, it will highlight (`UIAlertController` does not do this) +- Improves upon `UIAlertController` so that when an alert button is quickly + tapped, it will highlight (`UIAlertController` does not do this) - Fixes Auto Layout warnings for multi-line labels (#60) - Returns the correct text when accessing `titleLabel.text` ### 2.0.1 **Bug Fixes:** -- Raises an exception when presenting SDCAlertView 1.0 from a `UIAlertView` or `UIActionSheet` (#56) +- Raises an exception when presenting SDCAlertView 1.0 from a `UIAlertView` or + `UIActionSheet` (#56) - Prevents a crash when creating an alert with a `nil` title or message ## 2.0 @@ -213,12 +257,14 @@ versions of `SDCAlertView`. - All new API that matches and extends `UIAlertController` - Ability to always show buttons horizontally or vertically - Backwards compatible with `SDCAlertView` (1.0) -- More stylistic elements you can style (alert width, button separators, text fields, etc.) +- More stylistic elements you can style (alert width, button separators, text + fields, etc.) ### 1.4.3 **Bug Fixes:** -- Fixes an issue that would not enforce `contentPadding` on the title and message labels properly (#58) +- Fixes an issue that would not enforce `contentPadding` on the title and + message labels properly (#58) ### 1.4.2 @@ -232,65 +278,87 @@ versions of `SDCAlertView`. - Initialization is written a little differently to be more subclass-friendly - Ready for use in iOS 8 -Though the alert can still be used in iOS 8, keep in mind that the actual iOS 8 alert looks slightly different. The plan is to eventually have support for both, but that's currently not the case. +Though the alert can still be used in iOS 8, keep in mind that the actual iOS 8 +alert looks slightly different. The plan is to eventually have support for both, +but that's currently not the case. ### 1.4 **What's New:** -- Added the ability to position a two-button alert vertically as opposed to horizontally (#29) +- Added the ability to position a two-button alert vertically as opposed to + horizontally (#29) - Added `attributedTitle` and `attributedMessage` properties (#30) **Bug Fixes:** - Auto-layout doesn't complain anymore when using `[[SDCAlertView alloc] init]` -- Fixes a bug that would not show correct button titles in certain alert configurations (#32) -- Instead of clipping button text, it now reduces the size of text on buttons appropriately (#33) +- Fixes a bug that would not show correct button titles in certain alert + configurations (#32) +- Instead of clipping button text, it now reduces the size of text on buttons + appropriately (#33) ### 1.3 **What's New:** -- The `SDCAlertViewTransitioning` protocol allows users to customize alert transitions and behavior +- The `SDCAlertViewTransitioning` protocol allows users to customize alert + transitions and behavior - `SDCAlertView` now supports `tintColor` for buttons and `contentView` **Bug Fixes:** -- Fixes an issue where the status bar style would not be preserved if it was set to `UIStatusBarStyleLightContent` (#26 & #27) +- Fixes an issue where the status bar style would not be preserved if it was set + to `UIStatusBarStyleLightContent` (#26 & #27) - Fixes a bug that causes the app to lock up due to a race condition (#28) -- Adding subviews to `contentView` won't have any animation-related side effects anymore (see #25) +- Adding subviews to `contentView` won't have any animation-related side effects + anymore (see #25) ### 1.2.1 **Bug Fixes:** -- Resolves an issue that could put an app in a bad state when transitioning to and from multiple alerts in rapid succession. +- Resolves an issue that could put an app in a bad state when transitioning to + and from multiple alerts in rapid succession. ### 1.2 **What's New:** - New convenience methods for showing alerts more easily - The `SDCAlertViewWidth` constant is made public (#24) -- The `contentView` does not require vertical constraints anymore, though they can be used to size the `contentView` other than "hug its subviews" (#23) +- The `contentView` does not require vertical constraints anymore, though they + can be used to size the `contentView` other than "hug its subviews" (#23) **Bug Fixes:** -- Resolves an issue that would show the status bar if it was previously hidden (#21) -- Resolves an issue that would sometimes return the wrong button index when using `addButtonWithTitle:` -- Transitions between alerts flow more logically if you call several show or dismiss methods right after each other (#25) -- The `animated` argument in `dismissWithClickedButtonIndex:animated:` is honored again +- Resolves an issue that would show the status bar if it was previously hidden + (#21) +- Resolves an issue that would sometimes return the wrong button index when + using `addButtonWithTitle:` +- Transitions between alerts flow more logically if you call several show or + dismiss methods right after each other (#25) +- The `animated` argument in `dismissWithClickedButtonIndex:animated:` is + honored again ### 1.1 **What's New:** -- Support for styling an alert. A number of properties have been exposed so that alerts can be easily given a different style. Styling all alerts in an app using `UIAppearance` is also supported. +- Support for styling an alert. A number of properties have been exposed so that + alerts can be easily given a different style. Styling all alerts in an app + using `UIAppearance` is also supported. **Bug Fixes:** - The alert's background is no longer using the toolbar hack (#16 & #17) -- Accessing a text field before calling `show` does not cause a crash anymore (#14) -- The color of text on a button color did not match `UIAlertView`'s text color 100% -- A disabled button will have its label's `enabled` property set to `NO`, which is also where it gets its gray color from -- A 1px hairline at the bottom of the right button in a two-button alert was removed +- Accessing a text field before calling `show` does not cause a crash anymore + (#14) +- The color of text on a button color did not match `UIAlertView`'s text color + 100% +- A disabled button will have its label's `enabled` property set to `NO`, which + is also where it gets its gray color from +- A 1px hairline at the bottom of the right button in a two-button alert was + removed ## 1.0 This release marks the first official release of SDCAlertView. -SDCAlertView's behavior should now match UIAlertView's. Any behavior that's different from UIAlertView and is not documented as a "won't fix" or known bug is considered a bug that needs to be solved. +SDCAlertView's behavior should now match UIAlertView's. Any behavior that's +different from UIAlertView and is not documented as a "won't fix" or known bug +is considered a bug that needs to be solved. ## 0.9 diff --git a/README.md b/README.md index 3045235f..1be48b50 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ # SDCAlertView -![CI Status](https://travis-ci.org/sberrevoets/SDCAlertView.svg?branch=master) -![CocoaPods](https://img.shields.io/cocoapods/v/SDCAlertView.svg) -![Carthage](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat) - `SDCAlertView` started out as an alert that looked identical to `UIAlertView`, but had support for a custom content view. With the introduction of `UIAlertController` in iOS 8, the project was updated to the more modern API that `UIAlertController` brought.
@@ -28,22 +24,20 @@ # Requirements - - Swift 3 - - iOS 8 or higher - -If you want to use the library on iOS 7, please use version 2.5.4 (the latest 2.x release). + - Swift 4 + - iOS 9 or higher # Installation ## CocoaPods -To install SDCAlertView using CocoaPods, please integrate it in your existing Podfile, or create a new Podfile: +To install SDCAlertView using CocoaPods, integrate it in your existing Podfile, or create a new Podfile: ```ruby -platform :ios, '8.0' +platform :ios, '9.0' use_frameworks! target 'MyApp' do - pod 'SDCAlertView', '~> 7.1' + pod 'SDCAlertView' end ``` @@ -53,7 +47,7 @@ Then run `pod install`. To install with Carthage, add the following line to your `Cartfile`: ```ruby -github "sberrevoets/SDCAlertView" ~> 7.1 +github "sberrevoets/SDCAlertView" ``` Run `carthage update` and drag `SDCAlertView.framework` in the `Build` folder into your project. diff --git a/SDCAlertView.podspec b/SDCAlertView.podspec index 39f67217..84b75530 100644 --- a/SDCAlertView.podspec +++ b/SDCAlertView.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SDCAlertView" - s.version = "7.1.2" + s.version = "8.0" s.summary = "The little alert that could" s.homepage = "https://github.com/sberrevoets/SDCAlertView" s.license = { :type => "MIT" }