├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── AlertX │ ├── AlertX-Elements │ ├── AlertX.swift │ ├── Button.swift │ └── Window.swift │ ├── Essentials │ ├── AlertXViewController.swift │ ├── AlertX_View.swift │ └── View+Extension.swift │ └── Options │ ├── Animations.swift │ └── Themes.swift └── Tests ├── AlertXTests ├── AlertXTests.swift └── XCTestManifests.swift └── LinuxMain.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Neel Makhecha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 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: "AlertX", 8 | platforms: [.iOS(.v13), .macOS(.v10_15), .tvOS(.v13)], 9 | products: [ 10 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 11 | .library( 12 | name: "AlertX", 13 | targets: ["AlertX"]), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | // .package(url: /* package url */, from: "1.0.0"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 22 | .target( 23 | name: "AlertX", 24 | dependencies: []), 25 | .testTarget( 26 | name: "AlertXTests", 27 | dependencies: ["AlertX"]), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlertX 2 | 3 | ## Custom alerts in SwiftUI made easy 4 | 5 | AlertX is a library for SwiftUI projects to have custom alerts that can be implemented in a very easy and simple way, just like implementing built-in system alerts. 6 | 7 | ## A Quick Example 8 | 9 | 10 | import SwiftUI 11 | import AlertX 12 | ... 13 | ... 14 | 15 | 16 | Button(action: { 17 | 18 | self.showAlertX.toggle() 19 | 20 | }, label: { 21 | 22 | Text("Show AlertX") 23 | 24 | }).alertX(isPresented: $showAlertX, content: { 25 | 26 | AlertX(title: Text("AlertX Title"), 27 | message: Text("An optional message indicating some action goes here..."), 28 | primaryButton: .cancel(), 29 | secondaryButton: .default(Text("Done"), action: { 30 | // Some action 31 | }), 32 | theme: .graphite(withTransparency: true, roundedCorners: true), 33 | animation: .classicEffect()) 34 | }) 35 | 36 | 37 | ![Example AlertX Screenshot](https://neelmakhecha.tech/assets/files/alertX_documentation_image1.gif) 38 | 39 | # Documentation 40 | 41 | ## Working and Installation 42 | 43 | AlertX is a Swift Package and can be installed just like installing any other Swift Package libraries. 44 | 45 | 1. In your Xcode project, navigate to File > Swift Packages > Add Package Dependency. 46 | 2. In the package repository URL, enter this: https://github.com/neel-makhecha/AlertX.git 47 | 48 | One awesome thing about AlertX is that the implementation of it is much similar to the implementation of system's built in alerts. Once AlertX is installed and imported, a new instance method `alertX(isPresented: Binding, content: () -> AlertX)` can be used with any SwiftUI View, similar to the built-in method. 49 | 50 | 51 | ## Themes 52 | 53 | The appearance of AlertX is customizable with themes. There are a bunch of pre-defined themes already included, but you can also have your very own theme. 54 | 55 | All the themes of AlertX are of type `AlertX.Theme`. There are static methods for each pre-defined theme. Here's an example of how you can apply a theme: 56 | 57 | Button(action: { 58 | self.showAlertX.toggle() 59 | }, label: { 60 | Text("Show AlertX") 61 | }).alertX(isPresented: $showAlertX, content: { 62 | AlertX(title: Text("The Title"), theme: .wine()) 63 | }) 64 | 65 | 66 | 67 | 68 | For the static methods of all pre-defined themes, you can also pass boolean to enable or disable transparency and rounded corners for alert and buttons. By default, for all themes, transparency is true and rounded corners is set to false. 69 | 70 | AlertX.Theme.wine(withTransparency: false, roundedCorners: true) 71 | 72 | 73 | 74 | (The above example uses wine theme with transparency false and roundedCorners true) 75 | 76 | Here's a list of all pre-defined themes: 77 | 78 | graphite (default) 79 | light 80 | dark 81 | sun 82 | cherry 83 | mint 84 | wine 85 | 86 | 87 | To create a custom theme, use the following method which offers a variety of options: 88 | 89 | 90 | AlertX.Theme.custom(windowColor: Color, 91 | alertTextColor: Color, 92 | enableShadow: Bool, 93 | enableRoundedCorners: Bool, 94 | enableTransparency: Bool, 95 | cancelButtonColor: Color, 96 | cancelButtonTextColor: Color, 97 | defaultButtonColor: Color, 98 | defaultButtonTextColor: Color, 99 | roundedCornerRadius: CGFloat) 100 | 101 | 102 | ## Animations 103 | 104 | Animations for AlertX can be applied using `AlertX.AnimationX`. There are various cool pre-defined animations already included. Similar to themes, they can be applied using static method for each animation present inside `AlertX.AnimationX`. 105 | 106 | ![AlertX with default animation](https://neelmakhecha.tech/assets/files/alertX_documentation_image4.gif) 107 | 108 | Here's how you can apply an animation: 109 | 110 | `AlertX(title: Text("Some Title"), animation: .fadeEffect())` 111 | 112 | Here's a list of all pre-defined animations: 113 | 114 | defaultEffect 115 | classicEffect 116 | zoomEffect 117 | fadeEffect 118 | slideUpEffect 119 | 120 | You can also create your own animations just like you would for any other SwiftUI View using the following method: 121 | 122 | AlertX.AnimationX.custom(withTransition: AnyTransition) 123 | 124 | ## Adding More Buttons 125 | 126 | Use the initialiser which takes an array of AlertX.Button (buttonStack) to add as many buttons as you want in the alert. Here's an example (body of the closure to be passed in the alertX method): 127 | 128 | 129 | let buttons = [ 130 | AlertX.Button.default(Text("Yes")), 131 | AlertX.Button.default(Text("No")), 132 | AlertX.Button.cancel()] 133 | 134 | return AlertX(title: Text("Are you sure about this?"), buttonStack: buttons) 135 | 136 | ## Future Work 137 | 138 | 1. Support for text fields. 139 | 2. Even more and better pre-defined themes and animations. 140 | 141 | ## Contributing 142 | 143 | You are most welcome in contributing to this project with either new features (maybe one mentioned from the future work or anything else), refactoring and improving the code or adding new pre-defined themes and animations. Also, feel free to give suggestions and feedbacks. 144 | 145 | 146 | Created with ❤️ by Neel Makhecha. 147 | 148 | Get in touch on [Twitter](https://twitter.com/neelmakhecha). Visit: [neelmakhecha.tech](https://neelmakhecha.tech) 149 | -------------------------------------------------------------------------------- /Sources/AlertX/AlertX-Elements/AlertX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertX.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | 8 | import SwiftUI 9 | 10 | public struct AlertX: View { 11 | 12 | // Constant Parameters 13 | static let defaultCornerRadius: CGFloat = 25.0 14 | static let defaultShadowRadius: CGFloat = 1.0 15 | static let defaultAlertOpacity: Double = 0.9 16 | 17 | 18 | // Variable parameters 19 | var alertX_cornerRadius: CGFloat 20 | var alertX_shadowRadius: CGFloat 21 | var alertX_shadowColor: Color = Color.black 22 | 23 | // AlertX Fields 24 | var alertX_title: Text 25 | var alertX_message: Text? 26 | 27 | var buttonStack: [AlertX.Button]? 28 | 29 | // Theme and Animation 30 | var theme: AlertX.Theme = AlertX.Theme() 31 | var animation: AlertX.AnimationX = AlertX.AnimationX() 32 | 33 | public init(title: Text, message: Text? = nil, primaryButton: AlertX.Button? = .default(Text("OK")), secondaryButton: AlertX.Button? = nil, theme: AlertX.Theme = AlertX.Theme(), animation: AlertX.AnimationX = .defaultEffect()) { 34 | self.alertX_title = title 35 | self.alertX_message = message 36 | 37 | self.buttonStack = [primaryButton!] 38 | if let secondaryButton = secondaryButton { 39 | self.buttonStack?.append(secondaryButton) 40 | } 41 | 42 | self.theme = theme 43 | self.alertX_cornerRadius = theme.enableRoundedCorners ? theme.roundedCornerRadius : 0.0 44 | self.alertX_shadowRadius = theme.enableShadow ? AlertX.defaultShadowRadius : 0.0 45 | 46 | self.animation = animation 47 | } 48 | 49 | public init(title: Text, message: Text? = nil, buttonStack: [AlertX.Button] = [AlertX.Button.default(Text("OK"))], theme: AlertX.Theme = AlertX.Theme(), animation: AlertX.AnimationX = .defaultEffect()) { 50 | self.alertX_title = title 51 | self.alertX_message = message 52 | 53 | self.buttonStack = buttonStack 54 | 55 | self.theme = theme 56 | self.alertX_cornerRadius = theme.enableRoundedCorners ? theme.roundedCornerRadius : 0.0 57 | self.alertX_shadowRadius = theme.enableShadow ? AlertX.defaultShadowRadius : 0.0 58 | 59 | self.animation = animation 60 | } 61 | 62 | public var body: some View { 63 | 64 | ZStack { 65 | 66 | VStack { 67 | 68 | alertX_title 69 | .padding(.init(top: 35, leading: 25, bottom: 15, trailing: 25)) 70 | .foregroundColor(theme.alertTextColor) 71 | .font(.headline) 72 | 73 | alertX_message 74 | .padding(.init(top: 0, leading: 25, bottom: 35, trailing: 25)) 75 | .foregroundColor(theme.alertTextColor) 76 | 77 | 78 | if buttonStack != nil { 79 | 80 | if buttonStack!.count < 3 { 81 | 82 | HStack { 83 | ForEach((0...(buttonStack?.count ?? 0)-1), id: \.self) { 84 | 85 | self.buttonStack?[$0] 86 | .background(self.buttonStack![$0].buttonType == AlertX.ButtonType.default ? self.theme.defaultButtonColor : self.theme.cancelButtonColor) 87 | .foregroundColor(self.buttonStack![$0].buttonType == AlertX.ButtonType.default ? self.theme.defaultButtonTextColor : self.theme.cancelButtonTextColor) 88 | .cornerRadius(self.theme.enableRoundedCorners ? theme.roundedCornerRadius : 0.0) 89 | 90 | } 91 | }.padding() 92 | 93 | } else { 94 | 95 | VStack { 96 | 97 | ForEach((0...(buttonStack!.count)-1), id: \.self) { 98 | 99 | self.buttonStack?[$0] 100 | .background(self.buttonStack![$0].buttonType == AlertX.ButtonType.default ? self.theme.defaultButtonColor : self.theme.cancelButtonColor) 101 | .foregroundColor(self.buttonStack![$0].buttonType == AlertX.ButtonType.default ? self.theme.defaultButtonTextColor : self.theme.cancelButtonTextColor) 102 | .cornerRadius(self.theme.enableRoundedCorners ? theme.roundedCornerRadius : 0.0) 103 | .padding(.bottom, 10) 104 | 105 | } 106 | 107 | }.padding() 108 | 109 | } 110 | 111 | } 112 | 113 | }.background(AlertX.Window(color: theme.windowColor, cornerRadius: self.theme.enableRoundedCorners ? theme.roundedCornerRadius : 0.0, transparencyEnabled: theme.enableTransparency)) 114 | .frame(minWidth: 0, maxWidth: .infinity, alignment: .center) 115 | .padding() 116 | .shadow(radius: alertX_shadowRadius) 117 | .cornerRadius(alertX_cornerRadius) 118 | 119 | } 120 | 121 | } 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /Sources/AlertX/AlertX-Elements/Button.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ButtonX.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import SwiftUI 10 | 11 | typealias SystemButton = Button 12 | extension AlertX { 13 | 14 | enum ButtonType { 15 | case `default` 16 | case cancel 17 | } 18 | public struct Button: View { 19 | 20 | let text: Text 21 | var buttonType: AlertX.ButtonType = .default 22 | 23 | var buttonAction: (() -> Void)? 24 | 25 | private init(text: Text, buttonType: AlertX.ButtonType, action: (() -> Void)? = {}) { 26 | self.text = text 27 | self.buttonType = buttonType 28 | self.buttonAction = action 29 | } 30 | 31 | public var body: some View { 32 | SystemButton(action: { 33 | 34 | func prepareForDismiss(completion: @escaping () -> Void) { 35 | 36 | if let reference = AlertX_View.currentAlertXVCReference { 37 | AlertX_View.currentAlertXVCReference = nil 38 | reference.dismiss(animated: true, completion: completion) 39 | 40 | } else { 41 | completion() 42 | } 43 | 44 | } 45 | 46 | prepareForDismiss { 47 | buttonAction?() 48 | } 49 | 50 | }, label: { 51 | text 52 | .frame(minWidth: 150, maxWidth: .infinity, alignment: .center) 53 | .padding(.init(top: 10, leading: 10, bottom: 10, trailing: 10)) 54 | .shadow(radius: 1.0) 55 | }) 56 | 57 | } 58 | 59 | // button types 60 | public static func `default`(_ label: Text, action: (() -> Void)? = {}) -> AlertX.Button { 61 | return AlertX.Button(text: label, buttonType: .default, action: action) 62 | } 63 | 64 | public static func cancel(_ label: Text, action: (() -> Void)? = {}) -> AlertX.Button { 65 | return AlertX.Button(text: label, buttonType: .cancel, action: action) 66 | } 67 | 68 | public static func cancel(_ action: (() -> Void)? = {}) -> AlertX.Button { 69 | return AlertX.Button(text: Text("Cancel"), buttonType: .cancel, action: action) 70 | } 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Sources/AlertX/AlertX-Elements/Window.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Window.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import SwiftUI 10 | 11 | extension AlertX { 12 | 13 | struct Window: View { 14 | 15 | var windowColor: Color 16 | var windowColorOpacity: Double 17 | var cornerRadius: CGFloat 18 | 19 | public init(color: Color, cornerRadius: CGFloat, transparencyEnabled: Bool) { 20 | self.windowColor = color 21 | self.windowColorOpacity = transparencyEnabled ? AlertX.defaultAlertOpacity : 1.0 22 | self.cornerRadius = cornerRadius 23 | } 24 | 25 | var body: some View { 26 | Rectangle() 27 | .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center) 28 | .foregroundColor(windowColor.opacity(windowColorOpacity)) 29 | .cornerRadius(cornerRadius) 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Sources/AlertX/Essentials/AlertXViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertXViewController.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class AlertXViewController: UIHostingController { 13 | 14 | var alertX_view: AlertX_View 15 | var isPresented: Binding 16 | 17 | init(alertX_view: AlertX_View, isPresented: Binding) { 18 | self.alertX_view = alertX_view 19 | self.isPresented = isPresented 20 | super.init(rootView: self.alertX_view) 21 | } 22 | 23 | required init?(coder: NSCoder) { 24 | fatalError("init(coder:) has not been implemented") 25 | } 26 | 27 | public override func viewWillDisappear(_ animated: Bool) { 28 | self.isPresented.wrappedValue = false 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Sources/AlertX/Essentials/AlertX_View.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertX_View.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct AlertX_View: View { 12 | 13 | static var currentAlertXVCReference: AlertXViewController? 14 | 15 | @Binding var visible: Bool 16 | @State var show: Bool = false 17 | 18 | let alertX: AlertX 19 | 20 | public var body: some View { 21 | 22 | ZStack { 23 | 24 | Rectangle() 25 | .foregroundColor(Color.black.opacity(0.25)) 26 | .edgesIgnoringSafeArea(.all) 27 | 28 | if show { 29 | alertX 30 | .transition(self.alertX.animation.transition) 31 | } 32 | 33 | }.onAppear{ 34 | Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { timer in 35 | withAnimation { 36 | if self.visible { 37 | self.show = true 38 | } 39 | } 40 | } 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Sources/AlertX/Essentials/View+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewExtension.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import SwiftUI 10 | 11 | extension View { 12 | 13 | public func alertX(isPresented: Binding, content: () -> AlertX) -> some View { 14 | 15 | let alertX_view = AlertX_View(visible: isPresented, alertX: content()) 16 | let alertXVC = AlertXViewController(alertX_view: alertX_view, isPresented: isPresented) 17 | alertXVC.modalPresentationStyle = .overCurrentContext 18 | alertXVC.view.backgroundColor = UIColor.clear 19 | alertXVC.modalTransitionStyle = .crossDissolve 20 | 21 | if isPresented.wrappedValue { 22 | if AlertX_View.currentAlertXVCReference == nil { 23 | AlertX_View.currentAlertXVCReference = alertXVC 24 | } 25 | 26 | let viewController = self.topViewController() 27 | viewController?.present(alertXVC, animated: true, completion: nil) 28 | } else { 29 | alertXVC.dismiss(animated: true, completion: nil) 30 | } 31 | 32 | return self 33 | } 34 | 35 | private func topViewController(baseVC: UIViewController? = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController) -> UIViewController? { 36 | 37 | if let nav = baseVC as? UINavigationController { 38 | return topViewController(baseVC: nav.visibleViewController) 39 | } 40 | if let tab = baseVC as? UITabBarController { 41 | if let selected = tab.selectedViewController { 42 | return topViewController(baseVC: selected) 43 | } 44 | } 45 | if let presented = baseVC?.presentedViewController { 46 | return topViewController(baseVC: presented) 47 | } 48 | return baseVC 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/AlertX/Options/Animations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Animations.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import SwiftUI 10 | 11 | extension AlertX { 12 | 13 | public struct AnimationX { 14 | 15 | let transition: AnyTransition 16 | 17 | public init() { 18 | self = AlertX.AnimationX.defaultEffect() 19 | } 20 | 21 | private init(transition: AnyTransition) { 22 | self.transition = transition 23 | } 24 | 25 | public static func custom(withTransition transition: AnyTransition) -> AlertX.AnimationX { 26 | return AlertX.AnimationX(transition: transition) 27 | } 28 | 29 | public static func defaultEffect() -> AlertX.AnimationX { 30 | let transition = AnyTransition.scale(scale: 1.2).combined(with: .opacity).animation(.easeOut(duration: 0.15)) 31 | return AlertX.AnimationX(transition: transition) 32 | } 33 | 34 | public static func classicEffect() -> AlertX.AnimationX { 35 | let spring = Animation.spring(response: 0.25, dampingFraction: 0.6, blendDuration: 0.25) 36 | let transition = AnyTransition.scale.combined(with: .opacity).animation(spring) 37 | return AlertX.AnimationX(transition: transition) 38 | } 39 | 40 | public static func zoomEffect() -> AlertX.AnimationX { 41 | let transition = AnyTransition.scale.combined(with: .opacity) 42 | return AlertX.AnimationX(transition: transition) 43 | } 44 | 45 | public static func fadeEffect() -> AlertX.AnimationX { 46 | let transition = AnyTransition.opacity 47 | return AlertX.AnimationX(transition: transition) 48 | } 49 | 50 | public static func slideUpEffect() -> AlertX.AnimationX { 51 | let transition = AnyTransition.move(edge: .bottom).combined(with: .opacity).animation(.easeOut) 52 | return AlertX.AnimationX(transition: transition) 53 | } 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/AlertX/Options/Themes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertX+Options.swift 3 | // AlertX 4 | // 5 | // Copyright © 2020 Neel Makhecha. All rights reserved. 6 | // https://github.com/neel-makhecha/AlertX 7 | // 8 | 9 | import SwiftUI 10 | 11 | extension AlertX { 12 | 13 | public struct Theme { 14 | 15 | //Default theme 16 | public static var defaultTheme: AlertX.Theme = graphite() 17 | public static let defaultCornerRadius = AlertX.defaultCornerRadius 18 | 19 | // General Properties 20 | var windowColor: Color 21 | var alertTextColor: Color 22 | var enableShadow: Bool 23 | var enableRoundedCorners: Bool 24 | var roundedCornerRadius: CGFloat 25 | var enableTransparency: Bool 26 | 27 | // Button Properties 28 | var cancelButtonColor: Color 29 | var cancelButtonTextColor: Color 30 | var defaultButtonColor: Color 31 | var defaultButtonTextColor: Color 32 | 33 | // Public init 34 | public init() { 35 | self = AlertX.Theme.defaultTheme 36 | } 37 | 38 | // Private init 39 | private init(windowColor: Color, alertTextColor: Color, enableShadow: Bool, enableRoundedCorners: Bool, enableTransparency: Bool, cancelButtonColor: Color, cancelButtonTextColor: Color, defaultButtonColor: Color, defaultButtonTextColor: Color, roundedCornerRadius: CGFloat = defaultCornerRadius) { 40 | 41 | self.windowColor = windowColor 42 | self.alertTextColor = alertTextColor 43 | self.enableShadow = enableShadow 44 | self.enableRoundedCorners = enableRoundedCorners 45 | self.roundedCornerRadius = roundedCornerRadius 46 | self.enableTransparency = enableTransparency 47 | 48 | self.cancelButtonColor = cancelButtonColor 49 | self.cancelButtonTextColor = cancelButtonTextColor 50 | self.defaultButtonColor = defaultButtonColor 51 | self.defaultButtonTextColor = defaultButtonTextColor 52 | } 53 | 54 | // Define custom theme 55 | public static func custom(windowColor: Color, alertTextColor: Color, enableShadow: Bool, enableRoundedCorners: Bool, enableTransparency: Bool, cancelButtonColor: Color, cancelButtonTextColor: Color, defaultButtonColor: Color, defaultButtonTextColor: Color, roundedCornerRadius: CGFloat = defaultCornerRadius) -> AlertX.Theme { 56 | 57 | let theme = AlertX.Theme(windowColor: windowColor, 58 | alertTextColor: alertTextColor, 59 | enableShadow: enableShadow, 60 | enableRoundedCorners: enableRoundedCorners, 61 | enableTransparency: enableTransparency, 62 | cancelButtonColor: cancelButtonColor, 63 | cancelButtonTextColor: cancelButtonTextColor, 64 | defaultButtonColor: defaultButtonColor, 65 | defaultButtonTextColor: defaultButtonTextColor, 66 | roundedCornerRadius: roundedCornerRadius) 67 | 68 | return theme 69 | } 70 | 71 | // Predefined themes 72 | 73 | public static func graphite(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 74 | let theme = AlertX.Theme(windowColor: Color(red: 94/255, green: 94/255, blue: 94/255), 75 | alertTextColor: Color.white, 76 | enableShadow: true, 77 | enableRoundedCorners: roundedCorners, 78 | enableTransparency: transparency, 79 | cancelButtonColor: Color(red: 213/255, green: 213/255, blue: 213/255), 80 | cancelButtonTextColor: Color.black, 81 | defaultButtonColor: Color(red: 0/255, green: 0/255, blue: 0/255), 82 | defaultButtonTextColor: Color.white) 83 | 84 | return theme 85 | } 86 | 87 | public static func light(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 88 | let theme = AlertX.Theme(windowColor: Color(red: 255/255, green: 255/255, blue: 255/255), 89 | alertTextColor: Color.black, 90 | enableShadow: true, 91 | enableRoundedCorners: roundedCorners, 92 | enableTransparency: transparency, 93 | cancelButtonColor: Color(red: 213/255, green: 213/255, blue: 213/255), 94 | cancelButtonTextColor: Color.black, 95 | defaultButtonColor: Color(red: 146/255, green: 146/255, blue: 146/255), 96 | defaultButtonTextColor: Color.white) 97 | 98 | return theme 99 | } 100 | 101 | public static func dark(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 102 | let theme = AlertX.Theme(windowColor: Color(red: 0/255, green: 0/255, blue: 0/255), 103 | alertTextColor: Color.white, 104 | enableShadow: true, 105 | enableRoundedCorners: roundedCorners, 106 | enableTransparency: transparency, 107 | cancelButtonColor: Color(red: 213/255, green: 213/255, blue: 213/255), 108 | cancelButtonTextColor: Color.black, 109 | defaultButtonColor: Color(red: 94/255, green: 94/255, blue: 94/255), 110 | defaultButtonTextColor: Color.white) 111 | 112 | return theme 113 | } 114 | 115 | public static func sun(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 116 | let theme = AlertX.Theme(windowColor: Color(red: 253/255, green: 184/255, blue: 51/255), 117 | alertTextColor: Color(red: 118/255, green: 82/255, blue: 14/255), 118 | enableShadow: true, 119 | enableRoundedCorners: roundedCorners, 120 | enableTransparency: transparency, 121 | cancelButtonColor: Color(red: 255/255, green: 213/255, blue: 62/255), 122 | cancelButtonTextColor: Color(red: 118/255, green: 82/255, blue: 14/255), 123 | defaultButtonColor: Color(red: 255/255, green: 247/255, blue: 94/255), 124 | defaultButtonTextColor: Color(red: 118/255, green: 82/255, blue: 14/255)) 125 | 126 | return theme 127 | } 128 | 129 | public static func cherry(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 130 | let theme = AlertX.Theme(windowColor: Color(red: 239/255, green: 35/255, blue: 60/255), 131 | alertTextColor: Color.white, 132 | enableShadow: true, 133 | enableRoundedCorners: roundedCorners, 134 | enableTransparency: transparency, 135 | cancelButtonColor: Color(red: 216/255, green: 0/255, blue: 50/255), 136 | cancelButtonTextColor: Color.white, 137 | defaultButtonColor: Color(red: 255/255, green: 150/255, blue: 141/255), 138 | defaultButtonTextColor: Color.white) 139 | 140 | return theme 141 | } 142 | 143 | public static func mint(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 144 | let theme = AlertX.Theme(windowColor: Color(red: 111/255, green: 255/255, blue: 233/255), 145 | alertTextColor: Color(red: 11/255, green: 19/255, blue: 43/255), 146 | enableShadow: true, 147 | enableRoundedCorners: roundedCorners, 148 | enableTransparency: transparency, 149 | cancelButtonColor: Color(red: 91/255, green: 192/255, blue: 190/255), 150 | cancelButtonTextColor: Color.white, 151 | defaultButtonColor: Color(red: 11/255, green: 19/255, blue: 43/255), 152 | defaultButtonTextColor: Color.white) 153 | 154 | return theme 155 | } 156 | 157 | public static func wine(withTransparency transparency: Bool = true, roundedCorners: Bool = false) -> AlertX.Theme { 158 | let theme = AlertX.Theme(windowColor: Color(red: 100/255, green: 18/255, blue: 32/255), 159 | alertTextColor: Color.white, 160 | enableShadow: true, 161 | enableRoundedCorners: roundedCorners, 162 | enableTransparency: transparency, 163 | cancelButtonColor: Color(red: 178/255, green: 30/255, blue: 53/255), 164 | cancelButtonTextColor: Color.white, 165 | defaultButtonColor: Color(red: 224/255, green: 30/255, blue: 55/255), 166 | defaultButtonTextColor: Color.white) 167 | 168 | return theme 169 | } 170 | 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /Tests/AlertXTests/AlertXTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import AlertX 3 | 4 | final class AlertXTests: XCTestCase { 5 | func testExample() { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(AlertX().text, "Hello, World!") 10 | } 11 | 12 | static var allTests = [ 13 | ("testExample", testExample), 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Tests/AlertXTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(AlertXTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import AlertXTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += AlertXTests.allTests() 7 | XCTMain(tests) 8 | --------------------------------------------------------------------------------