├── .swift-version
├── toast_swift_screenshot.jpg
├── Example
├── Assets.xcassets
│ ├── Contents.json
│ ├── toast.imageset
│ │ ├── toast.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ └── LaunchScreen.storyboard
├── AppDelegate.swift
├── LaunchScreen.storyboard
└── ViewController.swift
├── Toast-Swift.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── Toast-Swift.xcscmblueprint
├── xcshareddata
│ └── xcschemes
│ │ ├── ToastSwiftFramework.xcscheme
│ │ └── Toast-Swift.xcscheme
└── project.pbxproj
├── Toast
├── Resources
│ └── PrivacyInfo.xcprivacy
└── Toast.swift
├── .gitignore
├── Package.swift
├── Toast-Swift-Framework
├── Toast-Swift-Framework.h
└── Info.plist
├── Toast-Swift.podspec
├── LICENSE
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/toast_swift_screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scalessec/Toast-Swift/HEAD/toast_swift_screenshot.jpg
--------------------------------------------------------------------------------
/Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/toast.imageset/toast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scalessec/Toast-Swift/HEAD/Example/Assets.xcassets/toast.imageset/toast.png
--------------------------------------------------------------------------------
/Toast-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Toast-Swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/toast.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "toast.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Toast/Resources/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyTracking
6 |
7 | NSPrivacyCollectedDataTypes
8 |
9 | NSPrivacyTrackingDomains
10 |
11 | NSPrivacyAccessedAPITypes
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Mac OS X
2 | .DS_Store
3 |
4 | # Xcode
5 |
6 | ## Build generated
7 | build/
8 | DerivedData
9 |
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata
20 |
21 | ## Other
22 | *.xccheckout
23 | *.moved-aside
24 | *.xcuserstate
25 | *.xcscmblueprint
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | .build/
37 |
38 | # Carthage
39 | Carthage/Build
40 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "Toast",
6 | platforms: [
7 | .iOS(.v12),
8 | ],
9 | products: [
10 | .library(
11 | name: "Toast",
12 | targets: ["Toast"]),
13 | ],
14 | dependencies: [],
15 | targets: [
16 | .target(
17 | name: "Toast",
18 | dependencies: [],
19 | path: "Toast",
20 | resources: [
21 | .copy("Resources/PrivacyInfo.xcprivacy")
22 | ])
23 | ],
24 | swiftLanguageVersions: [.v5]
25 | )
--------------------------------------------------------------------------------
/Toast-Swift-Framework/Toast-Swift-Framework.h:
--------------------------------------------------------------------------------
1 | //
2 | // Toast-Swift-Framework.h
3 | // Toast-Swift-Framework
4 | //
5 | // Created by Sandro Machado on 14/04/16.
6 | // Copyright © 2016 Charles Scalesse. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Toast-Swift-Framework.
12 | FOUNDATION_EXPORT double Toast_Swift_FrameworkVersionNumber;
13 |
14 | //! Project version string for Toast-Swift-Framework.
15 | FOUNDATION_EXPORT const unsigned char Toast_Swift_FrameworkVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Toast-Swift.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Toast-Swift"
3 | s.version = "5.1.1"
4 | s.summary = "A Swift extension that adds toast notifications to the UIView object class."
5 | s.homepage = "https://github.com/scalessec/Toast-Swift"
6 | s.license = 'MIT'
7 | s.author = { "Charles Scalesse" => "scalessec@gmail.com" }
8 | s.source = { :git => "https://github.com/scalessec/Toast-Swift.git", :tag => "5.1.1" }
9 | s.platform = :ios
10 | s.source_files = 'Toast/*.swift'
11 | s.resource_bundles = {'Toast-Swift': 'Toast/Resources/PrivacyInfo.xcprivacy'}
12 | s.framework = 'QuartzCore'
13 | s.requires_arc = true
14 | s.ios.deployment_target = '12.0'
15 | s.swift_version = '5.0'
16 | end
17 |
--------------------------------------------------------------------------------
/Toast-Swift-Framework/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015-2024 Charles Scalesse.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a
4 | copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Toast-Swift.xcodeproj/project.xcworkspace/xcshareddata/Toast-Swift.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "1AE4A4A0564E38992EFB0E8F9258432F89681A15",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "1AE4A4A0564E38992EFB0E8F9258432F89681A15" : 0,
8 | "775E195363DA268E1E1924859088052B9B8BE981" : 0
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "332B5743-ED98-44EA-BC62-A34924B1B90B",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "1AE4A4A0564E38992EFB0E8F9258432F89681A15" : "Toast-Swift\/",
13 | "775E195363DA268E1E1924859088052B9B8BE981" : "Toast\/"
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Toast-Swift",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Toast-Swift.xcodeproj",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:scalessec\/Toast-Swift.git",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1AE4A4A0564E38992EFB0E8F9258432F89681A15"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/scalessec\/Toast.git",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "775E195363DA268E1E1924859088052B9B8BE981"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/Example/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 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Toast-Swift.xcodeproj/xcshareddata/xcschemes/ToastSwiftFramework.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Toast-Swift
4 | //
5 | // Copyright (c) 2015-2024 Charles Scalesse.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a
8 | // copy of this software and associated documentation files (the
9 | // "Software"), to deal in the Software without restriction, including
10 | // without limitation the rights to use, copy, modify, merge, publish,
11 | // distribute, sublicense, and/or sell copies of the Software, and to
12 | // permit persons to whom the Software is furnished to do so, subject to
13 | // the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included
16 | // in all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 |
26 | import UIKit
27 |
28 | @UIApplicationMain
29 | class AppDelegate: UIResponder, UIApplicationDelegate {
30 |
31 | lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
32 |
33 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
34 | let viewController = ViewController(style: .plain)
35 | let navigationController = UINavigationController(rootViewController: viewController)
36 | self.window?.rootViewController = navigationController
37 | self.window?.makeKeyAndVisible()
38 |
39 | configureAppearance()
40 |
41 | return true
42 | }
43 |
44 | private func configureAppearance() {
45 | let navigationBarColor: UIColor = .lightBlue
46 | let titleTextAttributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.foregroundColor: UIColor.white]
47 |
48 | let appearance = UINavigationBar.appearance()
49 | if #available(iOS 15, *) {
50 | let barAppearance = UINavigationBarAppearance()
51 | barAppearance.backgroundColor = navigationBarColor
52 | barAppearance.titleTextAttributes = titleTextAttributes
53 | appearance.standardAppearance = barAppearance
54 | appearance.scrollEdgeAppearance = barAppearance
55 | } else {
56 | appearance.barTintColor = navigationBarColor
57 | appearance.titleTextAttributes = titleTextAttributes
58 | }
59 | }
60 | }
61 |
62 | // MARK: - Theming
63 |
64 | extension UIColor {
65 |
66 | static var lightBlue: UIColor {
67 | return UIColor(red: 76.0 / 255.0, green: 152.0 / 255.0, blue: 213.0 / 255.0, alpha: 1.0)
68 | }
69 |
70 | static var darkBlue: UIColor {
71 | return UIColor(red: 62.0 / 255.0, green: 128.0 / 255.0, blue: 180.0 / 255.0, alpha: 1.0)
72 | }
73 |
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/Example/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Toast-Swift.xcodeproj/xcshareddata/xcschemes/Toast-Swift.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Toast-Swift
2 | =============
3 |
4 | [](http://cocoadocs.org/docsets/Toast-Swift)
5 | [](https://github.com/Carthage/Carthage)
6 |
7 | Toast-Swift is a Swift extension that adds toast notifications to the `UIView` object class. It is intended to be simple, lightweight, and easy to use. Most toast notifications can be triggered with a single line of code.
8 |
9 | **Toast-Swift is a native Swift port of [Toast for iOS](https://github.com/scalessec/Toast "Toast for iOS").**
10 |
11 | Screenshots
12 | ---------
13 | 
14 |
15 |
16 | Basic Examples
17 | ---------
18 | ```swift
19 | // basic usage
20 | self.view.makeToast("This is a piece of toast")
21 |
22 | // toast with a specific duration and position
23 | self.view.makeToast("This is a piece of toast", duration: 3.0, position: .top)
24 |
25 | // toast presented with multiple options and with a completion closure
26 | self.view.makeToast("This is a piece of toast", duration: 2.0, point: CGPoint(x: 110.0, y: 110.0), title: "Toast Title", image: UIImage(named: "toast.png")) { didTap in
27 | if didTap {
28 | print("completion from tap")
29 | } else {
30 | print("completion without tap")
31 | }
32 | }
33 |
34 | // display toast with an activity spinner
35 | self.view.makeToastActivity(.center)
36 |
37 | // display any view as toast
38 | self.view.showToast(myView)
39 |
40 | // immediately hides all toast views in self.view
41 | self.view.hideAllToasts()
42 | ```
43 |
44 | But wait, there's more!
45 | ---------
46 | ```swift
47 | // create a new style
48 | var style = ToastStyle()
49 |
50 | // this is just one of many style options
51 | style.messageColor = .blue
52 |
53 | // present the toast with the new style
54 | self.view.makeToast("This is a piece of toast", duration: 3.0, position: .bottom, style: style)
55 |
56 | // or perhaps you want to use this style for all toasts going forward?
57 | // just set the shared style and there's no need to provide the style again
58 | ToastManager.shared.style = style
59 | self.view.makeToast("This is a piece of toast") // now uses the shared style
60 |
61 | // toggle "tap to dismiss" functionality
62 | ToastManager.shared.isTapToDismissEnabled = true
63 |
64 | // toggle queueing behavior
65 | ToastManager.shared.isQueueEnabled = true
66 | ```
67 |
68 | See the demo project for more examples.
69 |
70 |
71 | Setup Instructions
72 | ------------------
73 |
74 | [CocoaPods](http://cocoapods.org)
75 | ------------------
76 |
77 | To integrate Toast-Swift into your Xcode project using CocoaPods, specify it in your `Podfile`:
78 |
79 | ```ruby
80 | pod 'Toast-Swift', '~> 5.1.1'
81 | ```
82 |
83 | and in your code add `import Toast_Swift`.
84 |
85 | [Carthage](https://github.com/Carthage/Carthage)
86 | ------------------
87 |
88 | To integrate Toast-Swift into your Xcode project using Carthage, specify it in your `Cartfile`:
89 |
90 | ```ogdl
91 | github "scalessec/Toast-Swift" ~> 5.1.1
92 | ```
93 |
94 | Run `carthage update` to build the framework and drag the built `ToastSwiftFramework.framework` into your Xcode project.
95 |
96 | and in your code add `import ToastSwiftFramework`.
97 |
98 | [Swift Package Manager](https://swift.org/package-manager/)
99 | ------------------
100 |
101 | When using Xcode 11 or later, you can install `Toast` by going to your Project settings > `Swift Packages` and add the repository by providing the GitHub URL. Alternatively, you can go to `File` > `Swift Packages` > `Add Package Dependencies...`
102 |
103 | Manually
104 | ------------------
105 |
106 | 1. Add `Toast.swift` to your project.
107 | 2. Grab yourself a cold 🍺.
108 |
109 | Compatibility
110 | ------------------
111 | * Version `5.x.x` requires Swift 5 and Xcode 10.2 or later.
112 | * Version `4.x.x` requires Swift 4.2 and Xcode 10.
113 | * Version `3.x.x` requires Swift 4 and Xcode 9.
114 | * Version `2.x.x` requires Swift 3 and Xcode 8.
115 | * Version `1.4.x` requires Swift 2.2 and Xcode 7.3.
116 | * Version `1.0.0` can be used with Swift 2.1 and earlier versions of Xcode.
117 |
118 | Privacy
119 | -----------
120 | Toast-Swift does not collect any data. A [privacy manifest](Toast/Resources/PrivacyInfo.xcprivacy) is provided with the library. See [Apple's documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files) for related details.
121 |
122 | MIT License
123 | -----------
124 | Copyright (c) 2015-2024 Charles Scalesse.
125 |
126 | Permission is hereby granted, free of charge, to any person obtaining a
127 | copy of this software and associated documentation files (the
128 | "Software"), to deal in the Software without restriction, including
129 | without limitation the rights to use, copy, modify, merge, publish,
130 | distribute, sublicense, and/or sell copies of the Software, and to
131 | permit persons to whom the Software is furnished to do so, subject to
132 | the following conditions:
133 |
134 | The above copyright notice and this permission notice shall be included
135 | in all copies or substantial portions of the Software.
136 |
137 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
138 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
139 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
140 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
141 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
142 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
143 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
144 |
--------------------------------------------------------------------------------
/Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Toast-Swift
4 | //
5 | // Copyright (c) 2015-2024 Charles Scalesse.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a
8 | // copy of this software and associated documentation files (the
9 | // "Software"), to deal in the Software without restriction, including
10 | // without limitation the rights to use, copy, modify, merge, publish,
11 | // distribute, sublicense, and/or sell copies of the Software, and to
12 | // permit persons to whom the Software is furnished to do so, subject to
13 | // the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included
16 | // in all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 |
26 | import UIKit
27 |
28 | class ViewController: UITableViewController {
29 |
30 | fileprivate var showingActivity = false
31 |
32 | fileprivate struct ReuseIdentifiers {
33 | static let switchCellId = "switchCell"
34 | static let exampleCellId = "exampleCell"
35 | }
36 |
37 | // MARK: - Constructors
38 |
39 | override init(style: UITableView.Style) {
40 | super.init(style: style)
41 | self.title = "Toast-Swift"
42 | }
43 |
44 | required init?(coder aDecoder: NSCoder) {
45 | fatalError("not used")
46 | }
47 |
48 | // MARK: - View Lifecycle
49 |
50 | override func viewDidLoad() {
51 | super.viewDidLoad()
52 | self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: ReuseIdentifiers.exampleCellId)
53 | }
54 |
55 | // MARK: - Events
56 |
57 | @objc
58 | private func handleTapToDismissToggled() {
59 | ToastManager.shared.isTapToDismissEnabled = !ToastManager.shared.isTapToDismissEnabled
60 | }
61 |
62 | @objc
63 | private func handleQueueToggled() {
64 | ToastManager.shared.isQueueEnabled = !ToastManager.shared.isQueueEnabled
65 | }
66 | }
67 |
68 | // MARK: - UITableViewDelegate & DataSource Methods
69 |
70 | extension ViewController {
71 |
72 | override func numberOfSections(in tableView: UITableView) -> Int {
73 | return 2
74 | }
75 |
76 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
77 | if section == 0 {
78 | return 2
79 | } else {
80 | return 11
81 | }
82 | }
83 |
84 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
85 | return 60.0
86 | }
87 |
88 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
89 | return 40.0
90 | }
91 |
92 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
93 | if section == 0 {
94 | return "SETTINGS"
95 | } else {
96 | return "EXAMPLES"
97 | }
98 | }
99 |
100 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
101 | if indexPath.section == 0 {
102 |
103 | var cell = tableView.dequeueReusableCell(withIdentifier: ReuseIdentifiers.switchCellId)
104 |
105 | if indexPath.row == 0 {
106 | if cell == nil {
107 | cell = UITableViewCell(style: .default, reuseIdentifier: ReuseIdentifiers.switchCellId)
108 | let tapToDismissSwitch = UISwitch()
109 | tapToDismissSwitch.onTintColor = .darkBlue
110 | tapToDismissSwitch.isOn = ToastManager.shared.isTapToDismissEnabled
111 | tapToDismissSwitch.addTarget(self, action: #selector(ViewController.handleTapToDismissToggled), for: .valueChanged)
112 | cell?.accessoryView = tapToDismissSwitch
113 | cell?.selectionStyle = .none
114 | cell?.textLabel?.font = UIFont.systemFont(ofSize: 16.0)
115 | }
116 | cell?.textLabel?.text = "Tap to dismiss"
117 | } else {
118 | if cell == nil {
119 | cell = UITableViewCell(style: .default, reuseIdentifier: ReuseIdentifiers.switchCellId)
120 | let queueSwitch = UISwitch()
121 | queueSwitch.onTintColor = .darkBlue
122 | queueSwitch.isOn = ToastManager.shared.isQueueEnabled
123 | queueSwitch.addTarget(self, action: #selector(ViewController.handleQueueToggled), for: .valueChanged)
124 | cell?.accessoryView = queueSwitch
125 | cell?.selectionStyle = .none
126 | cell?.textLabel?.font = UIFont.systemFont(ofSize: 16.0)
127 | }
128 | cell?.textLabel?.text = "Queue toast"
129 | }
130 |
131 | return cell!
132 |
133 | } else {
134 |
135 | let cell = tableView.dequeueReusableCell(withIdentifier: ReuseIdentifiers.exampleCellId, for: indexPath)
136 | cell.textLabel?.numberOfLines = 2
137 | cell.textLabel?.font = UIFont.systemFont(ofSize: 16.0)
138 | cell.accessoryType = .disclosureIndicator
139 |
140 | switch indexPath.row {
141 | case 0: cell.textLabel?.text = "Make toast"
142 | case 1: cell.textLabel?.text = "Make toast on top for 3 seconds"
143 | case 2: cell.textLabel?.text = "Make toast with a title"
144 | case 3: cell.textLabel?.text = "Make toast with an image"
145 | case 4: cell.textLabel?.text = "Make toast with a title, image, and completion closure"
146 | case 5: cell.textLabel?.text = "Make toast with a custom style"
147 | case 6: cell.textLabel?.text = "Show a custom view as toast"
148 | case 7: cell.textLabel?.text = "Show an image as toast at point\n(110, 110)"
149 | case 8: cell.textLabel?.text = showingActivity ? "Hide toast activity" : "Show toast activity"
150 | case 9: cell.textLabel?.text = "Hide toast"
151 | case 10: cell.textLabel?.text = "Hide all toasts"
152 | default: cell.textLabel?.text = nil
153 | }
154 |
155 | return cell
156 |
157 | }
158 | }
159 |
160 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
161 | guard indexPath.section > 0 else { return }
162 |
163 | tableView.deselectRow(at: indexPath, animated: true)
164 |
165 | switch indexPath.row {
166 | case 0:
167 | // Make Toast
168 | self.navigationController?.view.makeToast("This is a piece of toast")
169 | case 1:
170 | // Make toast with a duration and position
171 | self.navigationController?.view.makeToast("This is a piece of toast on top for 3 seconds", duration: 3.0, position: .top)
172 | case 2:
173 | // Make toast with a title
174 | self.navigationController?.view.makeToast("This is a piece of toast with a title", duration: 2.0, position: .top, title: "Toast Title", image: nil)
175 | case 3:
176 | // Make toast with an image
177 | self.navigationController?.view.makeToast("This is a piece of toast with an image", duration: 2.0, position: .center, title: nil, image: UIImage(named: "toast.png"))
178 | case 4:
179 | // Make toast with an image, title, and completion closure
180 | self.navigationController?.view.makeToast("This is a piece of toast with a title, image, and completion closure", duration: 2.0, position: .bottom, title: "Toast Title", image: UIImage(named: "toast.png")) { didTap in
181 | if didTap {
182 | print("completion from tap")
183 | } else {
184 | print("completion without tap")
185 | }
186 | }
187 | case 5:
188 | // Make toast with a custom style
189 | var style = ToastStyle()
190 | style.messageFont = UIFont(name: "Zapfino", size: 14.0)!
191 | style.messageColor = UIColor.red
192 | style.messageAlignment = .center
193 | style.backgroundColor = UIColor.yellow
194 | self.navigationController?.view.makeToast("This is a piece of toast with a custom style", duration: 3.0, position: .bottom, style: style)
195 | case 6:
196 | // Show a custom view as toast
197 | let customView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 80.0, height: 400.0))
198 | customView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
199 | customView.backgroundColor = .lightBlue
200 | self.navigationController?.view.showToast(customView, duration: 2.0, position: .center)
201 | case 7:
202 | // Show an image view as toast, on center at point (110,110)
203 | let toastView = UIImageView(image: UIImage(named: "toast.png"))
204 | self.navigationController?.view.showToast(toastView, duration: 2.0, point: CGPoint(x: 110.0, y: 110.0))
205 | case 8:
206 | // Make toast activity
207 | if !showingActivity {
208 | self.navigationController?.view.makeToastActivity(.center)
209 | } else {
210 | self.navigationController?.view.hideToastActivity()
211 | }
212 |
213 | showingActivity.toggle()
214 |
215 | tableView.reloadData()
216 | case 9:
217 | // Hide toast
218 | self.navigationController?.view.hideToast()
219 | case 10:
220 | // Hide all toasts
221 | self.navigationController?.view.hideAllToasts()
222 | default:
223 | break
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/Toast-Swift.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 151796EB1BE68E4B004BA6E5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 151796EA1BE68E4B004BA6E5 /* AppDelegate.swift */; };
11 | 151796ED1BE68E4B004BA6E5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 151796EC1BE68E4B004BA6E5 /* ViewController.swift */; };
12 | 151796F21BE68E4B004BA6E5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 151796F11BE68E4B004BA6E5 /* Assets.xcassets */; };
13 | 151797001BE69026004BA6E5 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 151796FF1BE69026004BA6E5 /* Toast.swift */; };
14 | 15FC18381FC0877300C57AFA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 15FC18371FC0877300C57AFA /* LaunchScreen.storyboard */; };
15 | 2A3743511CBFAF4B006A3BAB /* Toast-Swift-Framework.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A3743501CBFAF4B006A3BAB /* Toast-Swift-Framework.h */; settings = {ATTRIBUTES = (Public, ); }; };
16 | 2A3743561CBFAF94006A3BAB /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 151796FF1BE69026004BA6E5 /* Toast.swift */; };
17 | 56E0D0E72B4A084F00926644 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 56E0D0E62B4A084F00926644 /* PrivacyInfo.xcprivacy */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 151796E71BE68E4B004BA6E5 /* Toast-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Toast-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; };
22 | 151796EA1BE68E4B004BA6E5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Example/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
23 | 151796EC1BE68E4B004BA6E5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = Example/ViewController.swift; sourceTree = SOURCE_ROOT; };
24 | 151796F11BE68E4B004BA6E5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Example/Assets.xcassets; sourceTree = SOURCE_ROOT; };
25 | 151796F61BE68E4B004BA6E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Example/Info.plist; sourceTree = ""; };
26 | 151796FF1BE69026004BA6E5 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = Toast/Toast.swift; sourceTree = SOURCE_ROOT; };
27 | 15FC18371FC0877300C57AFA /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Example/LaunchScreen.storyboard; sourceTree = ""; };
28 | 2A37434E1CBFAF4B006A3BAB /* ToastSwiftFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ToastSwiftFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 2A3743501CBFAF4B006A3BAB /* Toast-Swift-Framework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Toast-Swift-Framework.h"; sourceTree = ""; };
30 | 2A3743521CBFAF4C006A3BAB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | 56E0D0E62B4A084F00926644 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Toast/Resources/PrivacyInfo.xcprivacy; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 151796E41BE68E4B004BA6E5 /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | 2A37434A1CBFAF4B006A3BAB /* Frameworks */ = {
43 | isa = PBXFrameworksBuildPhase;
44 | buildActionMask = 2147483647;
45 | files = (
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | /* End PBXFrameworksBuildPhase section */
50 |
51 | /* Begin PBXGroup section */
52 | 151796DE1BE68E4B004BA6E5 = {
53 | isa = PBXGroup;
54 | children = (
55 | 151796E91BE68E4B004BA6E5 /* Toast */,
56 | 151796FC1BE68E75004BA6E5 /* Example */,
57 | 2A37434F1CBFAF4B006A3BAB /* Toast-Swift-Framework */,
58 | 151796E81BE68E4B004BA6E5 /* Products */,
59 | );
60 | sourceTree = "";
61 | };
62 | 151796E81BE68E4B004BA6E5 /* Products */ = {
63 | isa = PBXGroup;
64 | children = (
65 | 151796E71BE68E4B004BA6E5 /* Toast-Swift.app */,
66 | 2A37434E1CBFAF4B006A3BAB /* ToastSwiftFramework.framework */,
67 | );
68 | name = Products;
69 | sourceTree = "";
70 | };
71 | 151796E91BE68E4B004BA6E5 /* Toast */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 151796FF1BE69026004BA6E5 /* Toast.swift */,
75 | 56E0D0E52B4A083500926644 /* Resources */,
76 | );
77 | name = Toast;
78 | sourceTree = "";
79 | };
80 | 151796FC1BE68E75004BA6E5 /* Example */ = {
81 | isa = PBXGroup;
82 | children = (
83 | 151796EA1BE68E4B004BA6E5 /* AppDelegate.swift */,
84 | 151796EC1BE68E4B004BA6E5 /* ViewController.swift */,
85 | 151796FD1BE68EAC004BA6E5 /* Supporting Files */,
86 | 151796FE1BE68EB5004BA6E5 /* Resources */,
87 | );
88 | name = Example;
89 | sourceTree = "";
90 | };
91 | 151796FD1BE68EAC004BA6E5 /* Supporting Files */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 151796F61BE68E4B004BA6E5 /* Info.plist */,
95 | );
96 | name = "Supporting Files";
97 | sourceTree = "";
98 | };
99 | 151796FE1BE68EB5004BA6E5 /* Resources */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 151796F11BE68E4B004BA6E5 /* Assets.xcassets */,
103 | 15FC18371FC0877300C57AFA /* LaunchScreen.storyboard */,
104 | );
105 | name = Resources;
106 | sourceTree = "";
107 | };
108 | 2A37434F1CBFAF4B006A3BAB /* Toast-Swift-Framework */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 2A3743501CBFAF4B006A3BAB /* Toast-Swift-Framework.h */,
112 | 2A3743521CBFAF4C006A3BAB /* Info.plist */,
113 | );
114 | path = "Toast-Swift-Framework";
115 | sourceTree = "";
116 | };
117 | 56E0D0E52B4A083500926644 /* Resources */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 56E0D0E62B4A084F00926644 /* PrivacyInfo.xcprivacy */,
121 | );
122 | name = Resources;
123 | sourceTree = "";
124 | };
125 | /* End PBXGroup section */
126 |
127 | /* Begin PBXHeadersBuildPhase section */
128 | 2A37434B1CBFAF4B006A3BAB /* Headers */ = {
129 | isa = PBXHeadersBuildPhase;
130 | buildActionMask = 2147483647;
131 | files = (
132 | 2A3743511CBFAF4B006A3BAB /* Toast-Swift-Framework.h in Headers */,
133 | );
134 | runOnlyForDeploymentPostprocessing = 0;
135 | };
136 | /* End PBXHeadersBuildPhase section */
137 |
138 | /* Begin PBXNativeTarget section */
139 | 151796E61BE68E4B004BA6E5 /* Toast-Swift */ = {
140 | isa = PBXNativeTarget;
141 | buildConfigurationList = 151796F91BE68E4B004BA6E5 /* Build configuration list for PBXNativeTarget "Toast-Swift" */;
142 | buildPhases = (
143 | 151796E31BE68E4B004BA6E5 /* Sources */,
144 | 151796E41BE68E4B004BA6E5 /* Frameworks */,
145 | 151796E51BE68E4B004BA6E5 /* Resources */,
146 | );
147 | buildRules = (
148 | );
149 | dependencies = (
150 | );
151 | name = "Toast-Swift";
152 | productName = "Toast-Swift";
153 | productReference = 151796E71BE68E4B004BA6E5 /* Toast-Swift.app */;
154 | productType = "com.apple.product-type.application";
155 | };
156 | 2A37434D1CBFAF4B006A3BAB /* ToastSwiftFramework */ = {
157 | isa = PBXNativeTarget;
158 | buildConfigurationList = 2A3743551CBFAF4C006A3BAB /* Build configuration list for PBXNativeTarget "ToastSwiftFramework" */;
159 | buildPhases = (
160 | 2A3743491CBFAF4B006A3BAB /* Sources */,
161 | 2A37434A1CBFAF4B006A3BAB /* Frameworks */,
162 | 2A37434B1CBFAF4B006A3BAB /* Headers */,
163 | 2A37434C1CBFAF4B006A3BAB /* Resources */,
164 | );
165 | buildRules = (
166 | );
167 | dependencies = (
168 | );
169 | name = ToastSwiftFramework;
170 | productName = "Toast-Swift-Framework";
171 | productReference = 2A37434E1CBFAF4B006A3BAB /* ToastSwiftFramework.framework */;
172 | productType = "com.apple.product-type.framework";
173 | };
174 | /* End PBXNativeTarget section */
175 |
176 | /* Begin PBXProject section */
177 | 151796DF1BE68E4B004BA6E5 /* Project object */ = {
178 | isa = PBXProject;
179 | attributes = {
180 | BuildIndependentTargetsInParallel = YES;
181 | LastSwiftUpdateCheck = 0710;
182 | LastUpgradeCheck = 1500;
183 | ORGANIZATIONNAME = "Charles Scalesse";
184 | TargetAttributes = {
185 | 151796E61BE68E4B004BA6E5 = {
186 | CreatedOnToolsVersion = 7.1;
187 | LastSwiftMigration = 1020;
188 | ProvisioningStyle = Automatic;
189 | };
190 | 2A37434D1CBFAF4B006A3BAB = {
191 | CreatedOnToolsVersion = 7.3;
192 | };
193 | };
194 | };
195 | buildConfigurationList = 151796E21BE68E4B004BA6E5 /* Build configuration list for PBXProject "Toast-Swift" */;
196 | compatibilityVersion = "Xcode 3.2";
197 | developmentRegion = en;
198 | hasScannedForEncodings = 0;
199 | knownRegions = (
200 | en,
201 | Base,
202 | );
203 | mainGroup = 151796DE1BE68E4B004BA6E5;
204 | productRefGroup = 151796E81BE68E4B004BA6E5 /* Products */;
205 | projectDirPath = "";
206 | projectRoot = "";
207 | targets = (
208 | 151796E61BE68E4B004BA6E5 /* Toast-Swift */,
209 | 2A37434D1CBFAF4B006A3BAB /* ToastSwiftFramework */,
210 | );
211 | };
212 | /* End PBXProject section */
213 |
214 | /* Begin PBXResourcesBuildPhase section */
215 | 151796E51BE68E4B004BA6E5 /* Resources */ = {
216 | isa = PBXResourcesBuildPhase;
217 | buildActionMask = 2147483647;
218 | files = (
219 | 151796F21BE68E4B004BA6E5 /* Assets.xcassets in Resources */,
220 | 15FC18381FC0877300C57AFA /* LaunchScreen.storyboard in Resources */,
221 | );
222 | runOnlyForDeploymentPostprocessing = 0;
223 | };
224 | 2A37434C1CBFAF4B006A3BAB /* Resources */ = {
225 | isa = PBXResourcesBuildPhase;
226 | buildActionMask = 2147483647;
227 | files = (
228 | 56E0D0E72B4A084F00926644 /* PrivacyInfo.xcprivacy in Resources */,
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | };
232 | /* End PBXResourcesBuildPhase section */
233 |
234 | /* Begin PBXSourcesBuildPhase section */
235 | 151796E31BE68E4B004BA6E5 /* Sources */ = {
236 | isa = PBXSourcesBuildPhase;
237 | buildActionMask = 2147483647;
238 | files = (
239 | 151796ED1BE68E4B004BA6E5 /* ViewController.swift in Sources */,
240 | 151797001BE69026004BA6E5 /* Toast.swift in Sources */,
241 | 151796EB1BE68E4B004BA6E5 /* AppDelegate.swift in Sources */,
242 | );
243 | runOnlyForDeploymentPostprocessing = 0;
244 | };
245 | 2A3743491CBFAF4B006A3BAB /* Sources */ = {
246 | isa = PBXSourcesBuildPhase;
247 | buildActionMask = 2147483647;
248 | files = (
249 | 2A3743561CBFAF94006A3BAB /* Toast.swift in Sources */,
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | /* End PBXSourcesBuildPhase section */
254 |
255 | /* Begin XCBuildConfiguration section */
256 | 151796F71BE68E4B004BA6E5 /* Debug */ = {
257 | isa = XCBuildConfiguration;
258 | buildSettings = {
259 | ALWAYS_SEARCH_USER_PATHS = NO;
260 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
262 | CLANG_CXX_LIBRARY = "libc++";
263 | CLANG_ENABLE_MODULES = YES;
264 | CLANG_ENABLE_OBJC_ARC = YES;
265 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
266 | CLANG_WARN_BOOL_CONVERSION = YES;
267 | CLANG_WARN_COMMA = YES;
268 | CLANG_WARN_CONSTANT_CONVERSION = YES;
269 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
271 | CLANG_WARN_EMPTY_BODY = YES;
272 | CLANG_WARN_ENUM_CONVERSION = YES;
273 | CLANG_WARN_INFINITE_RECURSION = YES;
274 | CLANG_WARN_INT_CONVERSION = YES;
275 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
276 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
277 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
279 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
281 | CLANG_WARN_STRICT_PROTOTYPES = YES;
282 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
283 | CLANG_WARN_UNREACHABLE_CODE = YES;
284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
285 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
286 | COPY_PHASE_STRIP = NO;
287 | CURRENT_PROJECT_VERSION = 5.1.0;
288 | DEBUG_INFORMATION_FORMAT = dwarf;
289 | ENABLE_STRICT_OBJC_MSGSEND = YES;
290 | ENABLE_TESTABILITY = YES;
291 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
292 | GCC_C_LANGUAGE_STANDARD = gnu99;
293 | GCC_DYNAMIC_NO_PIC = NO;
294 | GCC_NO_COMMON_BLOCKS = YES;
295 | GCC_OPTIMIZATION_LEVEL = 0;
296 | GCC_PREPROCESSOR_DEFINITIONS = (
297 | "DEBUG=1",
298 | "$(inherited)",
299 | );
300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
302 | GCC_WARN_UNDECLARED_SELECTOR = YES;
303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
304 | GCC_WARN_UNUSED_FUNCTION = YES;
305 | GCC_WARN_UNUSED_VARIABLE = YES;
306 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
307 | MARKETING_VERSION = 5.1.0;
308 | MTL_ENABLE_DEBUG_INFO = YES;
309 | ONLY_ACTIVE_ARCH = YES;
310 | SDKROOT = iphoneos;
311 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
312 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
313 | SWIFT_VERSION = 5.0;
314 | };
315 | name = Debug;
316 | };
317 | 151796F81BE68E4B004BA6E5 /* Release */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ALWAYS_SEARCH_USER_PATHS = NO;
321 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
323 | CLANG_CXX_LIBRARY = "libc++";
324 | CLANG_ENABLE_MODULES = YES;
325 | CLANG_ENABLE_OBJC_ARC = YES;
326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
327 | CLANG_WARN_BOOL_CONVERSION = YES;
328 | CLANG_WARN_COMMA = YES;
329 | CLANG_WARN_CONSTANT_CONVERSION = YES;
330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
332 | CLANG_WARN_EMPTY_BODY = YES;
333 | CLANG_WARN_ENUM_CONVERSION = YES;
334 | CLANG_WARN_INFINITE_RECURSION = YES;
335 | CLANG_WARN_INT_CONVERSION = YES;
336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
340 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
342 | CLANG_WARN_STRICT_PROTOTYPES = YES;
343 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
344 | CLANG_WARN_UNREACHABLE_CODE = YES;
345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
347 | COPY_PHASE_STRIP = NO;
348 | CURRENT_PROJECT_VERSION = 5.1.0;
349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
350 | ENABLE_NS_ASSERTIONS = NO;
351 | ENABLE_STRICT_OBJC_MSGSEND = YES;
352 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu99;
354 | GCC_NO_COMMON_BLOCKS = YES;
355 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
356 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
357 | GCC_WARN_UNDECLARED_SELECTOR = YES;
358 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
359 | GCC_WARN_UNUSED_FUNCTION = YES;
360 | GCC_WARN_UNUSED_VARIABLE = YES;
361 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
362 | MARKETING_VERSION = 5.1.0;
363 | MTL_ENABLE_DEBUG_INFO = NO;
364 | SDKROOT = iphoneos;
365 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
366 | SWIFT_VERSION = 5.0;
367 | VALIDATE_PRODUCT = YES;
368 | };
369 | name = Release;
370 | };
371 | 151796FA1BE68E4B004BA6E5 /* Debug */ = {
372 | isa = XCBuildConfiguration;
373 | buildSettings = {
374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
375 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
376 | CODE_SIGN_STYLE = Automatic;
377 | DEVELOPMENT_TEAM = "";
378 | INFOPLIST_FILE = Example/Info.plist;
379 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
380 | LD_RUNPATH_SEARCH_PATHS = (
381 | "$(inherited)",
382 | "@executable_path/Frameworks",
383 | );
384 | PRODUCT_BUNDLE_IDENTIFIER = "com.scalessec.Toast-Swift";
385 | PRODUCT_NAME = "$(TARGET_NAME)";
386 | PROVISIONING_PROFILE_SPECIFIER = "";
387 | };
388 | name = Debug;
389 | };
390 | 151796FB1BE68E4B004BA6E5 /* Release */ = {
391 | isa = XCBuildConfiguration;
392 | buildSettings = {
393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
394 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
395 | CODE_SIGN_STYLE = Automatic;
396 | DEVELOPMENT_TEAM = "";
397 | INFOPLIST_FILE = Example/Info.plist;
398 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
399 | LD_RUNPATH_SEARCH_PATHS = (
400 | "$(inherited)",
401 | "@executable_path/Frameworks",
402 | );
403 | PRODUCT_BUNDLE_IDENTIFIER = "com.scalessec.Toast-Swift";
404 | PRODUCT_NAME = "$(TARGET_NAME)";
405 | PROVISIONING_PROFILE_SPECIFIER = "";
406 | SWIFT_COMPILATION_MODE = wholemodule;
407 | SWIFT_OPTIMIZATION_LEVEL = "-O";
408 | };
409 | name = Release;
410 | };
411 | 2A3743531CBFAF4C006A3BAB /* Debug */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | CLANG_ANALYZER_NONNULL = YES;
415 | CODE_SIGN_IDENTITY = "";
416 | CURRENT_PROJECT_VERSION = 1;
417 | DEFINES_MODULE = YES;
418 | DYLIB_COMPATIBILITY_VERSION = 1;
419 | DYLIB_CURRENT_VERSION = 1;
420 | DYLIB_INSTALL_NAME_BASE = "@rpath";
421 | ENABLE_MODULE_VERIFIER = YES;
422 | INFOPLIST_FILE = "Toast-Swift-Framework/Info.plist";
423 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
424 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
425 | LD_RUNPATH_SEARCH_PATHS = (
426 | "$(inherited)",
427 | "@executable_path/Frameworks",
428 | "@loader_path/Frameworks",
429 | );
430 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
431 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
432 | PRODUCT_BUNDLE_IDENTIFIER = "com.scalessec.Toast-Swift-Framework";
433 | PRODUCT_NAME = "$(TARGET_NAME)";
434 | SKIP_INSTALL = YES;
435 | TARGETED_DEVICE_FAMILY = "1,2";
436 | VERSIONING_SYSTEM = "apple-generic";
437 | VERSION_INFO_PREFIX = "";
438 | };
439 | name = Debug;
440 | };
441 | 2A3743541CBFAF4C006A3BAB /* Release */ = {
442 | isa = XCBuildConfiguration;
443 | buildSettings = {
444 | CLANG_ANALYZER_NONNULL = YES;
445 | CODE_SIGN_IDENTITY = "";
446 | CURRENT_PROJECT_VERSION = 1;
447 | DEFINES_MODULE = YES;
448 | DYLIB_COMPATIBILITY_VERSION = 1;
449 | DYLIB_CURRENT_VERSION = 1;
450 | DYLIB_INSTALL_NAME_BASE = "@rpath";
451 | ENABLE_MODULE_VERIFIER = YES;
452 | INFOPLIST_FILE = "Toast-Swift-Framework/Info.plist";
453 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
454 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
455 | LD_RUNPATH_SEARCH_PATHS = (
456 | "$(inherited)",
457 | "@executable_path/Frameworks",
458 | "@loader_path/Frameworks",
459 | );
460 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
461 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
462 | PRODUCT_BUNDLE_IDENTIFIER = "com.scalessec.Toast-Swift-Framework";
463 | PRODUCT_NAME = "$(TARGET_NAME)";
464 | SKIP_INSTALL = YES;
465 | SWIFT_COMPILATION_MODE = wholemodule;
466 | SWIFT_OPTIMIZATION_LEVEL = "-O";
467 | TARGETED_DEVICE_FAMILY = "1,2";
468 | VERSIONING_SYSTEM = "apple-generic";
469 | VERSION_INFO_PREFIX = "";
470 | };
471 | name = Release;
472 | };
473 | /* End XCBuildConfiguration section */
474 |
475 | /* Begin XCConfigurationList section */
476 | 151796E21BE68E4B004BA6E5 /* Build configuration list for PBXProject "Toast-Swift" */ = {
477 | isa = XCConfigurationList;
478 | buildConfigurations = (
479 | 151796F71BE68E4B004BA6E5 /* Debug */,
480 | 151796F81BE68E4B004BA6E5 /* Release */,
481 | );
482 | defaultConfigurationIsVisible = 0;
483 | defaultConfigurationName = Release;
484 | };
485 | 151796F91BE68E4B004BA6E5 /* Build configuration list for PBXNativeTarget "Toast-Swift" */ = {
486 | isa = XCConfigurationList;
487 | buildConfigurations = (
488 | 151796FA1BE68E4B004BA6E5 /* Debug */,
489 | 151796FB1BE68E4B004BA6E5 /* Release */,
490 | );
491 | defaultConfigurationIsVisible = 0;
492 | defaultConfigurationName = Release;
493 | };
494 | 2A3743551CBFAF4C006A3BAB /* Build configuration list for PBXNativeTarget "ToastSwiftFramework" */ = {
495 | isa = XCConfigurationList;
496 | buildConfigurations = (
497 | 2A3743531CBFAF4C006A3BAB /* Debug */,
498 | 2A3743541CBFAF4C006A3BAB /* Release */,
499 | );
500 | defaultConfigurationIsVisible = 0;
501 | defaultConfigurationName = Release;
502 | };
503 | /* End XCConfigurationList section */
504 | };
505 | rootObject = 151796DF1BE68E4B004BA6E5 /* Project object */;
506 | }
507 |
--------------------------------------------------------------------------------
/Toast/Toast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Toast.swift
3 | // Toast-Swift
4 | //
5 | // Copyright (c) 2015-2024 Charles Scalesse.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a
8 | // copy of this software and associated documentation files (the
9 | // "Software"), to deal in the Software without restriction, including
10 | // without limitation the rights to use, copy, modify, merge, publish,
11 | // distribute, sublicense, and/or sell copies of the Software, and to
12 | // permit persons to whom the Software is furnished to do so, subject to
13 | // the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included
16 | // in all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 |
26 | import UIKit
27 | import ObjectiveC
28 |
29 | /**
30 | Toast is a Swift extension that adds toast notifications to the `UIView` object class.
31 | It is intended to be simple, lightweight, and easy to use. Most toast notifications
32 | can be triggered with a single line of code.
33 |
34 | The `makeToast` methods create a new view and then display it as toast.
35 |
36 | The `showToast` methods display any view as toast.
37 |
38 | */
39 | public extension UIView {
40 |
41 | /**
42 | Keys used for associated objects.
43 | */
44 | private struct ToastKeys {
45 | static var timer = malloc(1)
46 | static var duration = malloc(1)
47 | static var point = malloc(1)
48 | static var completion = malloc(1)
49 | static var activeToasts = malloc(1)
50 | static var activityView = malloc(1)
51 | static var queue = malloc(1)
52 | }
53 |
54 | /**
55 | Swift closures can't be directly associated with objects via the
56 | Objective-C runtime, so the (ugly) solution is to wrap them in a
57 | class that can be used with associated objects.
58 | */
59 | private class ToastCompletionWrapper {
60 | let completion: ((Bool) -> Void)?
61 |
62 | init(_ completion: ((Bool) -> Void)?) {
63 | self.completion = completion
64 | }
65 | }
66 |
67 | private enum ToastError: Error {
68 | case missingParameters
69 | }
70 |
71 | private var activeToasts: NSMutableArray {
72 | get {
73 | if let activeToasts = objc_getAssociatedObject(self, &ToastKeys.activeToasts) as? NSMutableArray {
74 | return activeToasts
75 | } else {
76 | let activeToasts = NSMutableArray()
77 | objc_setAssociatedObject(self, &ToastKeys.activeToasts, activeToasts, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
78 | return activeToasts
79 | }
80 | }
81 | }
82 |
83 | private var queue: NSMutableArray {
84 | get {
85 | if let queue = objc_getAssociatedObject(self, &ToastKeys.queue) as? NSMutableArray {
86 | return queue
87 | } else {
88 | let queue = NSMutableArray()
89 | objc_setAssociatedObject(self, &ToastKeys.queue, queue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
90 | return queue
91 | }
92 | }
93 | }
94 |
95 | // MARK: - Make Toast Methods
96 |
97 | /**
98 | Creates and presents a new toast view.
99 |
100 | @param message The message to be displayed
101 | @param duration The toast duration
102 | @param position The toast's position
103 | @param title The title
104 | @param image The image
105 | @param style The style. The shared style will be used when nil
106 | @param completion The completion closure, executed after the toast view disappears.
107 | didTap will be `true` if the toast view was dismissed from a tap.
108 | */
109 | func makeToast(_ message: String?, duration: TimeInterval = ToastManager.shared.duration, position: ToastPosition = ToastManager.shared.position, title: String? = nil, image: UIImage? = nil, style: ToastStyle = ToastManager.shared.style, completion: ((_ didTap: Bool) -> Void)? = nil) {
110 | do {
111 | let toast = try toastViewForMessage(message, title: title, image: image, style: style)
112 | showToast(toast, duration: duration, position: position, completion: completion)
113 | } catch ToastError.missingParameters {
114 | print("Error: message, title, and image are all nil")
115 | } catch {}
116 | }
117 |
118 | /**
119 | Creates a new toast view and presents it at a given center point.
120 |
121 | @param message The message to be displayed
122 | @param duration The toast duration
123 | @param point The toast's center point
124 | @param title The title
125 | @param image The image
126 | @param style The style. The shared style will be used when nil
127 | @param completion The completion closure, executed after the toast view disappears.
128 | didTap will be `true` if the toast view was dismissed from a tap.
129 | */
130 | func makeToast(_ message: String?, duration: TimeInterval = ToastManager.shared.duration, point: CGPoint, title: String?, image: UIImage?, style: ToastStyle = ToastManager.shared.style, completion: ((_ didTap: Bool) -> Void)?) {
131 | do {
132 | let toast = try toastViewForMessage(message, title: title, image: image, style: style)
133 | showToast(toast, duration: duration, point: point, completion: completion)
134 | } catch ToastError.missingParameters {
135 | print("Error: message, title, and image cannot all be nil")
136 | } catch {}
137 | }
138 |
139 | // MARK: - Show Toast Methods
140 |
141 | /**
142 | Displays any view as toast at a provided position and duration. The completion closure
143 | executes when the toast view completes. `didTap` will be `true` if the toast view was
144 | dismissed from a tap.
145 |
146 | @param toast The view to be displayed as toast
147 | @param duration The notification duration
148 | @param position The toast's position
149 | @param completion The completion block, executed after the toast view disappears.
150 | didTap will be `true` if the toast view was dismissed from a tap.
151 | */
152 | func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, position: ToastPosition = ToastManager.shared.position, completion: ((_ didTap: Bool) -> Void)? = nil) {
153 | let point = position.centerPoint(forToast: toast, inSuperview: self)
154 | showToast(toast, duration: duration, point: point, completion: completion)
155 | }
156 |
157 | /**
158 | Displays any view as toast at a provided center point and duration. The completion closure
159 | executes when the toast view completes. `didTap` will be `true` if the toast view was
160 | dismissed from a tap.
161 |
162 | @param toast The view to be displayed as toast
163 | @param duration The notification duration
164 | @param point The toast's center point
165 | @param completion The completion block, executed after the toast view disappears.
166 | didTap will be `true` if the toast view was dismissed from a tap.
167 | */
168 | func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, point: CGPoint, completion: ((_ didTap: Bool) -> Void)? = nil) {
169 | objc_setAssociatedObject(toast, &ToastKeys.completion, ToastCompletionWrapper(completion), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
170 |
171 | if ToastManager.shared.isQueueEnabled, activeToasts.count > 0 {
172 | objc_setAssociatedObject(toast, &ToastKeys.duration, NSNumber(value: duration), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
173 | objc_setAssociatedObject(toast, &ToastKeys.point, NSValue(cgPoint: point), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
174 |
175 | queue.add(toast)
176 | } else {
177 | showToast(toast, duration: duration, point: point)
178 | }
179 | }
180 |
181 | // MARK: - Hide Toast Methods
182 |
183 | /**
184 | Hides the active toast. If there are multiple toasts active in a view, this method
185 | hides the oldest toast (the first of the toasts to have been presented).
186 |
187 | @see `hideAllToasts()` to remove all active toasts from a view.
188 |
189 | @warning This method has no effect on activity toasts. Use `hideToastActivity` to
190 | hide activity toasts.
191 |
192 | */
193 | func hideToast() {
194 | guard let activeToast = activeToasts.firstObject as? UIView else { return }
195 | hideToast(activeToast)
196 | }
197 |
198 | /**
199 | Hides an active toast.
200 |
201 | @param toast The active toast view to dismiss. Any toast that is currently being displayed
202 | on the screen is considered active.
203 |
204 | @warning this does not clear a toast view that is currently waiting in the queue.
205 | */
206 | func hideToast(_ toast: UIView) {
207 | guard activeToasts.contains(toast) else { return }
208 | hideToast(toast, fromTap: false)
209 | }
210 |
211 | /**
212 | Hides all toast views.
213 |
214 | @param includeActivity If `true`, toast activity will also be hidden. Default is `false`.
215 | @param clearQueue If `true`, removes all toast views from the queue. Default is `true`.
216 | */
217 | func hideAllToasts(includeActivity: Bool = false, clearQueue: Bool = true) {
218 | if clearQueue {
219 | clearToastQueue()
220 | }
221 |
222 | activeToasts.compactMap { $0 as? UIView }
223 | .forEach { hideToast($0) }
224 |
225 | if includeActivity {
226 | hideToastActivity()
227 | }
228 | }
229 |
230 | /**
231 | Removes all toast views from the queue. This has no effect on toast views that are
232 | active. Use `hideAllToasts(clearQueue:)` to hide the active toasts views and clear
233 | the queue.
234 | */
235 | func clearToastQueue() {
236 | queue.removeAllObjects()
237 | }
238 |
239 | // MARK: - Activity Methods
240 |
241 | /**
242 | Creates and displays a new toast activity indicator view at a specified position.
243 |
244 | @warning Only one toast activity indicator view can be presented per superview. Subsequent
245 | calls to `makeToastActivity(position:)` will be ignored until `hideToastActivity()` is called.
246 |
247 | @warning `makeToastActivity(position:)` works independently of the `showToast` methods. Toast
248 | activity views can be presented and dismissed while toast views are being displayed.
249 | `makeToastActivity(position:)` has no effect on the queueing behavior of the `showToast` methods.
250 |
251 | @param position The toast's position
252 | */
253 | func makeToastActivity(_ position: ToastPosition) {
254 | // sanity
255 | guard objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView == nil else { return }
256 |
257 | let toast = createToastActivityView()
258 | let point = position.centerPoint(forToast: toast, inSuperview: self)
259 | makeToastActivity(toast, point: point)
260 | }
261 |
262 | /**
263 | Creates and displays a new toast activity indicator view at a specified position.
264 |
265 | @warning Only one toast activity indicator view can be presented per superview. Subsequent
266 | calls to `makeToastActivity(position:)` will be ignored until `hideToastActivity()` is called.
267 |
268 | @warning `makeToastActivity(position:)` works independently of the `showToast` methods. Toast
269 | activity views can be presented and dismissed while toast views are being displayed.
270 | `makeToastActivity(position:)` has no effect on the queueing behavior of the `showToast` methods.
271 |
272 | @param point The toast's center point
273 | */
274 | func makeToastActivity(_ point: CGPoint) {
275 | // sanity
276 | guard objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView == nil else { return }
277 |
278 | let toast = createToastActivityView()
279 | makeToastActivity(toast, point: point)
280 | }
281 |
282 | /**
283 | Dismisses the active toast activity indicator view.
284 | */
285 | func hideToastActivity() {
286 | if let toast = objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView {
287 | UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: {
288 | toast.alpha = 0.0
289 | }) { _ in
290 | toast.removeFromSuperview()
291 | objc_setAssociatedObject(self, &ToastKeys.activityView, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
292 | }
293 | }
294 | }
295 |
296 | // MARK: - Helper Methods
297 |
298 | /**
299 | Returns `true` if a toast view or toast activity view is actively being displayed.
300 | */
301 | func isShowingToast() -> Bool {
302 | return activeToasts.count > 0 || objc_getAssociatedObject(self, &ToastKeys.activityView) != nil
303 | }
304 |
305 | // MARK: - Private Activity Methods
306 |
307 | private func makeToastActivity(_ toast: UIView, point: CGPoint) {
308 | toast.alpha = 0.0
309 | toast.center = point
310 |
311 | objc_setAssociatedObject(self, &ToastKeys.activityView, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
312 |
313 | self.addSubview(toast)
314 |
315 | UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: .curveEaseOut, animations: {
316 | toast.alpha = 1.0
317 | })
318 | }
319 |
320 | private func createToastActivityView() -> UIView {
321 | let style = ToastManager.shared.style
322 |
323 | let activityView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: style.activitySize.width, height: style.activitySize.height))
324 | activityView.backgroundColor = style.activityBackgroundColor
325 | activityView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
326 | activityView.layer.cornerRadius = style.cornerRadius
327 |
328 | if style.displayShadow {
329 | activityView.layer.shadowColor = style.shadowColor.cgColor
330 | activityView.layer.shadowOpacity = style.shadowOpacity
331 | activityView.layer.shadowRadius = style.shadowRadius
332 | activityView.layer.shadowOffset = style.shadowOffset
333 | }
334 |
335 | let activityIndicatorView = UIActivityIndicatorView(style: .whiteLarge)
336 | activityIndicatorView.center = CGPoint(x: activityView.bounds.size.width / 2.0, y: activityView.bounds.size.height / 2.0)
337 | activityView.addSubview(activityIndicatorView)
338 | activityIndicatorView.color = style.activityIndicatorColor
339 | activityIndicatorView.startAnimating()
340 |
341 | return activityView
342 | }
343 |
344 | // MARK: - Private Show/Hide Methods
345 |
346 | private func showToast(_ toast: UIView, duration: TimeInterval, point: CGPoint) {
347 | toast.center = point
348 | toast.alpha = 0.0
349 |
350 | if ToastManager.shared.isTapToDismissEnabled {
351 | let recognizer = UITapGestureRecognizer(target: self, action: #selector(UIView.handleToastTapped(_:)))
352 | toast.addGestureRecognizer(recognizer)
353 | toast.isUserInteractionEnabled = true
354 | toast.isExclusiveTouch = true
355 | }
356 |
357 | activeToasts.add(toast)
358 | self.addSubview(toast)
359 |
360 | let timer = Timer(timeInterval: duration, target: self, selector: #selector(UIView.toastTimerDidFinish(_:)), userInfo: toast, repeats: false)
361 | objc_setAssociatedObject(toast, &ToastKeys.timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
362 |
363 | UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseOut, .allowUserInteraction], animations: {
364 | toast.alpha = 1.0
365 | }) { _ in
366 | guard let timer = objc_getAssociatedObject(toast, &ToastKeys.timer) as? Timer else { return }
367 | RunLoop.main.add(timer, forMode: .common)
368 | }
369 |
370 | UIAccessibility.post(notification: .screenChanged, argument: toast)
371 | }
372 |
373 | private func hideToast(_ toast: UIView, fromTap: Bool) {
374 | if let timer = objc_getAssociatedObject(toast, &ToastKeys.timer) as? Timer {
375 | timer.invalidate()
376 | }
377 |
378 | UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: {
379 | toast.alpha = 0.0
380 | }) { _ in
381 | toast.removeFromSuperview()
382 | self.activeToasts.remove(toast)
383 |
384 | if let wrapper = objc_getAssociatedObject(toast, &ToastKeys.completion) as? ToastCompletionWrapper, let completion = wrapper.completion {
385 | completion(fromTap)
386 | }
387 |
388 | if let nextToast = self.queue.firstObject as? UIView, let duration = objc_getAssociatedObject(nextToast, &ToastKeys.duration) as? NSNumber, let point = objc_getAssociatedObject(nextToast, &ToastKeys.point) as? NSValue {
389 | self.queue.removeObject(at: 0)
390 | self.showToast(nextToast, duration: duration.doubleValue, point: point.cgPointValue)
391 | }
392 | }
393 | }
394 |
395 | // MARK: - Events
396 |
397 | @objc
398 | private func handleToastTapped(_ recognizer: UITapGestureRecognizer) {
399 | guard let toast = recognizer.view else { return }
400 | hideToast(toast, fromTap: true)
401 | }
402 |
403 | @objc
404 | private func toastTimerDidFinish(_ timer: Timer) {
405 | guard let toast = timer.userInfo as? UIView else { return }
406 | hideToast(toast)
407 | }
408 |
409 | // MARK: - Toast Construction
410 |
411 | /**
412 | Creates a new toast view with any combination of message, title, and image.
413 | The look and feel is configured via the style. Unlike the `makeToast` methods,
414 | this method does not present the toast view automatically. One of the `showToast`
415 | methods must be used to present the resulting view.
416 |
417 | @warning if message, title, and image are all nil, this method will throw
418 | `ToastError.missingParameters`
419 |
420 | @param message The message to be displayed
421 | @param title The title
422 | @param image The image
423 | @param style The style. The shared style will be used when nil
424 | @throws `ToastError.missingParameters` when message, title, and image are all nil
425 | @return The newly created toast view
426 | */
427 | func toastViewForMessage(_ message: String?, title: String?, image: UIImage?, style: ToastStyle) throws -> UIView {
428 | // sanity
429 | guard message != nil || title != nil || image != nil else {
430 | throw ToastError.missingParameters
431 | }
432 |
433 | var messageLabel: UILabel?
434 | var titleLabel: UILabel?
435 | var imageView: UIImageView?
436 |
437 | let wrapperView = UIView()
438 | wrapperView.backgroundColor = style.backgroundColor
439 | wrapperView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
440 | wrapperView.layer.cornerRadius = style.cornerRadius
441 |
442 | if style.displayShadow {
443 | wrapperView.layer.shadowColor = style.shadowColor.cgColor
444 | wrapperView.layer.shadowOpacity = style.shadowOpacity
445 | wrapperView.layer.shadowRadius = style.shadowRadius
446 | wrapperView.layer.shadowOffset = style.shadowOffset
447 | }
448 |
449 | if let image = image {
450 | imageView = UIImageView(image: image)
451 | imageView?.contentMode = .scaleAspectFit
452 | imageView?.frame = CGRect(x: style.horizontalPadding, y: style.verticalPadding, width: style.imageSize.width, height: style.imageSize.height)
453 | }
454 |
455 | var imageRect = CGRect.zero
456 |
457 | if let imageView = imageView {
458 | imageRect.origin.x = style.horizontalPadding
459 | imageRect.origin.y = style.verticalPadding
460 | imageRect.size.width = imageView.bounds.size.width
461 | imageRect.size.height = imageView.bounds.size.height
462 | }
463 |
464 | if let title = title {
465 | titleLabel = UILabel()
466 | titleLabel?.numberOfLines = style.titleNumberOfLines
467 | titleLabel?.font = style.titleFont
468 | titleLabel?.textAlignment = style.titleAlignment
469 | titleLabel?.lineBreakMode = .byTruncatingTail
470 | titleLabel?.textColor = style.titleColor
471 | titleLabel?.backgroundColor = UIColor.clear
472 | titleLabel?.text = title;
473 |
474 | let maxTitleSize = CGSize(width: (self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, height: self.bounds.size.height * style.maxHeightPercentage)
475 | let titleSize = titleLabel?.sizeThatFits(maxTitleSize)
476 | if let titleSize = titleSize {
477 | titleLabel?.frame = CGRect(x: 0.0, y: 0.0, width: titleSize.width, height: titleSize.height)
478 | }
479 | }
480 |
481 | if let message = message {
482 | messageLabel = UILabel()
483 | messageLabel?.text = message
484 | messageLabel?.numberOfLines = style.messageNumberOfLines
485 | messageLabel?.font = style.messageFont
486 | messageLabel?.textAlignment = style.messageAlignment
487 | messageLabel?.lineBreakMode = .byTruncatingTail;
488 | messageLabel?.textColor = style.messageColor
489 | messageLabel?.backgroundColor = UIColor.clear
490 |
491 | let maxMessageSize = CGSize(width: (self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, height: self.bounds.size.height * style.maxHeightPercentage)
492 | let messageSize = messageLabel?.sizeThatFits(maxMessageSize)
493 | if let messageSize = messageSize {
494 | let actualWidth = min(messageSize.width, maxMessageSize.width)
495 | let actualHeight = min(messageSize.height, maxMessageSize.height)
496 | messageLabel?.frame = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
497 | }
498 | }
499 |
500 | var titleRect = CGRect.zero
501 |
502 | if let titleLabel = titleLabel {
503 | titleRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding
504 | titleRect.origin.y = style.verticalPadding
505 | titleRect.size.width = titleLabel.bounds.size.width
506 | titleRect.size.height = titleLabel.bounds.size.height
507 | }
508 |
509 | var messageRect = CGRect.zero
510 |
511 | if let messageLabel = messageLabel {
512 | messageRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding
513 | messageRect.origin.y = titleRect.origin.y + titleRect.size.height + style.verticalPadding
514 | messageRect.size.width = messageLabel.bounds.size.width
515 | messageRect.size.height = messageLabel.bounds.size.height
516 | }
517 |
518 | let longerWidth = max(titleRect.size.width, messageRect.size.width)
519 | let longerX = max(titleRect.origin.x, messageRect.origin.x)
520 | let wrapperWidth = max((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding))
521 |
522 | let textMaxY = messageRect.size.height <= 0.0 && titleRect.size.height > 0.0 ? titleRect.maxY : messageRect.maxY
523 | let wrapperHeight = max((textMaxY + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0)))
524 |
525 | wrapperView.frame = CGRect(x: 0.0, y: 0.0, width: wrapperWidth, height: wrapperHeight)
526 |
527 | if let titleLabel = titleLabel {
528 | titleRect.size.width = longerWidth
529 | titleLabel.frame = titleRect
530 | wrapperView.addSubview(titleLabel)
531 | }
532 |
533 | if let messageLabel = messageLabel {
534 | messageRect.size.width = longerWidth
535 | messageLabel.frame = messageRect
536 | wrapperView.addSubview(messageLabel)
537 | }
538 |
539 | if let imageView = imageView {
540 | wrapperView.addSubview(imageView)
541 | }
542 |
543 | return wrapperView
544 | }
545 |
546 | }
547 |
548 | // MARK: - Toast Style
549 |
550 | /**
551 | `ToastStyle` instances define the look and feel for toast views created via the
552 | `makeToast` methods as well for toast views created directly with
553 | `toastViewForMessage(message:title:image:style:)`.
554 |
555 | @warning `ToastStyle` offers relatively simple styling options for the default
556 | toast view. If you require a toast view with more complex UI, it probably makes more
557 | sense to create your own custom UIView subclass and present it with the `showToast`
558 | methods.
559 | */
560 | public struct ToastStyle {
561 |
562 | public init() {}
563 |
564 | /**
565 | The background color. Default is `.black` at 80% opacity.
566 | */
567 | public var backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8)
568 |
569 | /**
570 | The title color. Default is `UIColor.whiteColor()`.
571 | */
572 | public var titleColor: UIColor = .white
573 |
574 | /**
575 | The message color. Default is `.white`.
576 | */
577 | public var messageColor: UIColor = .white
578 |
579 | /**
580 | A percentage value from 0.0 to 1.0, representing the maximum width of the toast
581 | view relative to it's superview. Default is 0.8 (80% of the superview's width).
582 | */
583 | public var maxWidthPercentage: CGFloat = 0.8 {
584 | didSet {
585 | maxWidthPercentage = max(min(maxWidthPercentage, 1.0), 0.0)
586 | }
587 | }
588 |
589 | /**
590 | A percentage value from 0.0 to 1.0, representing the maximum height of the toast
591 | view relative to it's superview. Default is 0.8 (80% of the superview's height).
592 | */
593 | public var maxHeightPercentage: CGFloat = 0.8 {
594 | didSet {
595 | maxHeightPercentage = max(min(maxHeightPercentage, 1.0), 0.0)
596 | }
597 | }
598 |
599 | /**
600 | The spacing from the horizontal edge of the toast view to the content. When an image
601 | is present, this is also used as the padding between the image and the text.
602 | Default is 10.0.
603 |
604 | */
605 | public var horizontalPadding: CGFloat = 10.0
606 |
607 | /**
608 | The spacing from the vertical edge of the toast view to the content. When a title
609 | is present, this is also used as the padding between the title and the message.
610 | Default is 10.0. On iOS11+, this value is added added to the `safeAreaInset.top`
611 | and `safeAreaInsets.bottom`.
612 | */
613 | public var verticalPadding: CGFloat = 10.0
614 |
615 | /**
616 | The corner radius. Default is 10.0.
617 | */
618 | public var cornerRadius: CGFloat = 10.0;
619 |
620 | /**
621 | The title font. Default is `.boldSystemFont(16.0)`.
622 | */
623 | public var titleFont: UIFont = .boldSystemFont(ofSize: 16.0)
624 |
625 | /**
626 | The message font. Default is `.systemFont(ofSize: 16.0)`.
627 | */
628 | public var messageFont: UIFont = .systemFont(ofSize: 16.0)
629 |
630 | /**
631 | The title text alignment. Default is `NSTextAlignment.Left`.
632 | */
633 | public var titleAlignment: NSTextAlignment = .left
634 |
635 | /**
636 | The message text alignment. Default is `NSTextAlignment.Left`.
637 | */
638 | public var messageAlignment: NSTextAlignment = .left
639 |
640 | /**
641 | The maximum number of lines for the title. The default is 0 (no limit).
642 | */
643 | public var titleNumberOfLines = 0
644 |
645 | /**
646 | The maximum number of lines for the message. The default is 0 (no limit).
647 | */
648 | public var messageNumberOfLines = 0
649 |
650 | /**
651 | Enable or disable a shadow on the toast view. Default is `false`.
652 | */
653 | public var displayShadow = false
654 |
655 | /**
656 | The shadow color. Default is `.black`.
657 | */
658 | public var shadowColor: UIColor = .black
659 |
660 | /**
661 | A value from 0.0 to 1.0, representing the opacity of the shadow.
662 | Default is 0.8 (80% opacity).
663 | */
664 | public var shadowOpacity: Float = 0.8 {
665 | didSet {
666 | shadowOpacity = max(min(shadowOpacity, 1.0), 0.0)
667 | }
668 | }
669 |
670 | /**
671 | The shadow radius. Default is 6.0.
672 | */
673 | public var shadowRadius: CGFloat = 6.0
674 |
675 | /**
676 | The shadow offset. The default is 4 x 4.
677 | */
678 | public var shadowOffset = CGSize(width: 4.0, height: 4.0)
679 |
680 | /**
681 | The image size. The default is 80 x 80.
682 | */
683 | public var imageSize = CGSize(width: 80.0, height: 80.0)
684 |
685 | /**
686 | The size of the toast activity view when `makeToastActivity(position:)` is called.
687 | Default is 100 x 100.
688 | */
689 | public var activitySize = CGSize(width: 100.0, height: 100.0)
690 |
691 | /**
692 | The fade in/out animation duration. Default is 0.2.
693 | */
694 | public var fadeDuration: TimeInterval = 0.2
695 |
696 | /**
697 | Activity indicator color. Default is `.white`.
698 | */
699 | public var activityIndicatorColor: UIColor = .white
700 |
701 | /**
702 | Activity background color. Default is `.black` at 80% opacity.
703 | */
704 | public var activityBackgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8)
705 |
706 | }
707 |
708 | // MARK: - Toast Manager
709 |
710 | /**
711 | `ToastManager` provides general configuration options for all toast
712 | notifications. Backed by a singleton instance.
713 | */
714 | public class ToastManager {
715 |
716 | /**
717 | The `ToastManager` singleton instance.
718 |
719 | */
720 | public static let shared = ToastManager()
721 |
722 | /**
723 | The shared style. Used whenever toastViewForMessage(message:title:image:style:) is called
724 | with with a nil style.
725 |
726 | */
727 | public var style = ToastStyle()
728 |
729 | /**
730 | Enables or disables tap to dismiss on toast views. Default is `true`.
731 |
732 | */
733 | public var isTapToDismissEnabled = true
734 |
735 | /**
736 | Enables or disables queueing behavior for toast views. When `true`,
737 | toast views will appear one after the other. When `false`, multiple toast
738 | views will appear at the same time (potentially overlapping depending
739 | on their positions). This has no effect on the toast activity view,
740 | which operates independently of normal toast views. Default is `false`.
741 |
742 | */
743 | public var isQueueEnabled = false
744 |
745 | /**
746 | The default duration. Used for the `makeToast` and
747 | `showToast` methods that don't require an explicit duration.
748 | Default is 3.0.
749 |
750 | */
751 | public var duration: TimeInterval = 3.0
752 |
753 | /**
754 | Sets the default position. Used for the `makeToast` and
755 | `showToast` methods that don't require an explicit position.
756 | Default is `ToastPosition.Bottom`.
757 |
758 | */
759 | public var position: ToastPosition = .bottom
760 |
761 | }
762 |
763 | // MARK: - ToastPosition
764 |
765 | public enum ToastPosition {
766 | case top
767 | case center
768 | case bottom
769 |
770 | fileprivate func centerPoint(forToast toast: UIView, inSuperview superview: UIView) -> CGPoint {
771 | let topPadding: CGFloat = ToastManager.shared.style.verticalPadding + superview.csSafeAreaInsets.top
772 | let bottomPadding: CGFloat = ToastManager.shared.style.verticalPadding + superview.csSafeAreaInsets.bottom
773 |
774 | switch self {
775 | case .top:
776 | return CGPoint(x: superview.bounds.size.width / 2.0, y: (toast.frame.size.height / 2.0) + topPadding)
777 | case .center:
778 | return CGPoint(x: superview.bounds.size.width / 2.0, y: superview.bounds.size.height / 2.0)
779 | case .bottom:
780 | return CGPoint(x: superview.bounds.size.width / 2.0, y: (superview.bounds.size.height - (toast.frame.size.height / 2.0)) - bottomPadding)
781 | }
782 | }
783 | }
784 |
785 | // MARK: - Private UIView Extensions
786 |
787 | private extension UIView {
788 |
789 | var csSafeAreaInsets: UIEdgeInsets {
790 | if #available(iOS 11.0, *) {
791 | return self.safeAreaInsets
792 | } else {
793 | return .zero
794 | }
795 | }
796 |
797 | }
798 |
--------------------------------------------------------------------------------