2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.8
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "AlertViewCustom",
8 | platforms: [
9 | .iOS(.v14),
10 | .macOS(.v11)
11 | ],
12 | products: [
13 | .library(
14 | name: "AlertViewCustom",
15 | targets: ["AlertViewCustom"]),
16 | ],
17 | dependencies: [],
18 | targets: [
19 | .target(
20 | name: "AlertViewCustom",
21 | dependencies: [])
22 | ]
23 | )
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | []([https://swift.org/package-manager/](https://github.com/apple/swift-package-manager))
2 | [](https://cocoapods.org/pods/AlertViewCustom)
3 | [](https://cocoapods.org/pods/AlertViewCustom)
4 |
5 |
6 | [](https://swiftpackageindex.com/jadebowl/AlertViewCustom)
7 |
8 | [](https://app.codacy.com/gh/jadebowl/AlertViewCustom/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
9 |
10 |
11 | # AlertViewCustom
12 |
13 |
14 | With AlertViewCustom you can create your own customised UIAlertView instead of using the default one from Apple, which doesn't always fit in with the style of your app.
15 |
16 |
17 | ## Features
18 | - Can be used both in UIKit and SwiftUI
19 | - Add Icon
20 | - Personalise Title, Message and both Buttons
21 | - Possibility to hide Title, Message and Cancel Button
22 | - Change Alert Position (.center or .bottom)
23 | - Change Agree Button Corner Radius
24 | - Change Agree Button Color
25 | - Change View Background Color
26 | - Change Corner Radius of the whole AlertView
27 | - Add Animation from the Bottom when in .bottom Position
28 |
29 | ### Latest Updates:
30 | - Possibility to change Font
31 | - Possibility to have the Agree Button Outlined
32 |
33 | ## Examples
34 |
35 |
36 | Bottom & No Title
37 | Font Custom
38 | Icon & Color
39 | One Button
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Outlined Button
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ## Installation
62 |
63 | ### Swift Package Manager
64 |
65 | To integrate using Apple's [Swift Package Manager](https://swift.org/package-manager/), add the following as a dependency to your `Package.swift`:
66 |
67 | ```swift
68 | dependencies: [
69 | .package(url: "https://github.com/jadebowl/AlertViewCustom.git", from: "4.0.0")
70 | ]
71 | ```
72 |
73 | Alternatively navigate to your Xcode project, select `Swift Packages` and click the `+` icon to search for `AlertViewCustom`.
74 |
75 | ### CocoaPods
76 |
77 | AlertViewCustom is available through [CocoaPods](http://cocoapods.org). To install
78 | it, simply add the following line to your Podfile:
79 |
80 | ```ruby
81 | pod 'AlertViewCustom'
82 | ```
83 |
84 | ### Manually
85 |
86 | If you prefer not to use any of the aforementioned dependency managers, you can integrate AlertViewCustom into your project manually. Simply drag the `Sources` Folder into your Xcode project.
87 |
88 | ## Usage
89 |
90 | Create an Alert:
91 | ```swift
92 | import AlertViewCustom
93 |
94 | var alert = AlertView()
95 | ```
96 |
97 | Customise the UI and add the Fade transition:
98 | ```swift
99 | let agreeButton = AgreeButton(title: "Go to Settings")
100 | let alertSettings = AlertSettings(accentColor: .systemBlue,
101 | backgroundColor: .systemBackground,
102 | icon: UIImage(systemName: "hand.wave"),
103 | title: "I am a title",
104 | message: "Lorem ipsum dolor sit amet, consectetuadipiscing elit, sed do eiusmod tempor incididunt ulabore et dolore magna aliqua.",
105 | agreeButton: agreeButton,
106 | cancelTitle: "Cancel",
107 | position: .bottom(animated: true))
108 | alert.setupContents(delegate: self, settings: alertSettings)
109 | alert.fadeIn(duration: 0.3)
110 | ```
111 |
112 | Manage Actions:
113 | ```swift
114 | extension Controller: AlertViewDelegate {
115 | func agreeAction() {
116 | // MARK: - Example: Go to Settings
117 | guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
118 | if UIApplication.shared.canOpenURL(settingsUrl) {
119 | UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
120 | print("Settings opened: \(success)")
121 | })
122 | }
123 | }
124 |
125 | func cancelAction() {
126 | alert.removeFromSuperView(duration: 0.3)
127 | }
128 | }
129 | ```
130 |
131 | ## Contributing
132 | Contributions are very welcome 🙌
133 |
--------------------------------------------------------------------------------
/Sources/AlertViewCustom/AlertView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public protocol AlertViewDelegate: AnyObject {
4 | func agreeAction()
5 | func cancelAction()
6 | }
7 |
8 | /**
9 | With AlertViewCustom you can create your own customised UIAlertView instead of using the default one from Apple
10 | */
11 | public class AlertView {
12 | public var delegate: AlertViewDelegate? {
13 | didSet {
14 | alertView.delegate = delegate
15 | }
16 | }
17 | private let alertView: AlertViewCustom
18 | private let hostVC: UIViewController
19 | private let alertWindow: UIWindow
20 |
21 | public init() {
22 | self.alertView = AlertViewCustom()
23 | self.alertWindow = UIWindow()
24 | self.alertWindow.backgroundColor = .clear
25 |
26 | let viewController = UIViewController()
27 | viewController.view.backgroundColor = .clear
28 | self.hostVC = viewController
29 | self.alertWindow.rootViewController = viewController
30 | }
31 |
32 | /**
33 | Setup the alert contents
34 | - parameters:
35 | - delegate: To manage the agree button and cancel button actions
36 | - settings: To customise your alert
37 | */
38 | public func setupContents(delegate: AlertViewDelegate, settings: AlertSettings) {
39 | self.delegate = delegate
40 | setupContrastColor(accentColor: settings.accentColor,
41 | backgroundColor: settings.backgroundColor,
42 | borderWidth: settings.agreeButton.borderWidth)
43 | setupBackground(backgroundColor: settings.backgroundColor, backgroundRadius: settings.backgroundRadius)
44 | setupFont(fontName: settings.fontName)
45 | setupIcon(icon: settings.icon, accentColor: settings.accentColor)
46 | setupTitles(title: settings.title, message: settings.message)
47 | setupAgreeButton(accentColor: settings.accentColor,
48 | title: settings.agreeButton.title,
49 | cornerRadius: settings.agreeButton.cornerRadius,
50 | borderWidth: settings.agreeButton.borderWidth)
51 | setupCancelButton(accentColor: settings.accentColor, cancelTitle: settings.cancelTitle)
52 | setupPosition(position: settings.position)
53 | setupHostVCConstraints()
54 | }
55 |
56 | func setupContrastColor(accentColor: UIColor, backgroundColor: UIColor, borderWidth: CGFloat) {
57 | alertView.titleLabel.textColor = backgroundColor.contrastColor()
58 | alertView.messageLabel.textColor = backgroundColor.contrastColor()
59 |
60 | let titleColor = borderWidth != 0 ? accentColor : accentColor.contrastColor()
61 | alertView.agreeButton.setTitleColor(titleColor, for: .normal)
62 | }
63 |
64 | func setupBackground(backgroundColor: UIColor, backgroundRadius: CGFloat) {
65 | alertView.backgroundView.backgroundColor = backgroundColor
66 | alertView.backgroundView.layer.cornerRadius = backgroundRadius
67 | }
68 |
69 | func setupFont(fontName: String?) {
70 | guard fontName != nil, let fontName else {
71 | return
72 | }
73 | alertView.titleLabel.font = UIFont.font(for: .headline, name: fontName)
74 | alertView.messageLabel.font = UIFont.font(for: .body, name: fontName)
75 | alertView.agreeButton.titleLabel?.font = UIFont.font(for: .button, name: fontName)
76 | alertView.cancelButton.titleLabel?.font = UIFont.font(for: .body, name: fontName)
77 | }
78 |
79 | func setupIcon(icon: UIImage?, accentColor: UIColor) {
80 | alertView.iconImageView.image = icon
81 | alertView.iconImageView.tintColor = accentColor
82 | alertView.iconImageView.isHidden = alertView.iconImageView.image == nil
83 | }
84 |
85 | func setupTitles(title: String?, message: String?) {
86 | alertView.titleLabel.text = title
87 | alertView.titleLabel.isHidden = alertView.titleLabel.text == nil
88 | alertView.messageLabel.text = message
89 | alertView.messageLabel.isHidden = alertView.messageLabel.text == nil
90 | }
91 |
92 | func setupAgreeButton(accentColor: UIColor, title: String, cornerRadius: CGFloat, borderWidth: CGFloat) {
93 | alertView.agreeButton.setTitle(title, for: .normal)
94 | alertView.agreeButton.backgroundColor = borderWidth != 0 ? .clear : accentColor
95 | alertView.agreeButton.layer.borderColor = accentColor.cgColor
96 | alertView.agreeButton.layer.borderWidth = borderWidth
97 | alertView.agreeButton.layer.cornerRadius = cornerRadius
98 | }
99 |
100 | func setupCancelButton(accentColor: UIColor, cancelTitle: String?) {
101 | alertView.cancelButton.setTitle(cancelTitle, for: .normal)
102 | alertView.cancelButton.setTitleColor(accentColor, for: .normal)
103 | alertView.cancelButton.isHidden = cancelTitle == nil
104 | }
105 |
106 | func setupPosition(position: AlertPosition?) {
107 | guard let position else { return }
108 | switch position {
109 | case .bottom(let animation):
110 | alertView.alertBottomAnimation = animation
111 | alertView.backgroundView.translatesAutoresizingMaskIntoConstraints = false
112 | alertView.alertCenterConstraint?.isActive = false
113 | alertView.alertTopConstraint?.isActive = false
114 | alertView.alertBottomConstraint = alertView.backgroundView.bottomAnchor
115 | .constraint(equalTo: alertView.contentView.bottomAnchor, constant: -32)
116 | alertView.alertBottomConstraint?.isActive = true
117 | default:
118 | alertView.backgroundView.translatesAutoresizingMaskIntoConstraints = false
119 | alertView.alertBottomConstraint?.isActive = false
120 |
121 | alertView.backgroundView.centerXAnchor
122 | .constraint(equalTo: alertView.contentView.centerXAnchor).isActive = true
123 | alertView.backgroundView.centerYAnchor
124 | .constraint(equalTo: alertView.contentView.centerYAnchor).isActive = true
125 | alertView.backgroundView.widthAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true
126 | alertView.backgroundView.heightAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true
127 |
128 | alertView.alertCenterConstraint = alertView.backgroundView.centerYAnchor
129 | .constraint(equalTo: alertView.contentView.centerYAnchor)
130 | alertView.alertCenterConstraint?.isActive = true
131 | }
132 | }
133 |
134 | func setupHostVCConstraints() {
135 | hostVC.view.addSubview(alertView)
136 | alertView.translatesAutoresizingMaskIntoConstraints = false
137 | alertView.topAnchor.constraint(equalTo: hostVC.view.topAnchor, constant: 0).isActive = true
138 | alertView.bottomAnchor.constraint(equalTo: hostVC.view.bottomAnchor, constant: 0).isActive = true
139 | alertView.leadingAnchor.constraint(equalTo: hostVC.view.leadingAnchor, constant: 0).isActive = true
140 | alertView.trailingAnchor.constraint(equalTo: hostVC.view.trailingAnchor, constant: 0).isActive = true
141 | alertView.alpha = 0.0
142 | }
143 |
144 | /**
145 | Make the alert appear
146 | - parameters:
147 | - duration
148 | */
149 | public func fadeIn(duration: TimeInterval) {
150 | if let activeScene = UIApplication.shared.activeWindowScene {
151 | alertWindow.windowScene = activeScene
152 | alertWindow.frame = activeScene.coordinateSpace.bounds
153 | }
154 | alertWindow.isHidden = false
155 | alertView.isHidden = false
156 | if alertView.alertBottomAnimation {
157 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
158 | self.alertView.alertBottomConstraint?.constant = -96
159 | UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut) {
160 | self.alertView.contentView.layoutIfNeeded()
161 | self.alertView.alpha = 1
162 | }
163 | }
164 | } else {
165 | UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut) {
166 | self.alertView.alpha = 1
167 | }
168 | }
169 | }
170 |
171 | /**
172 | Make the alert disappear
173 | - parameters:
174 | - duration
175 | */
176 | public func removeFromSuperView(duration: TimeInterval) {
177 | if alertView.alertBottomAnimation {
178 | alertView.alertBottomConstraint?.constant = -32
179 | }
180 |
181 | UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
182 | self.alertView.alpha = 0.0
183 | if self.alertView.alertBottomAnimation {
184 | self.alertView.contentView.layoutIfNeeded()
185 | }
186 | }, completion: { completed in
187 | guard completed else { return }
188 | self.removeWindow()
189 | })
190 | }
191 |
192 | private func removeWindow() {
193 | alertWindow.isHidden = true
194 | alertWindow.windowScene = nil
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/Sources/AlertViewCustom/AlertViewCustom.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class AlertViewCustom: UIView {
4 | lazy var contentView: UIView = {
5 | let view = UIView()
6 | view.backgroundColor = .clear
7 | return view
8 | }()
9 |
10 | lazy var blurredView: UIVisualEffectView = {
11 | let blurEffect = UIBlurEffect(style: .dark)
12 | let blurredEffectView = UIVisualEffectView(effect: blurEffect)
13 | return blurredEffectView
14 | }()
15 |
16 | lazy var backgroundView: UIView = {
17 | let view = UIView()
18 | return view
19 | }()
20 |
21 | lazy var mainStackView: UIStackView = {
22 | let stack = UIStackView()
23 | stack.axis = .vertical
24 | stack.alignment = .center
25 | stack.distribution = .fill
26 | stack.spacing = 24
27 | return stack
28 | }()
29 |
30 | lazy var iconImageView: UIImageView = {
31 | let image = UIImageView()
32 | return image
33 | }()
34 |
35 | lazy var titlesStackView: UIStackView = {
36 | let stack = UIStackView()
37 | stack.axis = .vertical
38 | stack.alignment = .fill
39 | stack.distribution = .fill
40 | stack.spacing = 12
41 | return stack
42 | }()
43 |
44 | lazy var titleLabel: UILabel = {
45 | let title = UILabel()
46 | title.numberOfLines = 0
47 | title.textAlignment = .center
48 | title.font = .preferredFont(forTextStyle: .headline)
49 | return title
50 | }()
51 |
52 | lazy var messageLabel: UILabel = {
53 | let title = UILabel()
54 | title.numberOfLines = 0
55 | title.textAlignment = .center
56 | title.font = .preferredFont(forTextStyle: .subheadline)
57 | return title
58 | }()
59 |
60 | lazy var buttonsStackView: UIStackView = {
61 | let stack = UIStackView()
62 | stack.axis = .vertical
63 | stack.alignment = .fill
64 | stack.distribution = .fill
65 | stack.spacing = 4
66 | return stack
67 | }()
68 |
69 | lazy var agreeButton: UIButton = {
70 | let button = UIButton()
71 | button.titleLabel?.font = .systemFont(ofSize: 17, weight: .semibold)
72 | return button
73 | }()
74 |
75 | lazy var cancelButton: UIButton = {
76 | let button = UIButton()
77 | button.titleLabel?.font = .systemFont(ofSize: 17, weight: .regular)
78 | return button
79 | }()
80 |
81 | weak var delegate: AlertViewDelegate?
82 |
83 | var alertCenterConstraint: NSLayoutConstraint?
84 | var alertTopConstraint: NSLayoutConstraint?
85 | var alertBottomConstraint: NSLayoutConstraint?
86 | var alertBottomAnimation = false
87 |
88 | func setupViews() {
89 | setupContentView()
90 | setupBlurredView()
91 | setupBackgroundView()
92 | setupMainStack()
93 | setupIcon()
94 | setupTitles()
95 | setupButtons()
96 | setupActions()
97 | }
98 |
99 | private func setupContentView() {
100 | addSubview(contentView)
101 | contentView.translatesAutoresizingMaskIntoConstraints = false
102 | contentView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
103 | contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
104 | contentView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
105 | contentView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
106 | }
107 |
108 | private func setupBlurredView() {
109 | contentView.addSubview(blurredView)
110 | blurredView.translatesAutoresizingMaskIntoConstraints = false
111 | blurredView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
112 | blurredView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
113 | blurredView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0).isActive = true
114 | blurredView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true
115 | }
116 |
117 | private func setupBackgroundView() {
118 | contentView.addSubview(backgroundView)
119 | backgroundView.translatesAutoresizingMaskIntoConstraints = false
120 | backgroundView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 32).isActive = true
121 | backgroundView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
122 | }
123 |
124 | private func setupMainStack() {
125 | backgroundView.addSubview(mainStackView)
126 | mainStackView.translatesAutoresizingMaskIntoConstraints = false
127 | mainStackView.topAnchor.constraint(equalTo: backgroundView.topAnchor, constant: 32).isActive = true
128 | mainStackView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: -16).isActive = true
129 | mainStackView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 0).isActive = true
130 | mainStackView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: 0).isActive = true
131 | }
132 |
133 | private func setupIcon() {
134 | mainStackView.insertArrangedSubview(iconImageView, at: 0)
135 | iconImageView.contentMode = .scaleAspectFit
136 | iconImageView.translatesAutoresizingMaskIntoConstraints = false
137 | iconImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
138 | iconImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
139 | }
140 |
141 | private func setupTitles() {
142 | mainStackView.insertArrangedSubview(titlesStackView, at: 1)
143 | titlesStackView.translatesAutoresizingMaskIntoConstraints = false
144 | titlesStackView.insertArrangedSubview(titleLabel, at: 0)
145 | titlesStackView.insertArrangedSubview(messageLabel, at: 1)
146 | titlesStackView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor, constant: 16).isActive = true
147 | }
148 |
149 | private func setupButtons() {
150 | mainStackView.insertArrangedSubview(buttonsStackView, at: 2)
151 | buttonsStackView.insertArrangedSubview(agreeButton, at: 0)
152 | buttonsStackView.insertArrangedSubview(cancelButton, at: 1)
153 | buttonsStackView.translatesAutoresizingMaskIntoConstraints = false
154 | buttonsStackView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor, constant: 16).isActive = true
155 | agreeButton.translatesAutoresizingMaskIntoConstraints = false
156 | agreeButton.heightAnchor.constraint(equalToConstant: 56).isActive = true
157 | cancelButton.translatesAutoresizingMaskIntoConstraints = false
158 | cancelButton.heightAnchor.constraint(equalToConstant: 56).isActive = true
159 | }
160 |
161 | private func setupActions() {
162 | agreeButton.addTarget(self, action: #selector(agreeAction), for: .touchUpInside)
163 | cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
164 | }
165 |
166 | @objc private func agreeAction(_ sender: UIButton) {
167 | delegate?.agreeAction()
168 | }
169 |
170 | @objc private func cancelAction(_ sender: UIButton) {
171 | delegate?.cancelAction()
172 | }
173 |
174 | override init(frame: CGRect) {
175 | super.init(frame: frame)
176 | setupViews()
177 | }
178 |
179 | required init?(coder aDecoder: NSCoder) {
180 | super.init(coder: aDecoder)
181 | setupViews()
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/Sources/AlertViewCustom/Utils/Enums.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | /// Choose the alert position
4 | public enum AlertPosition {
5 | /// Default position
6 | case center
7 | /// When set to true the alert appears with an animation from the bottom
8 | case bottom(animated: Bool)
9 | }
10 |
--------------------------------------------------------------------------------
/Sources/AlertViewCustom/Utils/Extensions.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | internal extension UIColor {
4 | func contrastColor() -> UIColor {
5 | var bit = CGFloat(0)
6 | var red = CGFloat(0)
7 | var green = CGFloat(0)
8 | var blue = CGFloat(0)
9 | var alpha = CGFloat(0)
10 |
11 | getRed(&red, green: &green, blue: &blue, alpha: &alpha)
12 |
13 | let luminance = 1 - ((0.299 * red) + (0.587 * green) + (0.114 * blue))
14 | if luminance < 0.5 {
15 | bit = CGFloat(0)
16 | } else {
17 | bit = CGFloat(1)
18 | }
19 | return UIColor(red: bit, green: bit, blue: bit, alpha: 1)
20 | }
21 | }
22 |
23 | extension UIWindow {
24 | func dismiss() {
25 | isHidden = true
26 | windowScene = nil
27 | }
28 | }
29 |
30 | internal extension UIApplication {
31 | var activeWindowScene: UIWindowScene? {
32 | return connectedScenes
33 | .compactMap { $0 as? UIWindowScene }
34 | .first { $0.activationState == .foregroundActive }
35 | }
36 | }
37 |
38 | internal extension UIFont {
39 | enum FontStyle {
40 | case headline
41 | case body
42 | case button
43 |
44 | var metrics: UIFontMetrics? {
45 | switch self {
46 | case .headline: return UIFontMetrics(forTextStyle: .headline)
47 | case .body: return UIFontMetrics(forTextStyle: .body)
48 | case .button: return UIFontMetrics(forTextStyle: .body)
49 | }
50 | }
51 |
52 | var size: CGFloat {
53 | switch self {
54 | case .headline: return 19
55 | case .body: return 17
56 | case .button: return 19
57 | }
58 | }
59 |
60 | func fontName(name: String) -> String {
61 | switch self {
62 | case .headline, .button: return name+"-Bold"
63 | case .body: return name+"-Regular"
64 | }
65 | }
66 | }
67 |
68 | static func font(for style: FontStyle, name: String) -> UIFont {
69 | let font = UIFont(name: style.fontName(name: name), size: style.size) ?? UIFont.systemFont(ofSize: style.size)
70 | return style.metrics?.scaledFont(for: font) ?? font
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Sources/AlertViewCustom/Utils/Structs.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | /**
4 | All the alert properties you can customise
5 | - parameters:
6 | - accentColor: Applies color to icon, agree button background and cancel button title
7 | - backgroundColor
8 | - backgroundRadius: Applies a corner radius to the alert view
9 | - fontName: Applies a custom font to title, message, agree button and cancel button.
10 | Font files imported in the project folder have to be named with suffixes "-Regular" and "-Bold"
11 | - icon
12 | - title
13 | - message
14 | - agreeButton: With a title, a corner radius and a border width
15 | - cancelTitle
16 | - position
17 | */
18 | public struct AlertSettings {
19 | public init(accentColor: UIColor,
20 | backgroundColor: UIColor,
21 | backgroundRadius: CGFloat = 16,
22 | fontName: String? = nil,
23 | icon: UIImage? = nil,
24 | title: String? = nil,
25 | message: String? = nil,
26 | agreeButton: AgreeButton,
27 | cancelTitle: String? = nil,
28 | position: AlertPosition? = .center) {
29 | self.accentColor = accentColor
30 | self.backgroundColor = backgroundColor
31 | self.backgroundRadius = backgroundRadius
32 | self.fontName = fontName
33 | self.icon = icon
34 | self.title = title
35 | self.message = message
36 | self.agreeButton = agreeButton
37 | self.cancelTitle = cancelTitle
38 | self.position = position
39 | }
40 | let accentColor: UIColor
41 | let backgroundColor: UIColor
42 | let backgroundRadius: CGFloat
43 | let fontName: String?
44 | let icon: UIImage?
45 | let title: String?
46 | let message: String?
47 | let agreeButton: AgreeButton
48 | let cancelTitle: String?
49 | let position: AlertPosition?
50 | }
51 |
52 | /**
53 | Customise the agree button
54 | - parameters:
55 | - title
56 | - cornerRadius
57 | - borderWidth: Applies a border with the accentColor and makes the background clear
58 | */
59 | public struct AgreeButton {
60 | public init(title: String, cornerRadius: CGFloat = 16, borderWidth: CGFloat = 0) {
61 | self.title = title
62 | self.cornerRadius = cornerRadius
63 | self.borderWidth = borderWidth
64 | }
65 | let title: String
66 | let cornerRadius: CGFloat
67 | let borderWidth: CGFloat
68 | }
69 |
--------------------------------------------------------------------------------
/SwiftUIExamples/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SwiftUIExamples/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/SwiftUIExamples/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUIExamples/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftUIExamples
4 | //
5 | // Created by Giada Ciotola on 28/09/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | @StateObject var viewModel = ContentViewModel()
12 |
13 | var body: some View {
14 | Color.blue
15 | .ignoresSafeArea(.all)
16 | .overlay(
17 | VStack {
18 | Button {
19 | viewModel.showAlert()
20 | } label: {
21 | Text("Show Alert")
22 | }.font(.system(size: 17, weight: .semibold, design: .default))
23 | .foregroundColor(.white)
24 | })
25 | }
26 | }
27 |
28 | #Preview {
29 | ContentView()
30 | }
31 |
--------------------------------------------------------------------------------
/SwiftUIExamples/ContentViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentViewModel.swift
3 | // SwiftUIExamples
4 | //
5 | // Created by Giada Ciotola on 18/10/23.
6 | //
7 |
8 | import SwiftUI
9 | import AlertViewCustom
10 |
11 | class ContentViewModel: ObservableObject {
12 | let alert = AlertView()
13 |
14 | func showAlert() {
15 | let agreeButton = AgreeButton(title: "Go to Settings")
16 | let alertSettings = AlertSettings(accentColor: .systemBlue,
17 | backgroundColor: .systemBackground,
18 | icon: UIImage(systemName: "hand.wave"),
19 | title: "I am a title",
20 | message: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
21 | agreeButton: agreeButton,
22 | cancelTitle: "Cancel",
23 | position: .center)
24 | alert.setupContents(delegate: self, settings: alertSettings)
25 | alert.fadeIn(duration: 0.3)
26 | }
27 | }
28 |
29 | extension ContentViewModel: AlertViewDelegate {
30 | func agreeAction() {
31 | // MARK: - Example: Go to Settings
32 | guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
33 | if UIApplication.shared.canOpenURL(settingsUrl) {
34 | UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
35 | print("Settings opened: \(success)")
36 | })
37 | }
38 | }
39 |
40 | func cancelAction() {
41 | alert.removeFromSuperView(duration: 0.3)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/SwiftUIExamples/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUIExamples/SwiftUIExamplesApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIExamplesApp.swift
3 | // SwiftUIExamples
4 | //
5 | // Created by Giada Ciotola on 28/09/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct SwiftUIExamplesApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/UIKitExamples/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // UIKitExamples
4 | //
5 | // Created by Giada Ciotola on 28/09/23.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 | var window: UIWindow?
13 |
14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
15 | return true
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/UIKitExamples/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/UIKitExamples/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/UIKitExamples/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/UIKitExamples/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/UIKitExamples/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/UIKitExamples/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIAppFonts
6 |
7 | AveriaSerifLibre-Regular.ttf
8 | AveriaSerifLibre-Bold.ttf
9 | AveriaSerifLibre-Italic.ttf
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/UIKitExamples/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // UIKitExamples
4 | //
5 | // Created by Giada Ciotola on 28/09/23.
6 | //
7 |
8 | import UIKit
9 | import AlertViewCustom
10 |
11 | class ViewController: UIViewController {
12 |
13 | var alert = AlertView()
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | }
18 |
19 | func setupAlert() {
20 | let agreeButton = AgreeButton(title: "Go to Settings", borderWidth: 3)
21 | let alertSettings = AlertSettings(accentColor: .systemBlue,
22 | backgroundColor: .systemBackground,
23 | fontName: "AveriaSerifLibre",
24 | icon: UIImage(systemName: "hand.wave"),
25 | title: "I am a title",
26 | message: "Lorem ipsum dolor sit amet, consectetuadipiscing elit, sed do eiusmod tempor incididunt ulabore et dolore magna aliqua.",
27 | agreeButton: agreeButton,
28 | cancelTitle: "Cancel",
29 | position: .bottom(animated: true))
30 | alert.setupContents(delegate: self, settings: alertSettings)
31 | alert.fadeIn(duration: 0.3)
32 | }
33 |
34 | @IBAction func showAlert(_ sender: UIButton) {
35 | setupAlert()
36 | }
37 | }
38 |
39 | extension ViewController: AlertViewDelegate {
40 | func agreeAction() {
41 | // MARK: - Example: Go to Settings
42 | guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
43 | if UIApplication.shared.canOpenURL(settingsUrl) {
44 | UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
45 | print("Settings opened: \(success)")
46 | })
47 | }
48 | }
49 |
50 | func cancelAction() {
51 | alert.removeFromSuperView(duration: 0.3)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------