diff --git a/Example iOS/Controllers/PresetsController.swift b/Example iOS/Controllers/PresetsController.swift index efa8ccf..70f80e8 100644 --- a/Example iOS/Controllers/PresetsController.swift +++ b/Example iOS/Controllers/PresetsController.swift @@ -2,9 +2,10 @@ import UIKit import SparrowKit import SPDiffable -// Maybe ding toolbar class PresetsController: SPDiffableTableController { + // MARK: - Init + init() { super.init(style: .insetGrouped) } @@ -13,15 +14,27 @@ class PresetsController: SPDiffableTableController { fatalError("init(coder:) has not been implemented") } + // MARK: - Lifecycle + override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "SPAlert Presets" + + currentPreset = presets.first! setCellProviders(SPDiffableTableCellProviders.default, sections: content) navigationController?.isToolbarHidden = false toolbarItems = [ - .init(image: .system("chevron.down.circle.fill"), primaryAction: .init(handler: { (action) in - #warning("doing next switch") + .init(image: .system("chevron.down.circle"), primaryAction: .init(handler: { (action) in + guard let currentPreset = self.currentPreset else { + self.currentPreset = self.presets.first + return + } + guard let index = self.presets.firstIndex(where: { $0.id == currentPreset.id }) else { return } + self.currentPreset = self.presets[safe: index + 1] + if self.currentPreset == nil { + self.currentPreset = self.presets.first + } })), .init(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), .init(systemItem: .play, primaryAction: .init(handler: { (action) in @@ -31,6 +44,8 @@ class PresetsController: SPDiffableTableController { ] } + // MARK: - Data + fileprivate var presets: [AlertPreset] { return [ AlertPreset(name: "Done", preset: .done), @@ -38,10 +53,29 @@ class PresetsController: SPDiffableTableController { ] } + // MARK: - Diffable + + var currentPreset: AlertPreset? { + willSet { + guard let id = self.currentPreset?.id else { return } + let cell = diffableDataSource?.cell(UITableViewCell.self, for: id) + cell?.accessoryType = .none + } + didSet { + guard let id = self.currentPreset?.id else { return } + let cell = diffableDataSource?.cell(UITableViewCell.self, for: id) + cell?.accessoryType = .checkmark + } + } + var content: [SPDiffableSection] { let items = presets.map { (preset) -> SPDiffableTableRow in - return SPDiffableTableRow(text: preset.name, accessoryType: .none, selectionStyle: .none) { (_) in - return + return SPDiffableTableRow( + text: preset.name, + accessoryType: (preset.id == currentPreset?.id) ? .checkmark : .none, + selectionStyle: .none) { [weak self] _ in + guard let self = self else { return } + self.currentPreset = preset } } return [ @@ -49,9 +83,3 @@ class PresetsController: SPDiffableTableController { ] } } - -struct AlertPreset { - - var name: String - var preset: SPAlertPreset -} diff --git a/Example iOS/Models/AlertPreset.swift b/Example iOS/Models/AlertPreset.swift new file mode 100644 index 0000000..94970eb --- /dev/null +++ b/Example iOS/Models/AlertPreset.swift @@ -0,0 +1,11 @@ +import UIKit + +struct AlertPreset { + + var name: String + var preset: SPAlertIconPreset + + var id: String { + return name + } +} diff --git a/Sources/SPAlert/Models/SPAlertLayout.swift b/Sources/SPAlert/Models/SPAlertLayout.swift index bae3754..335601a 100644 --- a/Sources/SPAlert/Models/SPAlertLayout.swift +++ b/Sources/SPAlert/Models/SPAlertLayout.swift @@ -21,9 +21,45 @@ import UIKit -public struct SPAlertLayout { +open class SPAlertLayout { - var iconSize: CGSize = .init(width: 100, height: 100) - var margins: UIEdgeInsets = .init(top: 43, left: 16, bottom: 25, right: 16) - var spaceBetweenIconAndTitle: CGFloat = 41 + var iconSize: CGSize + var margins: UIEdgeInsets + var spaceBetweenIconAndTitle: CGFloat + + init() { + iconSize = .init(width: 100, height: 100) + margins = .init(top: 43, left: 16, bottom: 25, right: 16) + spaceBetweenIconAndTitle = 41 + } + + init(iconSize: CGSize, margins: UIEdgeInsets, spaceBetweenIconAndTitle: CGFloat) { + self.iconSize = iconSize + self.margins = margins + self.spaceBetweenIconAndTitle = spaceBetweenIconAndTitle + } +} + +public extension SPAlertLayout { + + private static var defaultHorizontalInset: CGFloat { return 16 } + + convenience init(preset: SPAlertIconPreset) { + switch preset { + case .done: + self.init( + iconSize: .init(width: 112, height: 112), + margins: .init(top: 63, left: Self.defaultHorizontalInset, bottom: 29, right: Self.defaultHorizontalInset), + spaceBetweenIconAndTitle: 35 + ) + case .heart: + self.init( + iconSize: .init(width: 112, height: 77), + margins: .init(top: 49, left: Self.defaultHorizontalInset, bottom: 25, right: Self.defaultHorizontalInset), + spaceBetweenIconAndTitle: 35 + ) + default: + self.init() + } + } } diff --git a/Sources/SPAlert/SPAlert.swift b/Sources/SPAlert/SPAlert.swift index 8884d84..b12a66b 100644 --- a/Sources/SPAlert/SPAlert.swift +++ b/Sources/SPAlert/SPAlert.swift @@ -23,7 +23,7 @@ import UIKit public enum SPAlert { - public static func present(title: String, message: String? = nil, preset: SPAlertPreset, completion: (() -> Void)? = nil) { + public static func present(title: String, message: String? = nil, preset: SPAlertIconPreset, completion: (() -> Void)? = nil) { let alertView = SPAlertView(title: title, message: message, preset: preset) alertView.present(completion: completion) } diff --git a/Sources/SPAlert/SPAlertPreset.swift b/Sources/SPAlert/SPAlertIconPreset.swift similarity index 90% rename from Sources/SPAlert/SPAlertPreset.swift rename to Sources/SPAlert/SPAlertIconPreset.swift index 7583929..87480af 100644 --- a/Sources/SPAlert/SPAlertPreset.swift +++ b/Sources/SPAlert/SPAlertIconPreset.swift @@ -21,7 +21,7 @@ import UIKit -public enum SPAlertPreset: CaseIterable { +public enum SPAlertIconPreset: CaseIterable { case done case heart @@ -30,7 +30,12 @@ public enum SPAlertPreset: CaseIterable { // MARK: - CaseIterable - public static var allCases: [SPAlertPreset] { + public static var allCases: [SPAlertIconPreset] { return [.done, .heart] } } + +extension SPAlertLayout { + + +} diff --git a/Sources/SPAlert/SPAlertView.swift b/Sources/SPAlert/SPAlertView.swift index c5f3718..535efa2 100644 --- a/Sources/SPAlert/SPAlertView.swift +++ b/Sources/SPAlert/SPAlertView.swift @@ -25,15 +25,9 @@ open class SPAlertView: UIView { // MARK: - Views - private lazy var titleLabel: UILabel = { - let label = UILabel() - return label - }() + private var titleLabel: UILabel? - private lazy var subtitleLabel: UILabel = { - let label = UILabel() - return label - }() + private var subtitleLabel: UILabel? private var iconView: UIView? @@ -49,29 +43,26 @@ open class SPAlertView: UIView { return view }() + weak open var presentWindow: UIWindow? = UIApplication.shared.windows.first + // MARK: - Properties open var dismissByTap: Bool = true // MARK: - Initializers - public init(title: String, message: String?, preset: SPAlertPreset) { - super.init(frame: CGRect.zero) - commonInit() - } - - public init(title: String, message: String?, icon view: UIView) { - super.init(frame: CGRect.zero) - commonInit() - } - - public init(title: String, message: String?, image: UIImage) { + public init(title: String, message: String?, preset: SPAlertIconPreset) { super.init(frame: CGRect.zero) + setTitle(title) + if let message = message { + setMessage(message) + } commonInit() } public init(message: String) { super.init(frame: CGRect.zero) + setMessage(message) commonInit() } @@ -80,15 +71,97 @@ open class SPAlertView: UIView { commonInit() } + // MARK: Configure + + private func setTitle(_ text: String) { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 22) + label.numberOfLines = 0 + let style = NSMutableParagraphStyle() + style.lineSpacing = 3 + style.alignment = .center + label.attributedText = NSAttributedString(string: text, attributes: [.paragraphStyle: style]) + titleLabel = label + } + + private func setMessage(_ text: String) { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.numberOfLines = 0 + let style = NSMutableParagraphStyle() + style.lineSpacing = 2 + style.alignment = .center + label.attributedText = NSAttributedString(string: text, attributes: [.paragraphStyle: style]) + subtitleLabel = label + } + func commonInit() { layer.masksToBounds = true layer.cornerRadius = 8 backgroundColor = .clear + addSubview(backgroundView) + + [titleLabel, subtitleLabel, iconView].forEach({ + if let view = $0 { + addSubview(view) + } + }) } // MARK: - Present + fileprivate var presentDismissDuration: TimeInterval = 0.2 + fileprivate var presentDismissScale: CGFloat = 0.8 + open func present(duration: TimeInterval = 1.5, haptic: SPAlertHaptic = .success, completion: (() -> Void)? = nil) { + guard let window = self.presentWindow else { return } + window.addSubview(self) + // Prepare for present + alpha = 0 + layout() + transform = transform.scaledBy(x: self.presentDismissScale, y: self.presentDismissScale) + + UIView.animate(withDuration: presentDismissDuration, animations: { + self.alpha = 1 + self.transform = CGAffineTransform.identity + }, completion: { finished in + /*if let iconView = self.iconView as? SPAlertIconAnimatable { + iconView.animate() + }*/ + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration) { + self.dismiss() + } + }) + } + + open func dismiss() { + UIView.animate(withDuration: presentDismissDuration, animations: { + self.alpha = 0 + self.transform = self.transform.scaledBy(x: self.presentDismissScale, y: self.presentDismissScale) + }, completion: { finished in + self.removeFromSuperview() + }) + } + + // MARK: - Layout + + //private var layout: SPAlertLayout + + fileprivate func layout() { + guard let window = self.presentWindow else { return } + frame = CGRect.init(x: 0, y: 0, width: 250, height: 250) + sizeToFit() + center = .init(x: window.frame.midX, y: window.frame.midY) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + layoutSubviews() + return CGSize.init(width: 250, height: 270) + } + + open override func layoutSubviews() { + super.layoutSubviews() + backgroundView.frame = bounds } }