├── .github ├── FUNDING.yml └── stale.yml ├── .gitignore ├── .ruby-version ├── .travis.yml ├── AppIcons ├── cardcast.png ├── envision.png ├── happy_scale.png ├── lukapizza.png ├── modernmagic8ball.png ├── q_talk_about_music.jpg ├── ris.png ├── stikkr.png ├── tsum.png ├── vh_dispatch.png └── wanderings.png ├── Example ├── NotificationBanner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ └── NotificationBanner-Example.xcscheme └── NotificationBanner │ ├── AppDelegate.swift │ ├── Base.lproj │ └── LaunchScreen.xib │ ├── BasicNotificationBannerCell.swift │ ├── CustomBannerColors.swift │ ├── ExampleView.swift │ ├── ExampleViewController.swift │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-29x29@1x-1.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x-1.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x-1.png │ │ ├── Icon-App-40x40@2x-2.png │ │ ├── Icon-App-60x60@2x-1.png │ │ ├── Icon-App-60x60@2x-2.png │ │ ├── Icon-App-60x60@3x-1.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ └── Icon-App-76x76@2x.png │ ├── Contents.json │ ├── cubs_logo.imageset │ │ ├── Contents.json │ │ └── cubs_logo.png │ ├── danger.imageset │ │ ├── Contents.json │ │ └── danger.png │ ├── duke_logo.imageset │ │ ├── Contents.json │ │ └── duke_logo.png │ ├── info.imageset │ │ ├── Contents.json │ │ └── info.png │ ├── right_chevron.imageset │ │ ├── Contents.json │ │ └── right_chevron.png │ ├── success.imageset │ │ ├── Contents.json │ │ └── success.png │ ├── tab_bar_icon.imageset │ │ ├── Contents.json │ │ ├── tab_bar_icon.png │ │ ├── tab_bar_icon@2x.png │ │ └── tab_bar_icon@3x.png │ └── unc_logo.imageset │ │ ├── Contents.json │ │ └── unc_logo.png │ ├── Info.plist │ └── NorthCarolinaBannerView.swift ├── LICENSE ├── NotificationBanner.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ └── NotificationBanner.xcscheme ├── NotificationBanner ├── Assets │ ├── .gitkeep │ ├── basic.gif │ ├── custom.gif │ ├── floating.gif │ ├── growing.gif │ ├── header.png │ ├── side_views.gif │ ├── stacked.gif │ └── status_bar.gif ├── Classes │ ├── .gitkeep │ ├── BannerColors.swift │ ├── BannerHapticGenerator.swift │ ├── BannerPositionFrame.swift │ ├── BannerStyle.swift │ ├── BaseNotificationBanner.swift │ ├── FloatingNotificationBanner.swift │ ├── GrowingNotificationBanner.swift │ ├── NotificationBanner.swift │ ├── NotificationBannerQueue.swift │ ├── NotificationBannerUtilities.swift │ ├── StatusBarNotificationBanner.swift │ ├── String+heightForConstrainedWidth.swift │ └── UIWindow+orientation.swift └── Supporting Files │ ├── Info.plist │ ├── NotificationBanner.h │ └── PrivacyInfo.xcprivacy ├── NotificationBannerSwift.podspec ├── Package.resolved ├── Package.swift ├── README.md └── _Pods.xcodeproj /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [daltron] 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: > 18 | This issue has been automatically closed due to inactivity. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | .swiftpm 35 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -workspace Example/NotificationBanner.xcworkspace -scheme NotificationBanner-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /AppIcons/cardcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/cardcast.png -------------------------------------------------------------------------------- /AppIcons/envision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/envision.png -------------------------------------------------------------------------------- /AppIcons/happy_scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/happy_scale.png -------------------------------------------------------------------------------- /AppIcons/lukapizza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/lukapizza.png -------------------------------------------------------------------------------- /AppIcons/modernmagic8ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/modernmagic8ball.png -------------------------------------------------------------------------------- /AppIcons/q_talk_about_music.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/q_talk_about_music.jpg -------------------------------------------------------------------------------- /AppIcons/ris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/ris.png -------------------------------------------------------------------------------- /AppIcons/stikkr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/stikkr.png -------------------------------------------------------------------------------- /AppIcons/tsum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/tsum.png -------------------------------------------------------------------------------- /AppIcons/vh_dispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/vh_dispatch.png -------------------------------------------------------------------------------- /AppIcons/wanderings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/AppIcons/wanderings.png -------------------------------------------------------------------------------- /Example/NotificationBanner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2B9CEC6121692C2100B5DEB9 /* GrowingNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9CEC6021692C2100B5DEB9 /* GrowingNotificationBanner.swift */; }; 11 | 5C025F761EE9E09E00ADB444 /* BannerColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F6F1EE9E09E00ADB444 /* BannerColors.swift */; }; 12 | 5C025F771EE9E09E00ADB444 /* BannerHapticGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F701EE9E09E00ADB444 /* BannerHapticGenerator.swift */; }; 13 | 5C025F781EE9E09E00ADB444 /* BannerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F711EE9E09E00ADB444 /* BannerStyle.swift */; }; 14 | 5C025F791EE9E09E00ADB444 /* BaseNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F721EE9E09E00ADB444 /* BaseNotificationBanner.swift */; }; 15 | 5C025F7A1EE9E09E00ADB444 /* NotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F731EE9E09E00ADB444 /* NotificationBanner.swift */; }; 16 | 5C025F7B1EE9E09E00ADB444 /* NotificationBannerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F741EE9E09E00ADB444 /* NotificationBannerQueue.swift */; }; 17 | 5C025F7C1EE9E09E00ADB444 /* StatusBarNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C025F751EE9E09E00ADB444 /* StatusBarNotificationBanner.swift */; }; 18 | 5C34233C1EB50580004B804C /* CustomBannerColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C34233B1EB50580004B804C /* CustomBannerColors.swift */; }; 19 | 5C40232C22DCC5E900FD9E11 /* FloatingNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C40232B22DCC5E900FD9E11 /* FloatingNotificationBanner.swift */; }; 20 | 5C8542E41EFDCA3A00AFBC90 /* BannerPositionFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C8542E31EFDCA3A00AFBC90 /* BannerPositionFrame.swift */; }; 21 | 5C8890A81F7155840032D605 /* NotificationBannerUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C8890A71F7155840032D605 /* NotificationBannerUtilities.swift */; }; 22 | 5CBD22411E8353B30041DC8F /* ExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD22401E8353B30041DC8F /* ExampleView.swift */; }; 23 | 5CBD22431E83556E0041DC8F /* BasicNotificationBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD22421E83556E0041DC8F /* BasicNotificationBannerCell.swift */; }; 24 | 5CBD22471E836DE50041DC8F /* NorthCarolinaBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD22461E836DE50041DC8F /* NorthCarolinaBannerView.swift */; }; 25 | 5CCB990C29733A51005A4BE6 /* NotificationBannerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5CCB990B29733A51005A4BE6 /* NotificationBannerSwift */; }; 26 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 27 | 607FACD81AFB9204008FA782 /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ExampleViewController.swift */; }; 28 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 29 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 23D0884ED7027DCB5F8507CC /* NotificationBannerSwift.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = NotificationBannerSwift.podspec; path = ../NotificationBannerSwift.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 34 | 2B9CEC6021692C2100B5DEB9 /* GrowingNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GrowingNotificationBanner.swift; path = ../../NotificationBanner/Classes/GrowingNotificationBanner.swift; sourceTree = ""; }; 35 | 5C025F6F1EE9E09E00ADB444 /* BannerColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BannerColors.swift; path = ../../NotificationBanner/Classes/BannerColors.swift; sourceTree = ""; }; 36 | 5C025F701EE9E09E00ADB444 /* BannerHapticGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BannerHapticGenerator.swift; path = ../../NotificationBanner/Classes/BannerHapticGenerator.swift; sourceTree = ""; }; 37 | 5C025F711EE9E09E00ADB444 /* BannerStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BannerStyle.swift; path = ../../NotificationBanner/Classes/BannerStyle.swift; sourceTree = ""; }; 38 | 5C025F721EE9E09E00ADB444 /* BaseNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BaseNotificationBanner.swift; path = ../../NotificationBanner/Classes/BaseNotificationBanner.swift; sourceTree = ""; }; 39 | 5C025F731EE9E09E00ADB444 /* NotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NotificationBanner.swift; path = ../../NotificationBanner/Classes/NotificationBanner.swift; sourceTree = ""; }; 40 | 5C025F741EE9E09E00ADB444 /* NotificationBannerQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NotificationBannerQueue.swift; path = ../../NotificationBanner/Classes/NotificationBannerQueue.swift; sourceTree = ""; }; 41 | 5C025F751EE9E09E00ADB444 /* StatusBarNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StatusBarNotificationBanner.swift; path = ../../NotificationBanner/Classes/StatusBarNotificationBanner.swift; sourceTree = ""; }; 42 | 5C34233B1EB50580004B804C /* CustomBannerColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomBannerColors.swift; sourceTree = ""; }; 43 | 5C40232B22DCC5E900FD9E11 /* FloatingNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FloatingNotificationBanner.swift; path = ../../NotificationBanner/Classes/FloatingNotificationBanner.swift; sourceTree = ""; }; 44 | 5C8542E31EFDCA3A00AFBC90 /* BannerPositionFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BannerPositionFrame.swift; path = ../../NotificationBanner/Classes/BannerPositionFrame.swift; sourceTree = ""; }; 45 | 5C8890A71F7155840032D605 /* NotificationBannerUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NotificationBannerUtilities.swift; path = ../../NotificationBanner/Classes/NotificationBannerUtilities.swift; sourceTree = ""; }; 46 | 5CBD22401E8353B30041DC8F /* ExampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleView.swift; sourceTree = ""; }; 47 | 5CBD22421E83556E0041DC8F /* BasicNotificationBannerCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicNotificationBannerCell.swift; sourceTree = ""; }; 48 | 5CBD22461E836DE50041DC8F /* NorthCarolinaBannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NorthCarolinaBannerView.swift; sourceTree = ""; }; 49 | 607FACD01AFB9204008FA782 /* NotificationBanner_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NotificationBanner_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | 607FACD71AFB9204008FA782 /* ExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 53 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 54 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 55 | 994F519A87A30C0CFB80BF18 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 56 | BB150F775F66DF9A8CE833B3 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 5CCB990C29733A51005A4BE6 /* NotificationBannerSwift in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 5C0BFCB51E7DFCCD00FE49EB /* Classes */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 5C025F6F1EE9E09E00ADB444 /* BannerColors.swift */, 75 | 5C025F701EE9E09E00ADB444 /* BannerHapticGenerator.swift */, 76 | 5C8542E31EFDCA3A00AFBC90 /* BannerPositionFrame.swift */, 77 | 5C025F711EE9E09E00ADB444 /* BannerStyle.swift */, 78 | 5C025F721EE9E09E00ADB444 /* BaseNotificationBanner.swift */, 79 | 5C40232B22DCC5E900FD9E11 /* FloatingNotificationBanner.swift */, 80 | 2B9CEC6021692C2100B5DEB9 /* GrowingNotificationBanner.swift */, 81 | 5C025F731EE9E09E00ADB444 /* NotificationBanner.swift */, 82 | 5C025F741EE9E09E00ADB444 /* NotificationBannerQueue.swift */, 83 | 5C8890A71F7155840032D605 /* NotificationBannerUtilities.swift */, 84 | 5C025F751EE9E09E00ADB444 /* StatusBarNotificationBanner.swift */, 85 | ); 86 | name = Classes; 87 | sourceTree = ""; 88 | }; 89 | 607FACC71AFB9204008FA782 = { 90 | isa = PBXGroup; 91 | children = ( 92 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 93 | 607FACD21AFB9204008FA782 /* Example for NotificationBanner */, 94 | 607FACD11AFB9204008FA782 /* Products */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | 607FACD11AFB9204008FA782 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 607FACD01AFB9204008FA782 /* NotificationBanner_Example.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 607FACD21AFB9204008FA782 /* Example for NotificationBanner */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 110 | 607FACD71AFB9204008FA782 /* ExampleViewController.swift */, 111 | 5CBD22401E8353B30041DC8F /* ExampleView.swift */, 112 | 5C34233B1EB50580004B804C /* CustomBannerColors.swift */, 113 | 5CBD22421E83556E0041DC8F /* BasicNotificationBannerCell.swift */, 114 | 5CBD22461E836DE50041DC8F /* NorthCarolinaBannerView.swift */, 115 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 116 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 117 | 5C0BFCB51E7DFCCD00FE49EB /* Classes */, 118 | 607FACD31AFB9204008FA782 /* Supporting Files */, 119 | ); 120 | name = "Example for NotificationBanner"; 121 | path = NotificationBanner; 122 | sourceTree = ""; 123 | }; 124 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 607FACD41AFB9204008FA782 /* Info.plist */, 128 | ); 129 | name = "Supporting Files"; 130 | sourceTree = ""; 131 | }; 132 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 23D0884ED7027DCB5F8507CC /* NotificationBannerSwift.podspec */, 136 | BB150F775F66DF9A8CE833B3 /* README.md */, 137 | 994F519A87A30C0CFB80BF18 /* LICENSE */, 138 | ); 139 | name = "Podspec Metadata"; 140 | sourceTree = ""; 141 | }; 142 | /* End PBXGroup section */ 143 | 144 | /* Begin PBXNativeTarget section */ 145 | 607FACCF1AFB9204008FA782 /* NotificationBanner_Example */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "NotificationBanner_Example" */; 148 | buildPhases = ( 149 | 607FACCC1AFB9204008FA782 /* Sources */, 150 | 607FACCD1AFB9204008FA782 /* Frameworks */, 151 | 607FACCE1AFB9204008FA782 /* Resources */, 152 | ); 153 | buildRules = ( 154 | ); 155 | dependencies = ( 156 | ); 157 | name = NotificationBanner_Example; 158 | packageProductDependencies = ( 159 | 5CCB990B29733A51005A4BE6 /* NotificationBannerSwift */, 160 | ); 161 | productName = NotificationBanner; 162 | productReference = 607FACD01AFB9204008FA782 /* NotificationBanner_Example.app */; 163 | productType = "com.apple.product-type.application"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 607FACC81AFB9204008FA782 /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastSwiftUpdateCheck = 0720; 172 | LastUpgradeCheck = 1000; 173 | ORGANIZATIONNAME = CocoaPods; 174 | TargetAttributes = { 175 | 607FACCF1AFB9204008FA782 = { 176 | CreatedOnToolsVersion = 6.3.1; 177 | DevelopmentTeam = S4K2Q6TV87; 178 | LastSwiftMigration = 1020; 179 | ProvisioningStyle = Automatic; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "NotificationBanner" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = en; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | Base, 190 | ); 191 | mainGroup = 607FACC71AFB9204008FA782; 192 | packageReferences = ( 193 | 5CCB990A29733A51005A4BE6 /* XCRemoteSwiftPackageReference "NotificationBanner" */, 194 | ); 195 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | 607FACCF1AFB9204008FA782 /* NotificationBanner_Example */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 607FACCE1AFB9204008FA782 /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 210 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | /* End PBXResourcesBuildPhase section */ 215 | 216 | /* Begin PBXSourcesBuildPhase section */ 217 | 607FACCC1AFB9204008FA782 /* Sources */ = { 218 | isa = PBXSourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 5C025F7C1EE9E09E00ADB444 /* StatusBarNotificationBanner.swift in Sources */, 222 | 5CBD22471E836DE50041DC8F /* NorthCarolinaBannerView.swift in Sources */, 223 | 5C025F7A1EE9E09E00ADB444 /* NotificationBanner.swift in Sources */, 224 | 5C025F761EE9E09E00ADB444 /* BannerColors.swift in Sources */, 225 | 2B9CEC6121692C2100B5DEB9 /* GrowingNotificationBanner.swift in Sources */, 226 | 5C40232C22DCC5E900FD9E11 /* FloatingNotificationBanner.swift in Sources */, 227 | 5C025F781EE9E09E00ADB444 /* BannerStyle.swift in Sources */, 228 | 5C025F791EE9E09E00ADB444 /* BaseNotificationBanner.swift in Sources */, 229 | 607FACD81AFB9204008FA782 /* ExampleViewController.swift in Sources */, 230 | 5C025F7B1EE9E09E00ADB444 /* NotificationBannerQueue.swift in Sources */, 231 | 5C34233C1EB50580004B804C /* CustomBannerColors.swift in Sources */, 232 | 5C025F771EE9E09E00ADB444 /* BannerHapticGenerator.swift in Sources */, 233 | 5C8890A81F7155840032D605 /* NotificationBannerUtilities.swift in Sources */, 234 | 5CBD22431E83556E0041DC8F /* BasicNotificationBannerCell.swift in Sources */, 235 | 5CBD22411E8353B30041DC8F /* ExampleView.swift in Sources */, 236 | 5C8542E41EFDCA3A00AFBC90 /* BannerPositionFrame.swift in Sources */, 237 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXSourcesBuildPhase section */ 242 | 243 | /* Begin PBXVariantGroup section */ 244 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | 607FACDF1AFB9204008FA782 /* Base */, 248 | ); 249 | name = LaunchScreen.xib; 250 | sourceTree = ""; 251 | }; 252 | /* End PBXVariantGroup section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | 607FACED1AFB9204008FA782 /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 264 | CLANG_WARN_BOOL_CONVERSION = YES; 265 | CLANG_WARN_COMMA = YES; 266 | CLANG_WARN_CONSTANT_CONVERSION = YES; 267 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_EMPTY_BODY = YES; 270 | CLANG_WARN_ENUM_CONVERSION = YES; 271 | CLANG_WARN_INFINITE_RECURSION = YES; 272 | CLANG_WARN_INT_CONVERSION = YES; 273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 278 | CLANG_WARN_STRICT_PROTOTYPES = YES; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 283 | COPY_PHASE_STRIP = NO; 284 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 285 | ENABLE_STRICT_OBJC_MSGSEND = YES; 286 | ENABLE_TESTABILITY = YES; 287 | GCC_C_LANGUAGE_STANDARD = gnu99; 288 | GCC_DYNAMIC_NO_PIC = NO; 289 | GCC_NO_COMMON_BLOCKS = YES; 290 | GCC_OPTIMIZATION_LEVEL = 0; 291 | GCC_PREPROCESSOR_DEFINITIONS = ( 292 | "DEBUG=1", 293 | "$(inherited)", 294 | ); 295 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 303 | MTL_ENABLE_DEBUG_INFO = YES; 304 | ONLY_ACTIVE_ARCH = YES; 305 | SDKROOT = iphoneos; 306 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 307 | }; 308 | name = Debug; 309 | }; 310 | 607FACEE1AFB9204008FA782 /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 319 | CLANG_WARN_BOOL_CONVERSION = YES; 320 | CLANG_WARN_COMMA = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | SWIFT_COMPILATION_MODE = wholemodule; 354 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | 607FACF01AFB9204008FA782 /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 364 | DEVELOPMENT_TEAM = S4K2Q6TV87; 365 | INFOPLIST_FILE = NotificationBanner/Info.plist; 366 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 367 | LD_RUNPATH_SEARCH_PATHS = ( 368 | "$(inherited)", 369 | "@executable_path/Frameworks", 370 | ); 371 | MODULE_NAME = ExampleApp; 372 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | PROVISIONING_PROFILE = ""; 375 | PROVISIONING_PROFILE_SPECIFIER = ""; 376 | SWIFT_VERSION = 5.0; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | }; 379 | name = Debug; 380 | }; 381 | 607FACF11AFB9204008FA782 /* Release */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | DEVELOPMENT_TEAM = S4K2Q6TV87; 387 | INFOPLIST_FILE = NotificationBanner/Info.plist; 388 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 389 | LD_RUNPATH_SEARCH_PATHS = ( 390 | "$(inherited)", 391 | "@executable_path/Frameworks", 392 | ); 393 | MODULE_NAME = ExampleApp; 394 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 395 | PRODUCT_NAME = "$(TARGET_NAME)"; 396 | SWIFT_VERSION = 5.0; 397 | TARGETED_DEVICE_FAMILY = "1,2"; 398 | }; 399 | name = Release; 400 | }; 401 | /* End XCBuildConfiguration section */ 402 | 403 | /* Begin XCConfigurationList section */ 404 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "NotificationBanner" */ = { 405 | isa = XCConfigurationList; 406 | buildConfigurations = ( 407 | 607FACED1AFB9204008FA782 /* Debug */, 408 | 607FACEE1AFB9204008FA782 /* Release */, 409 | ); 410 | defaultConfigurationIsVisible = 0; 411 | defaultConfigurationName = Release; 412 | }; 413 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "NotificationBanner_Example" */ = { 414 | isa = XCConfigurationList; 415 | buildConfigurations = ( 416 | 607FACF01AFB9204008FA782 /* Debug */, 417 | 607FACF11AFB9204008FA782 /* Release */, 418 | ); 419 | defaultConfigurationIsVisible = 0; 420 | defaultConfigurationName = Release; 421 | }; 422 | /* End XCConfigurationList section */ 423 | 424 | /* Begin XCRemoteSwiftPackageReference section */ 425 | 5CCB990A29733A51005A4BE6 /* XCRemoteSwiftPackageReference "NotificationBanner" */ = { 426 | isa = XCRemoteSwiftPackageReference; 427 | repositoryURL = "https://github.com/Daltron/NotificationBanner"; 428 | requirement = { 429 | kind = upToNextMajorVersion; 430 | minimumVersion = 3.0.0; 431 | }; 432 | }; 433 | /* End XCRemoteSwiftPackageReference section */ 434 | 435 | /* Begin XCSwiftPackageProductDependency section */ 436 | 5CCB990B29733A51005A4BE6 /* NotificationBannerSwift */ = { 437 | isa = XCSwiftPackageProductDependency; 438 | package = 5CCB990A29733A51005A4BE6 /* XCRemoteSwiftPackageReference "NotificationBanner" */; 439 | productName = NotificationBannerSwift; 440 | }; 441 | /* End XCSwiftPackageProductDependency section */ 442 | }; 443 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 444 | } 445 | -------------------------------------------------------------------------------- /Example/NotificationBanner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/NotificationBanner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/NotificationBanner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "marqueelabel", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/cbpowell/MarqueeLabel", 7 | "state" : { 8 | "revision" : "f2c72a5f8568579dade6350dc26a482076d3d346", 9 | "version" : "4.3.0" 10 | } 11 | }, 12 | { 13 | "identity" : "notificationbanner", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/Daltron/NotificationBanner", 16 | "state" : { 17 | "revision" : "e856a462053daf80c7936056d64246983f3f4d14", 18 | "version" : "3.1.0" 19 | } 20 | }, 21 | { 22 | "identity" : "snapkit", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/SnapKit/SnapKit", 25 | "state" : { 26 | "revision" : "f222cbdf325885926566172f6f5f06af95473158", 27 | "version" : "5.6.0" 28 | } 29 | } 30 | ], 31 | "version" : 2 32 | } 33 | -------------------------------------------------------------------------------- /Example/NotificationBanner.xcodeproj/xcshareddata/xcschemes/NotificationBanner-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 106 | 112 | 113 | 114 | 115 | 117 | 118 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Example/NotificationBanner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // NotificationBanner 4 | // 5 | // Created by Daltron on 03/18/2017. 6 | // Copyright (c) 2017 Daltron. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | 18 | let navigationController = UINavigationController(rootViewController: ExampleViewController()) 19 | let tabBarController = UITabBarController() 20 | tabBarController.viewControllers = [navigationController] 21 | 22 | let tab = tabBarController.tabBar.items![0] 23 | tab.image = #imageLiteral(resourceName: "tab_bar_icon").withRenderingMode(.alwaysOriginal) 24 | tab.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0) 25 | tab.title = nil 26 | 27 | window = UIWindow(frame: UIScreen.main.bounds) 28 | window?.backgroundColor = .white 29 | window?.rootViewController = tabBarController 30 | window?.makeKeyAndVisible() 31 | 32 | return true 33 | } 34 | 35 | func applicationWillResignActive(_ application: UIApplication) { 36 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 37 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 38 | } 39 | 40 | func applicationDidEnterBackground(_ application: UIApplication) { 41 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 42 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 43 | } 44 | 45 | func applicationWillEnterForeground(_ application: UIApplication) { 46 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 47 | } 48 | 49 | func applicationDidBecomeActive(_ application: UIApplication) { 50 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 51 | } 52 | 53 | func applicationWillTerminate(_ application: UIApplication) { 54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 55 | } 56 | 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Example/NotificationBanner/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Example/NotificationBanner/BasicNotificationBannerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationBannerCell.swift 3 | // NotificationBanner 4 | // 5 | // Created by Dalton Hinterscher on 3/22/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BasicNotificationBannerCell: UITableViewCell { 12 | 13 | private var coloredBlockView: UIImageView! 14 | private var titleLabel: UILabel! 15 | private var subtitleLabel: UILabel! 16 | 17 | init(reuseIdentifier: String?) { 18 | super.init(style: .default, reuseIdentifier: reuseIdentifier) 19 | 20 | coloredBlockView = UIImageView() 21 | coloredBlockView.contentMode = .scaleAspectFit 22 | contentView.addSubview(coloredBlockView) 23 | 24 | titleLabel = UILabel() 25 | titleLabel.font = UIFont.systemFont(ofSize: 20.0) 26 | contentView.addSubview(titleLabel) 27 | 28 | subtitleLabel = UILabel() 29 | subtitleLabel.font = UIFont.systemFont(ofSize: 14.0) 30 | subtitleLabel.textColor = .lightGray 31 | contentView.addSubview(subtitleLabel) 32 | 33 | coloredBlockView.snp.makeConstraints { (make) in 34 | make.top.equalToSuperview().offset(15) 35 | make.bottom.equalToSuperview().offset(-15) 36 | make.left.equalToSuperview().offset(15) 37 | make.width.equalTo(coloredBlockView.snp.height) 38 | } 39 | 40 | subtitleLabel.snp.remakeConstraints { (make) in 41 | make.left.equalTo(titleLabel) 42 | make.right.equalTo(titleLabel) 43 | make.bottom.equalTo(coloredBlockView) 44 | } 45 | } 46 | 47 | required init?(coder aDecoder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | 51 | func update(blockColor: UIColor?, image: UIImage?, title: String, subtitle: String?) { 52 | 53 | if let image = image { 54 | coloredBlockView.image = image 55 | coloredBlockView.backgroundColor = .clear 56 | } else if let blockColor = blockColor { 57 | coloredBlockView.backgroundColor = blockColor 58 | coloredBlockView.image = nil 59 | } 60 | 61 | titleLabel.text = title 62 | subtitleLabel.text = subtitle 63 | 64 | if let _ = subtitle { 65 | subtitleLabel.isHidden = false 66 | titleLabel.snp.remakeConstraints { (make) in 67 | make.left.equalTo(coloredBlockView.snp.right).offset(15) 68 | make.right.equalToSuperview().offset(-15) 69 | make.top.equalTo(coloredBlockView) 70 | } 71 | 72 | } else { 73 | subtitleLabel.isHidden = true 74 | titleLabel.snp.remakeConstraints { (make) in 75 | make.left.equalTo(coloredBlockView.snp.right).offset(15) 76 | make.right.equalToSuperview().offset(-15) 77 | make.centerY.equalTo(coloredBlockView) 78 | } 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Example/NotificationBanner/CustomBannerColors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomBannerColors.swift 3 | // NotificationBanner 4 | // 5 | // Created by Dalton Hinterscher on 4/29/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CustomBannerColors: BannerColors { 12 | 13 | /* In this example, I only want to override the warning style. If it is not the warning style, then 14 | I simply call super and return the default value. 15 | */ 16 | internal override func color(for style: BannerStyle) -> UIColor { 17 | switch style { 18 | case .danger: return super.color(for: style) 19 | case .info: return super.color(for: style) 20 | case .customView: return super.color(for: style) 21 | case .success: return super.color(for: style) 22 | case .warning: return UIColor(red:0.99, green:0.40, blue:0.13, alpha:1.00) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Example/NotificationBanner/ExampleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationBannerView.swift 3 | // NotificationBanner 4 | // 5 | // Created by Dalton Hinterscher on 3/22/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ExampleViewDelegate : AnyObject { 12 | func numberOfCells(for section: Int) -> Int 13 | func notificationBannerTitle(for section: Int) -> String 14 | func blockColor(at indexPath: IndexPath) -> UIColor 15 | func notificationTitles(at indexPath: IndexPath) -> (String, String?) 16 | func notificationImage(at indexPath: IndexPath) -> UIImage? 17 | func basicNotificationCellSelected(at index: Int) 18 | func basicNotificationCellWithSideViewsSelected(at index: Int) 19 | func basicCustomNotificationCellSelected(at index: Int) 20 | func basicGrowingNotificationCellSelected(at index: Int) 21 | func basicFloatingNotificationCellSelected(at index: Int) 22 | func basicSimulanteousFloatingNotificationCellSelected(at index: Int) 23 | func basicStatusBarNotificationCellSelected(at index: Int) 24 | } 25 | 26 | class ExampleView: UIView { 27 | 28 | weak var delegate: ExampleViewDelegate? 29 | var queuePositionSegmentedControl: UISegmentedControl! 30 | var bannerPositionSegmentedControl: UISegmentedControl! 31 | 32 | init(delegate: ExampleViewDelegate) { 33 | super.init(frame: .zero) 34 | self.delegate = delegate 35 | 36 | let queuePositionContentView = UIView() 37 | addSubview(queuePositionContentView) 38 | 39 | let queuePositionSegmentLabel = UILabel() 40 | queuePositionSegmentLabel.font = UIFont.systemFont(ofSize: 12.5) 41 | queuePositionSegmentLabel.text = "Queue Position:" 42 | queuePositionContentView.addSubview(queuePositionSegmentLabel) 43 | 44 | queuePositionSegmentedControl = UISegmentedControl(items: ["Front", "Back"]) 45 | queuePositionSegmentedControl.selectedSegmentIndex = 1 46 | queuePositionContentView.addSubview(queuePositionSegmentedControl) 47 | 48 | let bannerPositionContentView = UIView() 49 | addSubview(bannerPositionContentView) 50 | 51 | let bannerPositionSegmentLabel = UILabel() 52 | bannerPositionSegmentLabel.font = UIFont.systemFont(ofSize: 12.5) 53 | bannerPositionSegmentLabel.text = "Banner Position:" 54 | bannerPositionContentView.addSubview(bannerPositionSegmentLabel) 55 | 56 | bannerPositionSegmentedControl = UISegmentedControl(items: ["Top", "Bottom"]) 57 | bannerPositionSegmentedControl.selectedSegmentIndex = 0 58 | bannerPositionContentView.addSubview(bannerPositionSegmentedControl) 59 | 60 | let tableView = UITableView(frame: .zero, style: .grouped) 61 | tableView.rowHeight = 75.0 62 | tableView.dataSource = self 63 | tableView.delegate = self 64 | addSubview(tableView) 65 | 66 | queuePositionContentView.snp.makeConstraints { (make) in 67 | make.top.equalToSuperview().offset(5) 68 | make.left.equalToSuperview() 69 | make.width.equalToSuperview().multipliedBy(0.5) 70 | make.bottom.equalTo(queuePositionSegmentedControl) 71 | } 72 | 73 | queuePositionSegmentLabel.snp.makeConstraints { (make) in 74 | make.top.equalToSuperview() 75 | make.centerX.equalToSuperview() 76 | } 77 | 78 | queuePositionSegmentedControl.snp.makeConstraints { (make) in 79 | make.top.equalTo(queuePositionSegmentLabel.snp.bottom).offset(3.5) 80 | make.width.equalTo(150) 81 | make.height.equalTo(25) 82 | make.centerX.equalToSuperview() 83 | } 84 | 85 | bannerPositionContentView.snp.makeConstraints { (make) in 86 | make.top.equalTo(queuePositionContentView) 87 | make.left.equalTo(self.snp.centerX) 88 | make.width.equalTo(queuePositionContentView) 89 | make.height.equalTo(queuePositionContentView) 90 | } 91 | 92 | bannerPositionSegmentLabel.snp.makeConstraints { (make) in 93 | make.top.equalToSuperview() 94 | make.centerX.equalToSuperview() 95 | } 96 | 97 | bannerPositionSegmentedControl.snp.makeConstraints { (make) in 98 | make.top.equalTo(queuePositionSegmentedControl) 99 | make.width.equalTo(queuePositionSegmentedControl) 100 | make.height.equalTo(queuePositionSegmentedControl) 101 | make.centerX.equalToSuperview() 102 | } 103 | 104 | tableView.snp.makeConstraints { (make) in 105 | make.top.equalTo(queuePositionContentView.snp.bottom).offset(10) 106 | make.left.equalToSuperview() 107 | make.right.equalToSuperview() 108 | make.bottom.equalToSuperview() 109 | } 110 | 111 | backgroundColor = tableView.backgroundColor 112 | } 113 | 114 | required init?(coder aDecoder: NSCoder) { 115 | fatalError("init(coder:) has not been implemented") 116 | } 117 | 118 | } 119 | 120 | extension ExampleView : UITableViewDataSource { 121 | 122 | func numberOfSections(in tableView: UITableView) -> Int { 123 | return 7 124 | } 125 | 126 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 127 | return delegate!.numberOfCells(for: section) 128 | } 129 | 130 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 131 | return delegate!.notificationBannerTitle(for: section) 132 | } 133 | 134 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 135 | 136 | let reuseIdentifier = "BasicNotificationBannerCell" 137 | 138 | var cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as? BasicNotificationBannerCell 139 | if cell == nil { 140 | cell = BasicNotificationBannerCell(reuseIdentifier: reuseIdentifier) 141 | } 142 | 143 | return cell! 144 | } 145 | } 146 | 147 | extension ExampleView : UITableViewDelegate { 148 | 149 | func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 150 | let (title, subtitle) = delegate!.notificationTitles(at: indexPath) 151 | let blockColor = delegate!.blockColor(at: indexPath) 152 | let image = delegate!.notificationImage(at: indexPath) 153 | if let bannerCell = cell as? BasicNotificationBannerCell { 154 | bannerCell.update(blockColor: blockColor, image: image, title: title, subtitle: subtitle) 155 | } 156 | } 157 | 158 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 159 | tableView.deselectRow(at: indexPath, animated: false) 160 | if indexPath.section == 0 { 161 | delegate?.basicNotificationCellSelected(at: indexPath.row) 162 | } else if indexPath.section == 1 { 163 | delegate?.basicNotificationCellWithSideViewsSelected(at: indexPath.row) 164 | } else if indexPath.section == 2 { 165 | delegate?.basicCustomNotificationCellSelected(at: indexPath.row) 166 | } else if indexPath.section == 3 { 167 | delegate?.basicGrowingNotificationCellSelected(at: indexPath.row) 168 | } else if indexPath.section == 4 { 169 | delegate?.basicFloatingNotificationCellSelected(at: indexPath.row) 170 | } else if indexPath.section == 5 { 171 | delegate?.basicSimulanteousFloatingNotificationCellSelected(at: indexPath.row) 172 | } else if indexPath.section == 6 { 173 | delegate?.basicStatusBarNotificationCellSelected(at: indexPath.row) 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.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 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-App-29x29@1x.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-App-29x29@2x-1.png", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "size" : "29x29", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-App-29x29@3x.png", 29 | "scale" : "3x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-App-40x40@2x-1.png", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "size" : "40x40", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-App-60x60@2x-1.png", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "iphone", 45 | "size" : "57x57", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "iphone", 50 | "size" : "57x57", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "size" : "60x60", 55 | "idiom" : "iphone", 56 | "filename" : "Icon-App-60x60@2x-2.png", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "size" : "60x60", 61 | "idiom" : "iphone", 62 | "filename" : "Icon-App-60x60@3x.png", 63 | "scale" : "3x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "20x20", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "20x20", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@1x-1.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-29x29@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@1x.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-40x40@2x-2.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "idiom" : "ipad", 101 | "size" : "50x50", 102 | "scale" : "1x" 103 | }, 104 | { 105 | "idiom" : "ipad", 106 | "size" : "50x50", 107 | "scale" : "2x" 108 | }, 109 | { 110 | "idiom" : "ipad", 111 | "size" : "72x72", 112 | "scale" : "1x" 113 | }, 114 | { 115 | "idiom" : "ipad", 116 | "size" : "72x72", 117 | "scale" : "2x" 118 | }, 119 | { 120 | "size" : "76x76", 121 | "idiom" : "ipad", 122 | "filename" : "Icon-App-76x76@1x.png", 123 | "scale" : "1x" 124 | }, 125 | { 126 | "size" : "76x76", 127 | "idiom" : "ipad", 128 | "filename" : "Icon-App-76x76@2x.png", 129 | "scale" : "2x" 130 | }, 131 | { 132 | "size" : "83.5x83.5", 133 | "idiom" : "ipad", 134 | "filename" : "Icon-App-60x60@3x-1.png", 135 | "scale" : "2x" 136 | }, 137 | { 138 | "idiom" : "ios-marketing", 139 | "size" : "1024x1024", 140 | "scale" : "1x" 141 | } 142 | ], 143 | "info" : { 144 | "version" : 1, 145 | "author" : "xcode" 146 | } 147 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-1.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-2.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x-1.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/cubs_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cubs_logo.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/cubs_logo.imageset/cubs_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/cubs_logo.imageset/cubs_logo.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/danger.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "danger.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/danger.imageset/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/danger.imageset/danger.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/duke_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "duke_logo.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/duke_logo.imageset/duke_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/duke_logo.imageset/duke_logo.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/info.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "info.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/info.imageset/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/info.imageset/info.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/right_chevron.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "right_chevron.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/right_chevron.imageset/right_chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/right_chevron.imageset/right_chevron.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/success.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "success.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/success.imageset/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/success.imageset/success.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tab_bar_icon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tab_bar_icon@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "tab_bar_icon@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/tab_bar_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/tab_bar_icon.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/tab_bar_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/tab_bar_icon@2x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/tab_bar_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/tab_bar_icon.imageset/tab_bar_icon@3x.png -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/unc_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "unc_logo.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 | } -------------------------------------------------------------------------------- /Example/NotificationBanner/Images.xcassets/unc_logo.imageset/unc_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/Example/NotificationBanner/Images.xcassets/unc_logo.imageset/unc_logo.png -------------------------------------------------------------------------------- /Example/NotificationBanner/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 | 1.0 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 | -------------------------------------------------------------------------------- /Example/NotificationBanner/NorthCarolinaBannerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NorthCarolinaBannerView.swift 3 | // NotificationBanner 4 | // 5 | // Created by Dalton Hinterscher on 3/22/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NorthCarolinaBannerView: UIView { 12 | 13 | init() { 14 | super.init(frame: .zero) 15 | backgroundColor = .white 16 | 17 | let uncView = teamView(image: #imageLiteral(resourceName: "unc_logo"), record: "27-0", onLeft: true) 18 | let dukeView = teamView(image: #imageLiteral(resourceName: "duke_logo"), record: "18-9", onLeft: false) 19 | 20 | let centerView = UIView() 21 | addSubview(centerView) 22 | 23 | let scoreLabel = UILabel() 24 | scoreLabel.font = UIFont.systemFont(ofSize: 25.0) 25 | scoreLabel.text = "98 - 63" 26 | centerView.addSubview(scoreLabel) 27 | 28 | let finalLabel = UILabel() 29 | finalLabel.font = UIFont.boldSystemFont(ofSize: 15.0) 30 | finalLabel.text = "Final" 31 | centerView.addSubview(finalLabel) 32 | 33 | centerView.snp.makeConstraints { (make) in 34 | make.centerY.equalToSuperview() 35 | make.left.equalTo(uncView.snp.right).offset(10) 36 | make.right.equalTo(dukeView.snp.left).offset(-10) 37 | make.bottom.equalTo(finalLabel) 38 | } 39 | 40 | scoreLabel.snp.makeConstraints { (make) in 41 | make.top.equalToSuperview() 42 | make.centerX.equalTo(centerView) 43 | } 44 | 45 | finalLabel.snp.makeConstraints { (make) in 46 | make.top.equalTo(scoreLabel.snp.bottom) 47 | make.centerX.equalTo(centerView) 48 | } 49 | 50 | } 51 | 52 | required init?(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | private func teamView(image: UIImage, record: String, onLeft: Bool) -> UIView { 57 | 58 | let teamView = UIView() 59 | addSubview(teamView) 60 | 61 | let imageView = UIImageView(image: image) 62 | imageView.contentMode = .scaleAspectFit 63 | teamView.addSubview(imageView) 64 | 65 | let recordLabel = UILabel() 66 | recordLabel.font = UIFont.systemFont(ofSize: 12.5) 67 | recordLabel.text = onLeft ? "32-0" : "18-10" 68 | recordLabel.textAlignment = .center 69 | teamView.addSubview(recordLabel) 70 | 71 | 72 | imageView.snp.makeConstraints { (make) in 73 | make.top.equalToSuperview() 74 | make.left.equalToSuperview() 75 | make.right.equalToSuperview() 76 | make.height.equalTo(imageView.snp.width) 77 | } 78 | 79 | recordLabel.snp.makeConstraints { (make) in 80 | make.top.equalTo(imageView.snp.bottom).offset(2.5) 81 | make.left.equalToSuperview() 82 | make.right.equalToSuperview() 83 | } 84 | 85 | teamView.snp.makeConstraints { (make) in 86 | make.centerY.equalToSuperview() 87 | if onLeft { 88 | make.left.equalToSuperview().offset(15) 89 | } else { 90 | make.right.equalToSuperview().offset(-15) 91 | } 92 | make.bottom.equalTo(recordLabel) 93 | make.width.equalTo(40) 94 | } 95 | 96 | return teamView 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2023 Daltron 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /NotificationBanner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0D0D9ADF1F15D19D003B35A8 /* BannerPositionFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D0D9ADE1F15D19D003B35A8 /* BannerPositionFrame.swift */; }; 11 | 2B9A8469216538CA00A7418D /* GrowingNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9A8468216538CA00A7418D /* GrowingNotificationBanner.swift */; }; 12 | 2B9A846B216544E600A7418D /* String+heightForConstrainedWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9A846A216544E600A7418D /* String+heightForConstrainedWidth.swift */; }; 13 | 7ECA5719232FA42B00256BB9 /* FloatingNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECA5718232FA42B00256BB9 /* FloatingNotificationBanner.swift */; }; 14 | 823255AF1EB87313006F95C3 /* BannerColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8232559F1EB87313006F95C3 /* BannerColors.swift */; }; 15 | 823255B01EB87313006F95C3 /* BannerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823255A01EB87313006F95C3 /* BannerStyle.swift */; }; 16 | 823255B11EB87313006F95C3 /* BaseNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823255A11EB87313006F95C3 /* BaseNotificationBanner.swift */; }; 17 | 823255B21EB87313006F95C3 /* NotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823255A21EB87313006F95C3 /* NotificationBanner.swift */; }; 18 | 823255B31EB87313006F95C3 /* NotificationBannerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823255A31EB87313006F95C3 /* NotificationBannerQueue.swift */; }; 19 | 823255B41EB87313006F95C3 /* StatusBarNotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823255A41EB87313006F95C3 /* StatusBarNotificationBanner.swift */; }; 20 | 823255B61EB87313006F95C3 /* NotificationBanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 823255A71EB87313006F95C3 /* NotificationBanner.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | A999043F1EEF64F0006DA132 /* BannerHapticGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A999043E1EEF64F0006DA132 /* BannerHapticGenerator.swift */; }; 22 | BDB71E322BC8508500E946A4 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDB71E312BC8508500E946A4 /* SnapKit */; }; 23 | BDB71E372BC8509E00E946A4 /* MarqueeLabel in Frameworks */ = {isa = PBXBuildFile; productRef = BDB71E362BC8509E00E946A4 /* MarqueeLabel */; }; 24 | BDB71E3B2BC8577200E946A4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = BDB71E3A2BC8577200E946A4 /* PrivacyInfo.xcprivacy */; }; 25 | F63A905B2381423E00F70CEB /* UIWindow+orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F63A90592381423E00F70CEB /* UIWindow+orientation.swift */; }; 26 | F7E13CF61FB192ED0008EE4C /* NotificationBannerUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E13CF51FB192ED0008EE4C /* NotificationBannerUtilities.swift */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 0D0D9ADE1F15D19D003B35A8 /* BannerPositionFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerPositionFrame.swift; sourceTree = ""; }; 31 | 2B9A8468216538CA00A7418D /* GrowingNotificationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrowingNotificationBanner.swift; sourceTree = ""; }; 32 | 2B9A846A216544E600A7418D /* String+heightForConstrainedWidth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+heightForConstrainedWidth.swift"; sourceTree = ""; }; 33 | 7ECA5718232FA42B00256BB9 /* FloatingNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingNotificationBanner.swift; sourceTree = ""; }; 34 | 8232558B1EB872AB006F95C3 /* NotificationBanner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NotificationBanner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 8232559E1EB87313006F95C3 /* .gitkeep */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; 36 | 8232559F1EB87313006F95C3 /* BannerColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerColors.swift; sourceTree = ""; }; 37 | 823255A01EB87313006F95C3 /* BannerStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerStyle.swift; sourceTree = ""; }; 38 | 823255A11EB87313006F95C3 /* BaseNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseNotificationBanner.swift; sourceTree = ""; }; 39 | 823255A21EB87313006F95C3 /* NotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationBanner.swift; sourceTree = ""; }; 40 | 823255A31EB87313006F95C3 /* NotificationBannerQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationBannerQueue.swift; sourceTree = ""; }; 41 | 823255A41EB87313006F95C3 /* StatusBarNotificationBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarNotificationBanner.swift; sourceTree = ""; }; 42 | 823255A61EB87313006F95C3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 823255A71EB87313006F95C3 /* NotificationBanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationBanner.h; sourceTree = ""; }; 44 | 951D609C1F71BF82008E4BCC /* NotificationBannerUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationBannerUtilities.swift; sourceTree = ""; }; 45 | A999043E1EEF64F0006DA132 /* BannerHapticGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerHapticGenerator.swift; sourceTree = ""; }; 46 | BDB71E3A2BC8577200E946A4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 47 | F63A90592381423E00F70CEB /* UIWindow+orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWindow+orientation.swift"; sourceTree = ""; }; 48 | F7E13CF51FB192ED0008EE4C /* NotificationBannerUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationBannerUtilities.swift; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 823255871EB872AB006F95C3 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | BDB71E322BC8508500E946A4 /* SnapKit in Frameworks */, 57 | BDB71E372BC8509E00E946A4 /* MarqueeLabel in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 823255811EB872AB006F95C3 = { 65 | isa = PBXGroup; 66 | children = ( 67 | 8232558D1EB872AB006F95C3 /* NotificationBanner */, 68 | 8232558C1EB872AB006F95C3 /* Products */, 69 | EA9A17881EC75DE000CF2261 /* Frameworks */, 70 | ); 71 | sourceTree = ""; 72 | }; 73 | 8232558C1EB872AB006F95C3 /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 8232558B1EB872AB006F95C3 /* NotificationBanner.framework */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 8232558D1EB872AB006F95C3 /* NotificationBanner */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 8232559D1EB87313006F95C3 /* Classes */, 85 | 823255A51EB87313006F95C3 /* Supporting Files */, 86 | ); 87 | path = NotificationBanner; 88 | sourceTree = ""; 89 | }; 90 | 8232559D1EB87313006F95C3 /* Classes */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | F63A90592381423E00F70CEB /* UIWindow+orientation.swift */, 94 | 0D0D9ADE1F15D19D003B35A8 /* BannerPositionFrame.swift */, 95 | 8232559E1EB87313006F95C3 /* .gitkeep */, 96 | 8232559F1EB87313006F95C3 /* BannerColors.swift */, 97 | A999043E1EEF64F0006DA132 /* BannerHapticGenerator.swift */, 98 | 823255A01EB87313006F95C3 /* BannerStyle.swift */, 99 | 823255A11EB87313006F95C3 /* BaseNotificationBanner.swift */, 100 | 823255A21EB87313006F95C3 /* NotificationBanner.swift */, 101 | 823255A31EB87313006F95C3 /* NotificationBannerQueue.swift */, 102 | 951D609C1F71BF82008E4BCC /* NotificationBannerUtilities.swift */, 103 | 823255A41EB87313006F95C3 /* StatusBarNotificationBanner.swift */, 104 | F7E13CF51FB192ED0008EE4C /* NotificationBannerUtilities.swift */, 105 | 2B9A8468216538CA00A7418D /* GrowingNotificationBanner.swift */, 106 | 7ECA5718232FA42B00256BB9 /* FloatingNotificationBanner.swift */, 107 | 2B9A846A216544E600A7418D /* String+heightForConstrainedWidth.swift */, 108 | ); 109 | path = Classes; 110 | sourceTree = ""; 111 | }; 112 | 823255A51EB87313006F95C3 /* Supporting Files */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | BDB71E3A2BC8577200E946A4 /* PrivacyInfo.xcprivacy */, 116 | 823255A61EB87313006F95C3 /* Info.plist */, 117 | 823255A71EB87313006F95C3 /* NotificationBanner.h */, 118 | ); 119 | path = "Supporting Files"; 120 | sourceTree = ""; 121 | }; 122 | EA9A17881EC75DE000CF2261 /* Frameworks */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | ); 126 | name = Frameworks; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXHeadersBuildPhase section */ 132 | 823255881EB872AB006F95C3 /* Headers */ = { 133 | isa = PBXHeadersBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | 823255B61EB87313006F95C3 /* NotificationBanner.h in Headers */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXHeadersBuildPhase section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 8232558A1EB872AB006F95C3 /* NotificationBanner */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 823255931EB872AB006F95C3 /* Build configuration list for PBXNativeTarget "NotificationBanner" */; 146 | buildPhases = ( 147 | 823255861EB872AB006F95C3 /* Sources */, 148 | 823255871EB872AB006F95C3 /* Frameworks */, 149 | 823255881EB872AB006F95C3 /* Headers */, 150 | 823255891EB872AB006F95C3 /* Resources */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = NotificationBanner; 157 | packageProductDependencies = ( 158 | BDB71E312BC8508500E946A4 /* SnapKit */, 159 | BDB71E362BC8509E00E946A4 /* MarqueeLabel */, 160 | ); 161 | productName = NotificationBanner; 162 | productReference = 8232558B1EB872AB006F95C3 /* NotificationBanner.framework */; 163 | productType = "com.apple.product-type.framework"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 823255821EB872AB006F95C3 /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | BuildIndependentTargetsInParallel = YES; 172 | LastUpgradeCheck = 1530; 173 | ORGANIZATIONNAME = "Dalton Hinterscher"; 174 | TargetAttributes = { 175 | 8232558A1EB872AB006F95C3 = { 176 | CreatedOnToolsVersion = 8.3.2; 177 | ProvisioningStyle = Automatic; 178 | }; 179 | }; 180 | }; 181 | buildConfigurationList = 823255851EB872AB006F95C3 /* Build configuration list for PBXProject "NotificationBanner" */; 182 | compatibilityVersion = "Xcode 3.2"; 183 | developmentRegion = en; 184 | hasScannedForEncodings = 0; 185 | knownRegions = ( 186 | en, 187 | Base, 188 | ); 189 | mainGroup = 823255811EB872AB006F95C3; 190 | packageReferences = ( 191 | BDB71E302BC8508500E946A4 /* XCRemoteSwiftPackageReference "SnapKit" */, 192 | BDB71E352BC8509E00E946A4 /* XCRemoteSwiftPackageReference "MarqueeLabel" */, 193 | ); 194 | productRefGroup = 8232558C1EB872AB006F95C3 /* Products */; 195 | projectDirPath = ""; 196 | projectRoot = ""; 197 | targets = ( 198 | 8232558A1EB872AB006F95C3 /* NotificationBanner */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXResourcesBuildPhase section */ 204 | 823255891EB872AB006F95C3 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | BDB71E3B2BC8577200E946A4 /* PrivacyInfo.xcprivacy in Resources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXResourcesBuildPhase section */ 213 | 214 | /* Begin PBXSourcesBuildPhase section */ 215 | 823255861EB872AB006F95C3 /* Sources */ = { 216 | isa = PBXSourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | F63A905B2381423E00F70CEB /* UIWindow+orientation.swift in Sources */, 220 | 823255B41EB87313006F95C3 /* StatusBarNotificationBanner.swift in Sources */, 221 | 823255B21EB87313006F95C3 /* NotificationBanner.swift in Sources */, 222 | 2B9A846B216544E600A7418D /* String+heightForConstrainedWidth.swift in Sources */, 223 | 0D0D9ADF1F15D19D003B35A8 /* BannerPositionFrame.swift in Sources */, 224 | 7ECA5719232FA42B00256BB9 /* FloatingNotificationBanner.swift in Sources */, 225 | 823255AF1EB87313006F95C3 /* BannerColors.swift in Sources */, 226 | 2B9A8469216538CA00A7418D /* GrowingNotificationBanner.swift in Sources */, 227 | F7E13CF61FB192ED0008EE4C /* NotificationBannerUtilities.swift in Sources */, 228 | 823255B31EB87313006F95C3 /* NotificationBannerQueue.swift in Sources */, 229 | A999043F1EEF64F0006DA132 /* BannerHapticGenerator.swift in Sources */, 230 | 823255B01EB87313006F95C3 /* BannerStyle.swift in Sources */, 231 | 823255B11EB87313006F95C3 /* BaseNotificationBanner.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXSourcesBuildPhase section */ 236 | 237 | /* Begin XCBuildConfiguration section */ 238 | 823255911EB872AB006F95C3 /* Debug */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | ALWAYS_SEARCH_USER_PATHS = NO; 242 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 243 | CLANG_ANALYZER_NONNULL = YES; 244 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 250 | CLANG_WARN_BOOL_CONVERSION = YES; 251 | CLANG_WARN_COMMA = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 254 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 255 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 256 | CLANG_WARN_EMPTY_BODY = YES; 257 | CLANG_WARN_ENUM_CONVERSION = YES; 258 | CLANG_WARN_INFINITE_RECURSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 262 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 264 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 271 | COPY_PHASE_STRIP = NO; 272 | CURRENT_PROJECT_VERSION = 1; 273 | DEBUG_INFORMATION_FORMAT = dwarf; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | ENABLE_TESTABILITY = YES; 276 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_OPTIMIZATION_LEVEL = 0; 281 | GCC_PREPROCESSOR_DEFINITIONS = ( 282 | "DEBUG=1", 283 | "$(inherited)", 284 | ); 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 287 | GCC_WARN_UNDECLARED_SELECTOR = YES; 288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 289 | GCC_WARN_UNUSED_FUNCTION = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 292 | MTL_ENABLE_DEBUG_INFO = YES; 293 | ONLY_ACTIVE_ARCH = YES; 294 | SDKROOT = iphoneos; 295 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 296 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 297 | TARGETED_DEVICE_FAMILY = "1,2"; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | VERSION_INFO_PREFIX = ""; 300 | }; 301 | name = Debug; 302 | }; 303 | 823255921EB872AB006F95C3 /* Release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 308 | CLANG_ANALYZER_NONNULL = YES; 309 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 329 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 331 | CLANG_WARN_STRICT_PROTOTYPES = YES; 332 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | CURRENT_PROJECT_VERSION = 1; 338 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 339 | ENABLE_NS_ASSERTIONS = NO; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | SWIFT_COMPILATION_MODE = wholemodule; 354 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 355 | TARGETED_DEVICE_FAMILY = "1,2"; 356 | VALIDATE_PRODUCT = YES; 357 | VERSIONING_SYSTEM = "apple-generic"; 358 | VERSION_INFO_PREFIX = ""; 359 | }; 360 | name = Release; 361 | }; 362 | 823255941EB872AB006F95C3 /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | CODE_SIGN_IDENTITY = ""; 366 | DEFINES_MODULE = YES; 367 | DYLIB_COMPATIBILITY_VERSION = 1; 368 | DYLIB_CURRENT_VERSION = 1; 369 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 370 | ENABLE_MODULE_VERIFIER = YES; 371 | INFOPLIST_FILE = "$(SRCROOT)/NotificationBanner/Supporting Files/Info.plist"; 372 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 373 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 374 | LD_RUNPATH_SEARCH_PATHS = ( 375 | "$(inherited)", 376 | "@executable_path/Frameworks", 377 | "@loader_path/Frameworks", 378 | ); 379 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; 380 | PRODUCT_BUNDLE_IDENTIFIER = com.dh.NotificationBanner; 381 | PRODUCT_NAME = "$(TARGET_NAME)"; 382 | SKIP_INSTALL = YES; 383 | SWIFT_VERSION = 5.0; 384 | }; 385 | name = Debug; 386 | }; 387 | 823255951EB872AB006F95C3 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | CODE_SIGN_IDENTITY = ""; 391 | DEFINES_MODULE = YES; 392 | DYLIB_COMPATIBILITY_VERSION = 1; 393 | DYLIB_CURRENT_VERSION = 1; 394 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 395 | ENABLE_MODULE_VERIFIER = YES; 396 | INFOPLIST_FILE = "$(SRCROOT)/NotificationBanner/Supporting Files/Info.plist"; 397 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 398 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 399 | LD_RUNPATH_SEARCH_PATHS = ( 400 | "$(inherited)", 401 | "@executable_path/Frameworks", 402 | "@loader_path/Frameworks", 403 | ); 404 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; 405 | PRODUCT_BUNDLE_IDENTIFIER = com.dh.NotificationBanner; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | SKIP_INSTALL = YES; 408 | SWIFT_VERSION = 5.0; 409 | }; 410 | name = Release; 411 | }; 412 | /* End XCBuildConfiguration section */ 413 | 414 | /* Begin XCConfigurationList section */ 415 | 823255851EB872AB006F95C3 /* Build configuration list for PBXProject "NotificationBanner" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 823255911EB872AB006F95C3 /* Debug */, 419 | 823255921EB872AB006F95C3 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | 823255931EB872AB006F95C3 /* Build configuration list for PBXNativeTarget "NotificationBanner" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 823255941EB872AB006F95C3 /* Debug */, 428 | 823255951EB872AB006F95C3 /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | 435 | /* Begin XCRemoteSwiftPackageReference section */ 436 | BDB71E302BC8508500E946A4 /* XCRemoteSwiftPackageReference "SnapKit" */ = { 437 | isa = XCRemoteSwiftPackageReference; 438 | repositoryURL = "https://github.com/SnapKit/SnapKit"; 439 | requirement = { 440 | kind = upToNextMajorVersion; 441 | minimumVersion = 5.7.1; 442 | }; 443 | }; 444 | BDB71E352BC8509E00E946A4 /* XCRemoteSwiftPackageReference "MarqueeLabel" */ = { 445 | isa = XCRemoteSwiftPackageReference; 446 | repositoryURL = "https://github.com/cbpowell/MarqueeLabel"; 447 | requirement = { 448 | kind = upToNextMajorVersion; 449 | minimumVersion = 4.5.0; 450 | }; 451 | }; 452 | /* End XCRemoteSwiftPackageReference section */ 453 | 454 | /* Begin XCSwiftPackageProductDependency section */ 455 | BDB71E312BC8508500E946A4 /* SnapKit */ = { 456 | isa = XCSwiftPackageProductDependency; 457 | package = BDB71E302BC8508500E946A4 /* XCRemoteSwiftPackageReference "SnapKit" */; 458 | productName = SnapKit; 459 | }; 460 | BDB71E362BC8509E00E946A4 /* MarqueeLabel */ = { 461 | isa = XCSwiftPackageProductDependency; 462 | package = BDB71E352BC8509E00E946A4 /* XCRemoteSwiftPackageReference "MarqueeLabel" */; 463 | productName = MarqueeLabel; 464 | }; 465 | /* End XCSwiftPackageProductDependency section */ 466 | }; 467 | rootObject = 823255821EB872AB006F95C3 /* Project object */; 468 | } 469 | -------------------------------------------------------------------------------- /NotificationBanner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NotificationBanner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NotificationBanner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "2cd5226517eb8502063579cd80820b855cacd994f63ff7012771df4d679600f1", 3 | "pins" : [ 4 | { 5 | "identity" : "marqueelabel", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/cbpowell/MarqueeLabel", 8 | "state" : { 9 | "revision" : "877e810534cda9afabb8143ae319b7c3341b121b", 10 | "version" : "4.5.0" 11 | } 12 | }, 13 | { 14 | "identity" : "snapkit", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/SnapKit/SnapKit", 17 | "state" : { 18 | "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", 19 | "version" : "5.7.1" 20 | } 21 | } 22 | ], 23 | "version" : 3 24 | } 25 | -------------------------------------------------------------------------------- /NotificationBanner.xcodeproj/xcshareddata/xcschemes/NotificationBanner.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 | -------------------------------------------------------------------------------- /NotificationBanner/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/.gitkeep -------------------------------------------------------------------------------- /NotificationBanner/Assets/basic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/basic.gif -------------------------------------------------------------------------------- /NotificationBanner/Assets/custom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/custom.gif -------------------------------------------------------------------------------- /NotificationBanner/Assets/floating.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/floating.gif -------------------------------------------------------------------------------- /NotificationBanner/Assets/growing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/growing.gif -------------------------------------------------------------------------------- /NotificationBanner/Assets/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/header.png -------------------------------------------------------------------------------- /NotificationBanner/Assets/side_views.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/side_views.gif -------------------------------------------------------------------------------- /NotificationBanner/Assets/stacked.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/stacked.gif -------------------------------------------------------------------------------- /NotificationBanner/Assets/status_bar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Assets/status_bar.gif -------------------------------------------------------------------------------- /NotificationBanner/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daltron/NotificationBanner/9550d28f111652614e3957e754cde0d7b349bc43/NotificationBanner/Classes/.gitkeep -------------------------------------------------------------------------------- /NotificationBanner/Classes/BannerColors.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | 21 | @objc 22 | public protocol BannerColorsProtocol { 23 | func color(for style: BannerStyle) -> UIColor 24 | } 25 | 26 | public class BannerColors: BannerColorsProtocol { 27 | 28 | public func color(for style: BannerStyle) -> UIColor { 29 | switch style { 30 | case .danger: 31 | return UIColor(red:0.90, green:0.31, blue:0.26, alpha:1.00) 32 | case .info: 33 | return UIColor(red:0.23, green:0.60, blue:0.85, alpha:1.00) 34 | case .customView: 35 | return .clear 36 | case .success: 37 | return UIColor(red:0.22, green:0.80, blue:0.46, alpha:1.00) 38 | case .warning: 39 | return UIColor(red:1.00, green:0.66, blue:0.16, alpha:1.00) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/BannerHapticGenerator.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | 21 | public enum BannerHaptic { 22 | case light 23 | case medium 24 | case heavy 25 | case none 26 | 27 | @available(iOS 10.0, *) 28 | var impactStyle: UIImpactFeedbackGenerator.FeedbackStyle? { 29 | switch self { 30 | case .light: 31 | return .light 32 | case .medium: 33 | return .medium 34 | case .heavy: 35 | return .heavy 36 | case .none: 37 | return nil 38 | } 39 | } 40 | } 41 | 42 | open class BannerHapticGenerator: NSObject { 43 | 44 | /** 45 | Generates a haptic based on the given haptic 46 | -parameter haptic: The haptic strength to generate when a banner is shown 47 | */ 48 | open class func generate(_ haptic: BannerHaptic) { 49 | guard let style = haptic.impactStyle else { return } 50 | let feedbackGenerator = UIImpactFeedbackGenerator(style: style) 51 | feedbackGenerator.prepare() 52 | feedbackGenerator.impactOccurred() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/BannerPositionFrame.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | 21 | @objc 22 | public enum BannerPosition: Int { 23 | case bottom 24 | case top 25 | } 26 | 27 | class BannerPositionFrame { 28 | 29 | private(set) var startFrame: CGRect = .zero 30 | private(set) var endFrame: CGRect = .zero 31 | 32 | init( 33 | bannerPosition: BannerPosition, 34 | bannerWidth: CGFloat, 35 | bannerHeight: CGFloat, 36 | maxY: CGFloat, 37 | finishYOffset: CGFloat = 0, 38 | edgeInsets: UIEdgeInsets? 39 | ) { 40 | 41 | self.startFrame = startFrame( 42 | for: bannerPosition, 43 | bannerWidth: bannerWidth, 44 | bannerHeight: bannerHeight, 45 | maxY: maxY, 46 | edgeInsets: edgeInsets 47 | ) 48 | 49 | self.endFrame = endFrame( 50 | for: bannerPosition, 51 | bannerWidth: bannerWidth, 52 | bannerHeight: bannerHeight, 53 | maxY: maxY, 54 | finishYOffset: finishYOffset, 55 | edgeInsets: edgeInsets 56 | ) 57 | } 58 | 59 | /** 60 | Returns the start frame for the notification banner based on the given banner position 61 | - parameter bannerPosition: The position the notification banners should slide in from 62 | - parameter bannerWidth: The width of the notification banner 63 | - parameter bannerHeight: The height of the notification banner 64 | - parameter maxY: The maximum `y` position the banner can slide in from. This value is only used 65 | if the bannerPosition is .bottom 66 | - parameter edgeInsets: The sides edges insets from superview 67 | */ 68 | private func startFrame( 69 | for bannerPosition: BannerPosition, 70 | bannerWidth: CGFloat, 71 | bannerHeight: CGFloat, 72 | maxY: CGFloat, 73 | edgeInsets: UIEdgeInsets? 74 | ) -> CGRect { 75 | 76 | let edgeInsets = edgeInsets ?? .zero 77 | 78 | switch bannerPosition { 79 | case .bottom: 80 | return CGRect( 81 | x: edgeInsets.left, 82 | y: maxY, 83 | width: bannerWidth - edgeInsets.left - edgeInsets.right, 84 | height: bannerHeight 85 | ) 86 | case .top: 87 | return CGRect( 88 | x: edgeInsets.left, 89 | y: -bannerHeight, 90 | width: bannerWidth - edgeInsets.left - edgeInsets.right, 91 | height: bannerHeight 92 | ) 93 | 94 | } 95 | } 96 | 97 | /** 98 | Returns the end frame for the notification banner based on the given banner position 99 | - parameter bannerPosition: The position the notification banners should slide in from 100 | - parameter bannerWidth: The width of the notification banner 101 | - parameter bannerHeight: The height of the notification banner 102 | - parameter maxY: The maximum `y` position the banner can slide in from. This value is only used if the bannerPosition is .bottom 103 | - parameter finishYOffset: The `y` position offset the banner can slide in. Used for displaying several banenrs simaltaneously 104 | - parameter edgeInsets: The sides edges insets from superview 105 | */ 106 | private func endFrame( 107 | for bannerPosition: BannerPosition, 108 | bannerWidth: CGFloat, 109 | bannerHeight: CGFloat, 110 | maxY: CGFloat, 111 | finishYOffset: CGFloat = 0, 112 | edgeInsets: UIEdgeInsets? 113 | ) -> CGRect { 114 | 115 | let edgeInsets = edgeInsets ?? .zero 116 | 117 | switch bannerPosition { 118 | case .bottom: 119 | return CGRect( 120 | x: edgeInsets.left, 121 | y: maxY - bannerHeight - edgeInsets.bottom - finishYOffset, 122 | width: startFrame.width, 123 | height: startFrame.height) 124 | case .top: 125 | return CGRect( 126 | x: edgeInsets.left, 127 | y: edgeInsets.top + finishYOffset, 128 | width: startFrame.width, 129 | height: startFrame.height 130 | ) 131 | } 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/BannerStyle.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import Foundation 20 | 21 | @objc 22 | public enum BannerStyle: Int { 23 | case danger 24 | case info 25 | case customView 26 | case success 27 | case warning 28 | } 29 | 30 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/BaseNotificationBanner.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | import SnapKit 21 | 22 | import MarqueeLabel 23 | 24 | public protocol NotificationBannerDelegate: AnyObject { 25 | func notificationBannerWillAppear(_ banner: BaseNotificationBanner) 26 | func notificationBannerDidAppear(_ banner: BaseNotificationBanner) 27 | func notificationBannerWillDisappear(_ banner: BaseNotificationBanner) 28 | func notificationBannerDidDisappear(_ banner: BaseNotificationBanner) 29 | } 30 | 31 | @objcMembers 32 | open class BaseNotificationBanner: UIView { 33 | 34 | /// Notification that will be posted when a notification banner will appear 35 | public static let BannerWillAppear: Notification.Name = Notification.Name(rawValue: "NotificationBannerWillAppear") 36 | 37 | /// Notification that will be posted when a notification banner did appear 38 | public static let BannerDidAppear: Notification.Name = Notification.Name(rawValue: "NotificationBannerDidAppear") 39 | 40 | /// Notification that will be posted when a notification banner will appear 41 | public static let BannerWillDisappear: Notification.Name = Notification.Name(rawValue: "NotificationBannerWillDisappear") 42 | 43 | /// Notification that will be posted when a notification banner did appear 44 | public static let BannerDidDisappear: Notification.Name = Notification.Name(rawValue: "NotificationBannerDidDisappear") 45 | 46 | /// Notification banner object key that is included with each Notification 47 | public static let BannerObjectKey: String = "NotificationBannerObjectKey" 48 | 49 | /// The delegate of the notification banner 50 | public weak var delegate: NotificationBannerDelegate? 51 | 52 | /// The style of the notification banner 53 | public let style: BannerStyle 54 | 55 | /// The height of the banner when it is presented 56 | public var bannerHeight: CGFloat { 57 | get { 58 | if let customBannerHeight = customBannerHeight { 59 | return customBannerHeight 60 | } else if shouldAdjustForDynamicIsland() { 61 | return 104.0 62 | } else if shouldAdjustForNotchFeaturedIphone() { 63 | return 88.0 64 | } else { 65 | return 64.0 + heightAdjustment 66 | } 67 | } set { 68 | customBannerHeight = newValue 69 | } 70 | } 71 | 72 | /// The topmost label of the notification if a custom view is not desired 73 | public internal(set) var titleLabel: UILabel? 74 | 75 | /// The time before the notificaiton is automatically dismissed 76 | public var duration: TimeInterval = 5.0 { 77 | didSet { 78 | updateMarqueeLabelsDurations() 79 | } 80 | } 81 | 82 | /// If false, the banner will not be dismissed until the developer programatically dismisses it 83 | public var autoDismiss: Bool = true { 84 | didSet { 85 | if !autoDismiss { 86 | dismissOnTap = false 87 | dismissOnSwipeUp = false 88 | } 89 | } 90 | } 91 | 92 | /// The transparency of the background of the notification banner 93 | public var transparency: CGFloat = 1.0 { 94 | didSet { 95 | if let customView = customView { 96 | customView.backgroundColor = customView.backgroundColor?.withAlphaComponent(transparency) 97 | } else { 98 | let color = backgroundColor 99 | self.backgroundColor = color 100 | } 101 | } 102 | } 103 | 104 | /// The type of haptic to generate when a banner is displayed 105 | public var haptic: BannerHaptic = .heavy 106 | 107 | /// If true, notification will dismissed when tapped 108 | public var dismissOnTap: Bool = true 109 | 110 | /// If true, notification will dismissed when swiped up 111 | public var dismissOnSwipeUp: Bool = true 112 | 113 | /// Closure that will be executed if the notification banner is tapped 114 | public var onTap: (() -> Void)? 115 | 116 | /// Closure that will be executed if the notification banner is swiped up 117 | public var onSwipeUp: (() -> Void)? 118 | 119 | /// Responsible for positioning and auto managing notification banners 120 | public var bannerQueue: NotificationBannerQueue = NotificationBannerQueue.default 121 | 122 | /// Banner show and dimiss animation duration 123 | public var animationDuration: TimeInterval = 0.5 124 | 125 | /// Whether or not the notification banner is currently being displayed 126 | public var isDisplaying: Bool = false 127 | 128 | /// Whether or not to post the default accessibility notification. 129 | public var shouldPostAccessibilityNotification: Bool = true 130 | 131 | /// The view that the notification layout is presented on. The constraints/frame of this should not be changed 132 | internal var contentView: UIView! 133 | 134 | /// A view that helps the spring animation look nice when the banner appears 135 | internal var spacerView: UIView! 136 | 137 | // The custom view inside the notification banner 138 | internal var customView: UIView? 139 | 140 | /// The default offset for spacerView top or bottom 141 | internal var spacerViewDefaultOffset: CGFloat = 10.0 142 | 143 | /// The maximum number of banners simultaneously visible on screen 144 | internal var maximumVisibleBanners: Int = 1 145 | 146 | /// The default padding between edges and views 147 | internal var padding: CGFloat = 15.0 148 | 149 | /// The view controller to display the banner on. This is useful if you are wanting to display a banner underneath a navigation bar 150 | internal weak var parentViewController: UIViewController? 151 | 152 | /// If this is not nil, then this height will be used instead of the auto calculated height 153 | internal var customBannerHeight: CGFloat? 154 | 155 | /// Used by the banner queue to determine wether a notification banner was placed in front of it in the queue 156 | var isSuspended: Bool = false 157 | 158 | /// The main window of the application which banner views are placed on 159 | private let appWindow: UIWindow? = { 160 | if #available(iOS 13.0, *) { 161 | return UIApplication.shared.connectedScenes 162 | .first { $0.activationState == .foregroundActive || $0.activationState == .foregroundInactive } 163 | .map { $0 as? UIWindowScene } 164 | .flatMap { $0?.windows.first } ?? UIApplication.shared.delegate?.window ?? UIApplication.shared.keyWindow 165 | } 166 | 167 | return UIApplication.shared.delegate?.window ?? nil 168 | }() 169 | 170 | /// The position the notification banner should slide in from 171 | private(set) var bannerPosition: BannerPosition = .top 172 | 173 | /// The notification banner sides edges insets from superview. If presented - spacerView color will be transparent 174 | internal var bannerEdgeInsets: UIEdgeInsets? = nil { 175 | didSet { 176 | if bannerEdgeInsets != nil { 177 | spacerView.backgroundColor = .clear 178 | } 179 | } 180 | } 181 | 182 | /// Object that stores the start and end frames for the notification banner based on the provided banner position 183 | internal var bannerPositionFrame: BannerPositionFrame! 184 | 185 | /// The user info that gets passed to each notification 186 | private var notificationUserInfo: [String: BaseNotificationBanner] { 187 | return [BaseNotificationBanner.BannerObjectKey: self] 188 | } 189 | 190 | open override var backgroundColor: UIColor? { 191 | get { 192 | return contentView.backgroundColor 193 | } set { 194 | guard style != .customView else { return } 195 | let color = newValue?.withAlphaComponent(transparency) 196 | contentView.backgroundColor = color 197 | spacerView.backgroundColor = color 198 | } 199 | } 200 | 201 | init(style: BannerStyle, colors: BannerColorsProtocol? = nil) { 202 | self.style = style 203 | super.init(frame: .zero) 204 | 205 | spacerView = UIView() 206 | addSubview(spacerView) 207 | 208 | contentView = UIView() 209 | addSubview(contentView) 210 | 211 | if let colors = colors { 212 | backgroundColor = colors.color(for: style) 213 | } else { 214 | backgroundColor = BannerColors().color(for: style) 215 | } 216 | 217 | let swipeUpGesture = UISwipeGestureRecognizer(target: self, action: #selector(onSwipeUpGestureRecognizer)) 218 | swipeUpGesture.direction = .up 219 | addGestureRecognizer(swipeUpGesture) 220 | } 221 | 222 | required public init?(coder aDecoder: NSCoder) { 223 | fatalError("init(coder:) has not been implemented") 224 | } 225 | 226 | deinit { 227 | NotificationCenter.default.removeObserver( 228 | self, 229 | name: UIDevice.orientationDidChangeNotification, 230 | object: nil 231 | ) 232 | } 233 | 234 | /** 235 | Creates the proper banner constraints based on the desired banner position 236 | */ 237 | private func createBannerConstraints(for bannerPosition: BannerPosition) { 238 | 239 | spacerView.snp.remakeConstraints { (make) in 240 | if bannerPosition == .top { 241 | make.top.equalToSuperview().offset(-spacerViewDefaultOffset) 242 | } else { 243 | make.bottom.equalToSuperview().offset(spacerViewDefaultOffset) 244 | } 245 | make.left.equalToSuperview() 246 | make.right.equalToSuperview() 247 | updateSpacerViewHeight(make: make) 248 | } 249 | 250 | contentView.snp.remakeConstraints { (make) in 251 | if bannerPosition == .top { 252 | make.top.equalTo(spacerView.snp.bottom) 253 | make.bottom.equalToSuperview() 254 | } else { 255 | make.top.equalToSuperview() 256 | make.bottom.equalTo(spacerView.snp.top) 257 | } 258 | 259 | make.left.equalToSuperview() 260 | make.right.equalToSuperview() 261 | } 262 | 263 | } 264 | 265 | /** 266 | Updates the spacer view height. Specifically used for orientation changes. 267 | */ 268 | private func updateSpacerViewHeight(make: ConstraintMaker? = nil) { 269 | let finalHeight = spacerViewHeight() 270 | if let make = make { 271 | make.height.equalTo(finalHeight) 272 | } else { 273 | spacerView.snp.updateConstraints({ (make) in 274 | make.height.equalTo(finalHeight) 275 | }) 276 | } 277 | } 278 | 279 | internal func spacerViewHeight() -> CGFloat { 280 | if shouldAdjustForDynamicIsland() { 281 | return 44.0 282 | } else if shouldAdjustForNotchFeaturedIphone() { 283 | return 40.0 284 | } else { 285 | return 10.0 286 | } 287 | } 288 | 289 | private func finishBannerYOffset() -> CGFloat { 290 | let bannerIndex = (bannerQueue.banners.firstIndex(of: self) ?? bannerQueue.banners.filter { $0.isDisplaying }.count) 291 | 292 | return bannerQueue.banners.prefix(bannerIndex).reduce(0) { $0 293 | + $1.bannerHeight 294 | - (bannerPosition == .top ? spacerViewHeight() : 0) // notch spacer height for top position only 295 | + (bannerPosition == .top ? spacerViewDefaultOffset : -spacerViewDefaultOffset) // to reduct additions in createBannerConstraints (it's needed for proper shadow framing) 296 | + (bannerPosition == .top ? spacerViewDefaultOffset : -spacerViewDefaultOffset) // default space between banners 297 | // this calculations are made only for banners except first one, for first banner it'll be 0 298 | } 299 | } 300 | 301 | internal func updateBannerPositionFrames() { 302 | guard let window = appWindow else { return } 303 | bannerPositionFrame = BannerPositionFrame( 304 | bannerPosition: bannerPosition, 305 | bannerWidth: window.width, 306 | bannerHeight: bannerHeight, 307 | maxY: maximumYPosition(), 308 | finishYOffset: finishBannerYOffset(), 309 | edgeInsets: bannerEdgeInsets 310 | ) 311 | } 312 | 313 | internal func animateUpdatedBannerPositionFrames() { 314 | UIView.animate( 315 | withDuration: animationDuration, 316 | delay: 0.0, 317 | usingSpringWithDamping: 0.7, 318 | initialSpringVelocity: 1, 319 | options: [.curveLinear, .allowUserInteraction], 320 | animations: { 321 | self.frame = self.bannerPositionFrame.endFrame 322 | }) 323 | } 324 | 325 | /** 326 | Places a NotificationBanner on the queue and shows it if its the first one in the queue 327 | - parameter queuePosition: The position to show the notification banner. If the position is .front, the 328 | banner will be displayed immediately 329 | - parameter bannerPosition: The position the notification banner should slide in from 330 | - parameter queue: The queue to display the notification banner on. It is up to the developer 331 | to manage multiple banner queues and prevent any conflicts that may occur. 332 | - parameter viewController: The view controller to display the notifification banner on. If nil, it will 333 | be placed on the main app window 334 | */ 335 | public func show( 336 | queuePosition: QueuePosition = .back, 337 | bannerPosition: BannerPosition = .top, 338 | queue: NotificationBannerQueue = NotificationBannerQueue.default, 339 | on viewController: UIViewController? = nil 340 | ) { 341 | parentViewController = viewController 342 | bannerQueue = queue 343 | show( 344 | placeOnQueue: true, 345 | queuePosition: queuePosition, 346 | bannerPosition: bannerPosition 347 | ) 348 | } 349 | 350 | /** 351 | Places a NotificationBanner on the queue and shows it if its the first one in the queue 352 | - parameter placeOnQueue: If false, banner will not be placed on the queue and will be showed/resumed immediately 353 | - parameter queuePosition: The position to show the notification banner. If the position is .front, the 354 | banner will be displayed immediately 355 | - parameter bannerPosition: The position the notification banner should slide in from 356 | */ 357 | func show( 358 | placeOnQueue: Bool, 359 | queuePosition: QueuePosition = .back, 360 | bannerPosition: BannerPosition = .top 361 | ) { 362 | 363 | guard !isDisplaying else { 364 | return 365 | } 366 | 367 | self.bannerPosition = bannerPosition 368 | createBannerConstraints(for: bannerPosition) 369 | updateBannerPositionFrames() 370 | 371 | NotificationCenter.default.removeObserver( 372 | self, 373 | name: UIDevice.orientationDidChangeNotification, 374 | object: nil 375 | ) 376 | 377 | NotificationCenter.default.addObserver( 378 | self, 379 | selector: #selector(onOrientationChanged), 380 | name: UIDevice.orientationDidChangeNotification, 381 | object: nil 382 | ) 383 | 384 | if placeOnQueue { 385 | bannerQueue.addBanner( 386 | self, 387 | bannerPosition: bannerPosition, 388 | queuePosition: queuePosition 389 | ) 390 | } else { 391 | guard bannerPositionFrame != nil else { 392 | remove(); 393 | return 394 | } 395 | 396 | self.frame = bannerPositionFrame.startFrame 397 | 398 | if let parentViewController = parentViewController, parentViewController.view != nil { 399 | parentViewController.view.addSubview(self) 400 | if statusBarShouldBeShown() { 401 | appWindow?.windowLevel = UIWindow.Level.normal 402 | } 403 | } else { 404 | appWindow?.addSubview(self) 405 | if statusBarShouldBeShown() && !(parentViewController == nil && bannerPosition == .top) { 406 | appWindow?.windowLevel = UIWindow.Level.normal 407 | } else { 408 | appWindow?.windowLevel = UIWindow.Level.statusBar + 1 409 | } 410 | } 411 | 412 | NotificationCenter.default.post( 413 | name: BaseNotificationBanner.BannerWillAppear, 414 | object: self, 415 | userInfo: notificationUserInfo 416 | ) 417 | 418 | delegate?.notificationBannerWillAppear(self) 419 | 420 | if self.shouldPostAccessibilityNotification { 421 | postAccessibilityNotification() 422 | } 423 | 424 | let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.onTapGestureRecognizer)) 425 | self.addGestureRecognizer(tapGestureRecognizer) 426 | 427 | self.isDisplaying = true 428 | 429 | let bannerIndex = Double(bannerQueue.banners.firstIndex(of: self) ?? 0) + 1 430 | UIView.animate( 431 | withDuration: animationDuration * bannerIndex, 432 | delay: 0.0, 433 | usingSpringWithDamping: 0.7, 434 | initialSpringVelocity: 1, 435 | options: [.curveLinear, .allowUserInteraction], 436 | animations: { 437 | BannerHapticGenerator.generate(self.haptic) 438 | self.frame = self.bannerPositionFrame.endFrame 439 | }) { (completed) in 440 | 441 | NotificationCenter.default.post( 442 | name: BaseNotificationBanner.BannerDidAppear, 443 | object: self, 444 | userInfo: self.notificationUserInfo 445 | ) 446 | 447 | self.delegate?.notificationBannerDidAppear(self) 448 | 449 | /* We don't want to add the selector if another banner was queued in front of it 450 | before it finished animating or if it is meant to be shown infinitely 451 | */ 452 | if !self.isSuspended && self.autoDismiss { 453 | self.perform( 454 | #selector(self.dismiss), 455 | with: nil, 456 | afterDelay: self.duration 457 | ) 458 | } 459 | } 460 | } 461 | } 462 | 463 | /** 464 | Suspends a notification banner so it will not be dismissed. This happens because a new notification banner was placed in front of it on the queue. 465 | */ 466 | func suspend() { 467 | if autoDismiss { 468 | NSObject.cancelPreviousPerformRequests( 469 | withTarget: self, 470 | selector: #selector(dismiss), 471 | object: nil 472 | ) 473 | isSuspended = true 474 | isDisplaying = false 475 | } 476 | } 477 | 478 | /** 479 | Resumes a notification banner immediately. 480 | */ 481 | func resume() { 482 | if autoDismiss { 483 | self.perform( 484 | #selector(dismiss), 485 | with: nil, 486 | afterDelay: self.duration 487 | ) 488 | isSuspended = false 489 | isDisplaying = true 490 | } 491 | } 492 | 493 | /** 494 | Resets a notification banner's elapsed duration to zero. 495 | */ 496 | public func resetDuration() { 497 | if autoDismiss { 498 | NSObject.cancelPreviousPerformRequests( 499 | withTarget: self, 500 | selector: #selector(dismiss), 501 | object: nil 502 | ) 503 | 504 | self.perform(#selector(dismiss), with: nil, afterDelay: self.duration) 505 | } 506 | } 507 | 508 | /** 509 | The height adjustment needed in order for the banner to look properly displayed. 510 | */ 511 | internal var heightAdjustment: CGFloat { 512 | if NotificationBannerUtilities.hasDynamicIsland() { 513 | return 16.0 514 | } 515 | 516 | // iOS 13 does not allow covering the status bar on non-notch iPhones 517 | // The banner needs to be moved further down under the status bar in this case 518 | guard #available(iOS 13.0, *), !NotificationBannerUtilities.isNotchFeaturedIPhone() else { 519 | return 0 520 | } 521 | 522 | return UIApplication.shared.statusBarFrame.height 523 | } 524 | 525 | /** 526 | Update banner height, it's necessary after banner labels font update 527 | */ 528 | internal func updateBannerHeight() { 529 | onOrientationChanged() 530 | } 531 | 532 | /** 533 | Changes the frame of the notification banner when the orientation of the device changes 534 | */ 535 | @objc private dynamic func onOrientationChanged() { 536 | guard let window = appWindow, 537 | currentDeviceOrientationIsSupportedByApp() else { return } 538 | 539 | updateSpacerViewHeight() 540 | 541 | let edgeInsets = bannerEdgeInsets ?? .zero 542 | 543 | let newY = (bannerPosition == .top) ? (frame.origin.y) : (window.height - bannerHeight + edgeInsets.top - edgeInsets.bottom) 544 | 545 | frame = CGRect( 546 | x: frame.origin.x, 547 | y: newY, 548 | width: window.width - edgeInsets.left - edgeInsets.right, 549 | height: bannerHeight 550 | ) 551 | 552 | bannerPositionFrame = BannerPositionFrame( 553 | bannerPosition: bannerPosition, 554 | bannerWidth: window.width, 555 | bannerHeight: bannerHeight, 556 | maxY: maximumYPosition(), 557 | finishYOffset: finishBannerYOffset(), 558 | edgeInsets: bannerEdgeInsets 559 | ) 560 | } 561 | 562 | /** 563 | Dismisses the NotificationBanner and shows the next one if there is one to show on the queue 564 | */ 565 | @objc public func dismiss(forced: Bool = false) { 566 | 567 | guard isDisplaying else { 568 | return 569 | } 570 | 571 | NSObject.cancelPreviousPerformRequests( 572 | withTarget: self, 573 | selector: #selector(dismiss), 574 | object: nil 575 | ) 576 | 577 | NotificationCenter.default.post( 578 | name: BaseNotificationBanner.BannerWillDisappear, 579 | object: self, 580 | userInfo: notificationUserInfo 581 | ) 582 | 583 | delegate?.notificationBannerWillDisappear(self) 584 | 585 | isDisplaying = false 586 | remove() 587 | 588 | // Prevent any user action from showing an additional animation 589 | self.bannerQueue.activeAnimation = true 590 | 591 | UIView.animate( 592 | withDuration: forced ? animationDuration / 2 : animationDuration, 593 | animations: { 594 | self.frame = self.bannerPositionFrame.startFrame 595 | }) { (completed) in 596 | self.bannerQueue.activeAnimation = false 597 | 598 | self.removeFromSuperview() 599 | 600 | NotificationCenter.default.post( 601 | name: BaseNotificationBanner.BannerDidDisappear, 602 | object: self, 603 | userInfo: self.notificationUserInfo 604 | ) 605 | 606 | self.delegate?.notificationBannerDidDisappear(self) 607 | 608 | self.bannerQueue.showNext(callback: { (isEmpty) in 609 | if isEmpty || self.statusBarShouldBeShown() { 610 | self.appWindow?.windowLevel = UIWindow.Level.normal 611 | } 612 | }) 613 | } 614 | } 615 | 616 | /** 617 | Removes the NotificationBanner from the queue if not displaying 618 | */ 619 | public func remove() { 620 | 621 | guard !isDisplaying else { 622 | return 623 | } 624 | 625 | bannerQueue.removeBanner(self) 626 | } 627 | 628 | /** 629 | Called when a notification banner is tapped 630 | */ 631 | @objc private dynamic func onTapGestureRecognizer() { 632 | if dismissOnTap { 633 | dismiss() 634 | } 635 | 636 | onTap?() 637 | } 638 | 639 | /** 640 | Called when a notification banner is swiped up 641 | */ 642 | @objc private dynamic func onSwipeUpGestureRecognizer() { 643 | if dismissOnSwipeUp { 644 | dismiss() 645 | } 646 | 647 | onSwipeUp?() 648 | } 649 | 650 | 651 | /** 652 | Determines wether or not the status bar should be shown when displaying 653 | a banner underneath the navigation bar 654 | */ 655 | private func statusBarShouldBeShown() -> Bool { 656 | 657 | for banner in bannerQueue.banners { 658 | if (banner.parentViewController == nil && banner.bannerPosition == .top) { 659 | return false 660 | } 661 | } 662 | 663 | return true 664 | } 665 | 666 | /** 667 | Determines wether or not the current orientation that the device is in 668 | is supported by the current application. 669 | */ 670 | private func currentDeviceOrientationIsSupportedByApp() -> Bool { 671 | let supportedOrientations = UIApplication.shared.supportedInterfaceOrientations(for: appWindow) 672 | 673 | switch UIDevice.current.orientation { 674 | case .portrait: 675 | return supportedOrientations.contains(.portrait) 676 | case .portraitUpsideDown: 677 | return supportedOrientations.contains(.portraitUpsideDown) 678 | case .landscapeLeft: 679 | return supportedOrientations.contains(.landscapeLeft) 680 | case .landscapeRight: 681 | return supportedOrientations.contains(.landscapeRight) 682 | default: 683 | return false 684 | } 685 | } 686 | 687 | /** 688 | Calculates the maximum `y` position that a notification banner can slide in from 689 | */ 690 | 691 | private func maximumYPosition() -> CGFloat { 692 | if let parentViewController = parentViewController { 693 | return parentViewController.view.frame.height 694 | } else { 695 | return appWindow?.height ?? 0 696 | } 697 | } 698 | 699 | /** 700 | Determines wether or not we should adjust the banner for notch featured iPhone 701 | */ 702 | 703 | internal func shouldAdjustForDynamicIsland() -> Bool { 704 | return NotificationBannerUtilities.hasDynamicIsland() 705 | && UIApplication.shared.statusBarOrientation.isPortrait 706 | && (self.parentViewController?.navigationController?.isNavigationBarHidden ?? true) 707 | } 708 | 709 | internal func shouldAdjustForNotchFeaturedIphone() -> Bool { 710 | return NotificationBannerUtilities.isNotchFeaturedIPhone() 711 | && UIApplication.shared.statusBarOrientation.isPortrait 712 | && (self.parentViewController?.navigationController?.isNavigationBarHidden ?? true) 713 | } 714 | /** 715 | Updates the scrolling marquee label duration 716 | */ 717 | internal func updateMarqueeLabelsDurations() { 718 | (titleLabel as? MarqueeLabel)?.speed = .duration(CGFloat(duration <= 3 ? 0.5 : duration - 3)) 719 | } 720 | 721 | 722 | /** 723 | Posts a `UIAccessibility` notification when a notification appears. 724 | */ 725 | private func postAccessibilityNotification() { 726 | var bannerAccessibilityLabel: String? = nil 727 | switch self { 728 | case let banner as NotificationBanner: 729 | if let title = banner.titleLabel?.text, let subtitle = banner.subtitleLabel?.text { 730 | bannerAccessibilityLabel = "\(title) \(subtitle)" 731 | } 732 | case let banner as FloatingNotificationBanner: 733 | if let title = banner.titleLabel?.text, let subtitle = banner.subtitleLabel?.text { 734 | bannerAccessibilityLabel = "\(title) \(subtitle)" 735 | } 736 | case let banner as GrowingNotificationBanner: 737 | if let title = banner.titleLabel?.text, let subtitle = banner.subtitleLabel?.text { 738 | bannerAccessibilityLabel = "\(title) \(subtitle)" 739 | } 740 | default: 741 | break 742 | } 743 | accessibilityLabel = bannerAccessibilityLabel 744 | isAccessibilityElement = true 745 | UIAccessibility.post(notification: .screenChanged, argument: self) 746 | } 747 | } 748 | 749 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/FloatingNotificationBanner.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2018 Denis Kozhukhov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | import SnapKit 21 | 22 | open class FloatingNotificationBanner: GrowingNotificationBanner { 23 | 24 | public init( 25 | title: String? = nil, 26 | subtitle: String? = nil, 27 | titleFont: UIFont? = nil, 28 | titleColor: UIColor? = nil, 29 | titleTextAlign: NSTextAlignment? = nil, 30 | subtitleFont: UIFont? = nil, 31 | subtitleColor: UIColor? = nil, 32 | subtitleTextAlign: NSTextAlignment? = nil, 33 | leftView: UIView? = nil, 34 | rightView: UIView? = nil, 35 | style: BannerStyle = .info, 36 | colors: BannerColorsProtocol? = nil, 37 | iconPosition: IconPosition = .center 38 | ) { 39 | 40 | super.init( 41 | title: title, 42 | subtitle: subtitle, 43 | leftView: leftView, 44 | rightView: rightView, 45 | style: style, 46 | colors: colors, 47 | iconPosition: iconPosition 48 | ) 49 | 50 | if let titleFont = titleFont { 51 | self.titleFont = titleFont 52 | titleLabel!.font = titleFont 53 | } 54 | 55 | if let titleColor = titleColor { 56 | titleLabel!.textColor = titleColor 57 | } 58 | 59 | if let titleTextAlign = titleTextAlign { 60 | titleLabel!.textAlignment = titleTextAlign 61 | } 62 | 63 | if let subtitleFont = subtitleFont { 64 | self.subtitleFont = subtitleFont 65 | subtitleLabel!.font = subtitleFont 66 | } 67 | 68 | if let subtitleColor = subtitleColor { 69 | subtitleLabel!.textColor = subtitleColor 70 | } 71 | 72 | if let subtitleTextAlign = subtitleTextAlign { 73 | subtitleLabel!.textAlignment = subtitleTextAlign 74 | } 75 | } 76 | 77 | public init(customView: UIView) { 78 | super.init(style: .customView) 79 | self.customView = customView 80 | 81 | contentView.addSubview(customView) 82 | customView.snp.makeConstraints { (make) in 83 | make.edges.equalTo(contentView) 84 | } 85 | 86 | spacerView.backgroundColor = customView.backgroundColor 87 | } 88 | 89 | /** 90 | Convenience function to display banner with non .zero default edge insets 91 | */ 92 | public func show( 93 | queuePosition: QueuePosition = .back, 94 | bannerPosition: BannerPosition = .top, 95 | queue: NotificationBannerQueue = NotificationBannerQueue.default, 96 | on viewController: UIViewController? = nil, 97 | edgeInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), 98 | cornerRadius: CGFloat? = nil, 99 | shadowColor: UIColor = .black, 100 | shadowOpacity: CGFloat = 1, 101 | shadowBlurRadius: CGFloat = 0, 102 | shadowCornerRadius: CGFloat = 0, 103 | shadowOffset: UIOffset = .zero, 104 | shadowEdgeInsets: UIEdgeInsets? = nil 105 | ) { 106 | 107 | self.bannerEdgeInsets = edgeInsets 108 | 109 | if let cornerRadius = cornerRadius { 110 | contentView.layer.cornerRadius = cornerRadius 111 | contentView.subviews.last?.layer.cornerRadius = cornerRadius 112 | } 113 | 114 | if style == .customView, let customView = contentView.subviews.last { 115 | customView.backgroundColor = customView.backgroundColor?.withAlphaComponent(transparency) 116 | } 117 | 118 | show( 119 | queuePosition: queuePosition, 120 | bannerPosition: bannerPosition, 121 | queue: queue, 122 | on: viewController 123 | ) 124 | 125 | applyShadow( 126 | withColor: shadowColor, 127 | opacity: shadowOpacity, 128 | blurRadius: shadowBlurRadius, 129 | cornerRadius: shadowCornerRadius, 130 | offset: shadowOffset, 131 | edgeInsets: shadowEdgeInsets 132 | ) 133 | 134 | } 135 | 136 | required public init?(coder aDecoder: NSCoder) { 137 | fatalError("init(coder:) has not been implemented") 138 | } 139 | 140 | } 141 | 142 | private extension FloatingNotificationBanner { 143 | 144 | /** 145 | Add shadow for notification with specified parameters. 146 | */ 147 | private func applyShadow( 148 | withColor color: UIColor = .black, 149 | opacity: CGFloat = 1, 150 | blurRadius: CGFloat = 0, 151 | cornerRadius: CGFloat = 0, 152 | offset: UIOffset = .zero, 153 | edgeInsets: UIEdgeInsets? = nil 154 | ) { 155 | 156 | guard blurRadius >= 0 else { return } 157 | 158 | contentView.layer.shadowColor = color.cgColor 159 | contentView.layer.shadowOpacity = Float(opacity) 160 | contentView.layer.shadowRadius = blurRadius 161 | contentView.layer.shadowOffset = CGSize(width: offset.horizontal, height: offset.vertical) 162 | 163 | if let edgeInsets = edgeInsets { 164 | var shadowRect = CGRect(origin: .zero, size: bannerPositionFrame.startFrame.size) 165 | shadowRect.size.height -= (spacerViewHeight() - spacerViewDefaultOffset) // to proper handle spacer height affects 166 | shadowRect.origin.x += edgeInsets.left 167 | shadowRect.origin.y += edgeInsets.top 168 | shadowRect.size.width -= (edgeInsets.left + edgeInsets.right) 169 | shadowRect.size.height -= (edgeInsets.top + edgeInsets.bottom) 170 | contentView.layer.shadowPath = UIBezierPath(roundedRect: shadowRect, cornerRadius: cornerRadius).cgPath 171 | } 172 | 173 | contentView.layer.rasterizationScale = UIScreen.main.scale 174 | contentView.layer.shouldRasterize = true 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/GrowingNotificationBanner.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | import SnapKit 21 | 22 | open class GrowingNotificationBanner: BaseNotificationBanner { 23 | 24 | public enum IconPosition { 25 | case top 26 | case center 27 | } 28 | 29 | /// The height of the banner when it is presented 30 | override public var bannerHeight: CGFloat { 31 | get { 32 | if let customBannerHeight = customBannerHeight { 33 | return customBannerHeight 34 | } else { 35 | // Calculate the height based on contents of labels 36 | 37 | // Determine available width for displaying the label 38 | var boundingWidth = UIScreen.main.bounds.width - padding * 2 39 | 40 | // Substract safeAreaInsets from width, if available 41 | // We have to use keyWindow to ask for safeAreaInsets as `self` only knows its' safeAreaInsets in layoutSubviews 42 | if #available(iOS 11.0, *), let keyWindow = UIApplication.shared.keyWindow { 43 | let safeAreaOffset = keyWindow.safeAreaInsets.left + keyWindow.safeAreaInsets.right 44 | 45 | boundingWidth -= safeAreaOffset 46 | } 47 | 48 | if leftView != nil { 49 | boundingWidth -= sideViewSize + padding 50 | } 51 | 52 | if rightView != nil { 53 | boundingWidth -= sideViewSize + padding 54 | } 55 | 56 | let titleHeight = ceil(titleLabel?.sizeThatFits( 57 | CGSize(width: boundingWidth, 58 | height: .greatestFiniteMagnitude)).height ?? 0.0) 59 | 60 | let subtitleHeight = ceil(subtitleLabel?.sizeThatFits( 61 | CGSize(width: boundingWidth, 62 | height: .greatestFiniteMagnitude)).height ?? 0.0) 63 | 64 | let topOffset: CGFloat 65 | let minHeight: CGFloat 66 | 67 | if shouldAdjustForNotchFeaturedIphone() { 68 | topOffset = 44.0 69 | minHeight = 88.0 70 | } else if shouldAdjustForDynamicIsland() { 71 | topOffset = 44.0 72 | minHeight = 104.0 73 | } else { 74 | topOffset = verticalSpacing 75 | minHeight = 64.0 76 | } 77 | 78 | var actualBannerHeight = topOffset + titleHeight + subtitleHeight + verticalSpacing 79 | 80 | if !subtitleHeight.isZero && !titleHeight.isZero { 81 | actualBannerHeight += innerSpacing 82 | } 83 | 84 | return heightAdjustment + max(actualBannerHeight, minHeight) 85 | } 86 | } set { 87 | customBannerHeight = newValue 88 | } 89 | } 90 | 91 | /// Spacing between the last label and the bottom edge of the banner 92 | private let verticalSpacing: CGFloat = 14.0 93 | 94 | /// Spacing between title and subtitle 95 | private let innerSpacing: CGFloat = 2.5 96 | 97 | /// The bottom most label of the notification if a subtitle is provided 98 | public internal(set) var subtitleLabel: UILabel? 99 | 100 | /// The view that is presented on the left side of the notification 101 | private var leftView: UIView? 102 | 103 | /// The view that is presented on the right side of the notification 104 | private var rightView: UIView? 105 | 106 | /// Square size for left/right view if set 107 | private let sideViewSize: CGFloat 108 | 109 | /// Font used for the title label 110 | internal var titleFont: UIFont = UIFont.systemFont(ofSize: 17.5, weight: UIFont.Weight.bold) 111 | 112 | /// Font used for the subtitle label 113 | internal var subtitleFont: UIFont = UIFont.systemFont(ofSize: 15.0) 114 | 115 | public init( 116 | title: String? = nil, 117 | subtitle: String? = nil, 118 | leftView: UIView? = nil, 119 | rightView: UIView? = nil, 120 | style: BannerStyle = .info, 121 | colors: BannerColorsProtocol? = nil, 122 | iconPosition: IconPosition = .center, 123 | sideViewSize: CGFloat = 24.0 124 | ) { 125 | 126 | self.leftView = leftView 127 | self.rightView = rightView 128 | self.sideViewSize = sideViewSize 129 | 130 | super.init(style: style, colors: colors) 131 | 132 | let labelsView = UIStackView() 133 | labelsView.axis = .vertical 134 | labelsView.spacing = innerSpacing 135 | 136 | let outerStackView = UIStackView() 137 | outerStackView.spacing = padding 138 | 139 | switch iconPosition { 140 | case .top: 141 | outerStackView.alignment = .top 142 | case .center: 143 | outerStackView.alignment = .center 144 | } 145 | 146 | if let leftView = leftView { 147 | outerStackView.addArrangedSubview(leftView) 148 | leftView.snp.makeConstraints { $0.size.equalTo(sideViewSize) } 149 | } 150 | 151 | outerStackView.addArrangedSubview(labelsView) 152 | 153 | if let title = title { 154 | titleLabel = UILabel() 155 | titleLabel!.font = titleFont 156 | titleLabel!.numberOfLines = 0 157 | titleLabel!.textColor = .white 158 | titleLabel!.text = title 159 | titleLabel!.setContentHuggingPriority(.required, for: .vertical) 160 | labelsView.addArrangedSubview(titleLabel!) 161 | } 162 | 163 | if let subtitle = subtitle { 164 | subtitleLabel = UILabel() 165 | subtitleLabel!.font = subtitleFont 166 | subtitleLabel!.numberOfLines = 0 167 | subtitleLabel!.textColor = .white 168 | subtitleLabel!.text = subtitle 169 | if title == nil { 170 | subtitleLabel!.setContentHuggingPriority(.required, for: .vertical) 171 | } 172 | labelsView.addArrangedSubview(subtitleLabel!) 173 | } 174 | 175 | if let rightView = rightView { 176 | outerStackView.addArrangedSubview(rightView) 177 | rightView.snp.makeConstraints { $0.size.equalTo(sideViewSize) } 178 | } 179 | 180 | contentView.addSubview(outerStackView) 181 | outerStackView.snp.makeConstraints { (make) in 182 | if #available(iOS 11.0, *) { 183 | make.left.equalTo(safeAreaLayoutGuide).offset(padding) 184 | make.right.equalTo(safeAreaLayoutGuide).offset(-padding) 185 | } else { 186 | make.left.equalToSuperview().offset(padding) 187 | make.right.equalToSuperview().offset(-padding) 188 | } 189 | 190 | make.centerY.equalToSuperview() 191 | } 192 | } 193 | 194 | required public init?(coder aDecoder: NSCoder) { 195 | fatalError("init(coder:) has not been implemented") 196 | } 197 | 198 | override func spacerViewHeight() -> CGFloat { 199 | return super.spacerViewHeight() + heightAdjustment 200 | } 201 | } 202 | 203 | public extension GrowingNotificationBanner { 204 | 205 | func applyStyling( 206 | cornerRadius: CGFloat? = nil, 207 | titleFont: UIFont? = nil, 208 | titleColor: UIColor? = nil, 209 | titleTextAlign: NSTextAlignment? = nil, 210 | subtitleFont: UIFont? = nil, 211 | subtitleColor: UIColor? = nil, 212 | subtitleTextAlign: NSTextAlignment? = nil 213 | ) { 214 | 215 | if let cornerRadius = cornerRadius { 216 | contentView.layer.cornerRadius = cornerRadius 217 | } 218 | 219 | if let titleFont = titleFont { 220 | self.titleFont = titleFont 221 | titleLabel!.font = titleFont 222 | } 223 | 224 | if let titleColor = titleColor { 225 | titleLabel!.textColor = titleColor 226 | } 227 | 228 | if let titleTextAlign = titleTextAlign { 229 | titleLabel!.textAlignment = titleTextAlign 230 | } 231 | 232 | if let subtitleFont = subtitleFont { 233 | self.subtitleFont = subtitleFont 234 | subtitleLabel!.font = subtitleFont 235 | } 236 | 237 | if let subtitleColor = subtitleColor { 238 | subtitleLabel!.textColor = subtitleColor 239 | } 240 | 241 | if let subtitleTextAlign = subtitleTextAlign { 242 | subtitleLabel!.textAlignment = subtitleTextAlign 243 | } 244 | 245 | if titleFont != nil || subtitleFont != nil { 246 | updateBannerHeight() 247 | } 248 | } 249 | 250 | } 251 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/NotificationBanner.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | import SnapKit 21 | 22 | import MarqueeLabel 23 | 24 | @objcMembers 25 | open class NotificationBanner: BaseNotificationBanner { 26 | 27 | /// The bottom most label of the notification if a subtitle is provided 28 | public internal(set) var subtitleLabel: MarqueeLabel? 29 | 30 | /// The view that is presented on the left side of the notification 31 | private var leftView: UIView? 32 | 33 | /// The view that is presented on the right side of the notification 34 | private var rightView: UIView? 35 | 36 | /// Font used for the title label 37 | private var titleFont: UIFont = UIFont.systemFont(ofSize: 17.5, weight: UIFont.Weight.bold) 38 | 39 | /// Font used for the subtitle label 40 | private var subtitleFont: UIFont = UIFont.systemFont(ofSize: 15.0) 41 | 42 | public init( 43 | title: String? = nil, 44 | subtitle: String? = nil, 45 | leftView: UIView? = nil, 46 | rightView: UIView? = nil, 47 | style: BannerStyle = .info, 48 | colors: BannerColorsProtocol? = nil 49 | ) { 50 | 51 | super.init(style: style, colors: colors) 52 | 53 | if let leftView = leftView { 54 | contentView.addSubview(leftView) 55 | 56 | let size = (leftView.frame.height > 0) ? min(44, leftView.frame.height) : 44 57 | 58 | leftView.snp.makeConstraints({ (make) in 59 | make.centerY.equalToSuperview().offset(heightAdjustment / 4) 60 | make.left.equalToSuperview().offset(10) 61 | make.size.equalTo(size) 62 | }) 63 | } 64 | 65 | if let rightView = rightView { 66 | contentView.addSubview(rightView) 67 | 68 | let size = (rightView.frame.height > 0) ? min(44, rightView.frame.height) : 44 69 | rightView.snp.makeConstraints({ (make) in 70 | make.centerY.equalToSuperview().offset(heightAdjustment / 4) 71 | make.right.equalToSuperview().offset(-10) 72 | make.size.equalTo(size) 73 | }) 74 | } 75 | 76 | let labelsView = UIView() 77 | contentView.addSubview(labelsView) 78 | 79 | if let title = title { 80 | titleLabel = MarqueeLabel() 81 | (titleLabel as! MarqueeLabel).type = .left 82 | titleLabel!.font = titleFont 83 | titleLabel!.textColor = .white 84 | titleLabel!.text = title 85 | labelsView.addSubview(titleLabel!) 86 | 87 | titleLabel!.snp.makeConstraints { (make) in 88 | make.top.equalToSuperview() 89 | make.left.equalToSuperview() 90 | make.right.equalToSuperview() 91 | if let _ = subtitle { 92 | titleLabel!.numberOfLines = 1 93 | } else { 94 | titleLabel!.numberOfLines = 2 95 | } 96 | } 97 | } 98 | 99 | if let subtitle = subtitle { 100 | subtitleLabel = MarqueeLabel() 101 | subtitleLabel!.type = .left 102 | subtitleLabel!.font = subtitleFont 103 | subtitleLabel!.numberOfLines = 1 104 | subtitleLabel!.textColor = .white 105 | subtitleLabel!.text = subtitle 106 | labelsView.addSubview(subtitleLabel!) 107 | 108 | subtitleLabel!.snp.makeConstraints { (make) in 109 | if title != nil { 110 | make.top.equalTo(titleLabel!.snp.bottom).offset(2.5) 111 | make.left.equalTo(titleLabel!) 112 | make.right.equalTo(titleLabel!) 113 | } 114 | else { 115 | make.top.equalToSuperview() 116 | make.left.equalToSuperview() 117 | make.right.equalToSuperview() 118 | } 119 | } 120 | } 121 | 122 | labelsView.snp.makeConstraints { (make) in 123 | make.centerY.equalToSuperview().offset(heightAdjustment / 4) 124 | 125 | if let leftView = leftView { 126 | make.left.equalTo(leftView.snp.right).offset(padding) 127 | } else { 128 | make.left.equalToSuperview().offset(padding) 129 | } 130 | 131 | if let rightView = rightView { 132 | make.right.equalTo(rightView.snp.left).offset(-padding) 133 | } else { 134 | make.right.equalToSuperview().offset(-padding) 135 | } 136 | 137 | if let subtitleLabel = subtitleLabel { 138 | make.bottom.equalTo(subtitleLabel) 139 | } else { 140 | make.bottom.equalTo(titleLabel!) 141 | } 142 | } 143 | 144 | updateMarqueeLabelsDurations() 145 | 146 | } 147 | 148 | public convenience init( 149 | attributedTitle: NSAttributedString, 150 | attributedSubtitle: NSAttributedString? = nil, 151 | leftView: UIView? = nil, 152 | rightView: UIView? = nil, 153 | style: BannerStyle = .info, 154 | colors: BannerColorsProtocol? = nil 155 | ) { 156 | 157 | let subtitle: String? = (attributedSubtitle != nil) ? "" : nil 158 | self.init(title: "", subtitle: subtitle, leftView: leftView, rightView: rightView, style: style, colors: colors) 159 | titleLabel!.attributedText = attributedTitle 160 | subtitleLabel?.attributedText = attributedSubtitle 161 | } 162 | 163 | public init(customView: UIView) { 164 | super.init(style: .customView) 165 | self.customView = customView 166 | 167 | contentView.addSubview(customView) 168 | customView.snp.makeConstraints { (make) in 169 | make.edges.equalTo(contentView) 170 | } 171 | 172 | spacerView.backgroundColor = customView.backgroundColor 173 | } 174 | 175 | required public init?(coder aDecoder: NSCoder) { 176 | fatalError("init(coder:) has not been implemented") 177 | } 178 | 179 | internal override func updateMarqueeLabelsDurations() { 180 | super.updateMarqueeLabelsDurations() 181 | subtitleLabel?.speed = .duration(CGFloat(duration <= 1 ? 1 : duration - 1)) 182 | } 183 | 184 | } 185 | 186 | public extension NotificationBanner { 187 | 188 | func applyStyling( 189 | cornerRadius: CGFloat? = nil, 190 | titleFont: UIFont? = nil, 191 | titleColor: UIColor? = nil, 192 | titleTextAlign: NSTextAlignment? = nil, 193 | subtitleFont: UIFont? = nil, 194 | subtitleColor: UIColor? = nil, 195 | subtitleTextAlign: NSTextAlignment? = nil 196 | ) { 197 | 198 | if let cornerRadius = cornerRadius { 199 | contentView.layer.cornerRadius = cornerRadius 200 | } 201 | 202 | if let titleFont = titleFont { 203 | titleLabel!.font = titleFont 204 | } 205 | 206 | if let titleColor = titleColor { 207 | titleLabel!.textColor = titleColor 208 | } 209 | 210 | if let titleTextAlign = titleTextAlign { 211 | titleLabel!.textAlignment = titleTextAlign 212 | } 213 | 214 | if let subtitleFont = subtitleFont { 215 | subtitleLabel!.font = subtitleFont 216 | } 217 | 218 | if let subtitleColor = subtitleColor { 219 | subtitleLabel!.textColor = subtitleColor 220 | } 221 | 222 | if let subtitleTextAlign = subtitleTextAlign { 223 | subtitleLabel!.textAlignment = subtitleTextAlign 224 | } 225 | 226 | if titleFont != nil || subtitleFont != nil { 227 | updateBannerHeight() 228 | } 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/NotificationBannerQueue.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | 21 | @objc 22 | public enum QueuePosition: Int { 23 | case back 24 | case front 25 | } 26 | 27 | @objcMembers 28 | open class NotificationBannerQueue: NSObject { 29 | 30 | /// The default instance of the NotificationBannerQueue 31 | public static let `default` = NotificationBannerQueue() 32 | 33 | /// The notification banners currently placed on the queue 34 | private(set) var banners: [BaseNotificationBanner] = [] 35 | 36 | /// The notification banners currently placed on the queue 37 | private(set) var maxBannersOnScreenSimultaneously: Int = 1 38 | 39 | /// This is a mutex to prevent too many notification banners from appearing on screen at once 40 | /// while our BaseNotificationBanner is animating itself off screen 41 | public var activeAnimation = false 42 | 43 | /// The current number of notification banners on the queue 44 | public var numberOfBanners: Int { 45 | return banners.count 46 | } 47 | 48 | public init(maxBannersOnScreenSimultaneously: Int = 1) { 49 | self.maxBannersOnScreenSimultaneously = maxBannersOnScreenSimultaneously 50 | } 51 | 52 | /** 53 | Adds a banner to the queue 54 | -parameter banner: The notification banner to add to the queue 55 | -parameter queuePosition: The position to show the notification banner. If the position is .front, the 56 | banner will be displayed immediately 57 | */ 58 | func addBanner( 59 | _ banner: BaseNotificationBanner, 60 | bannerPosition: BannerPosition, 61 | queuePosition: QueuePosition 62 | ) { 63 | 64 | if queuePosition == .back { 65 | banners.append(banner) 66 | 67 | let bannersCount = banners.filter { $0.isDisplaying }.count 68 | if bannersCount < maxBannersOnScreenSimultaneously && self.activeAnimation == false { 69 | banner.show(placeOnQueue: false, bannerPosition: banner.bannerPosition) 70 | } 71 | 72 | } else { 73 | banner.show(placeOnQueue: false, bannerPosition: bannerPosition) 74 | 75 | if let firstBanner = firstNotDisplayedBanner() { 76 | firstBanner.suspend() 77 | } 78 | 79 | banners.insert(banner, at: 0) 80 | } 81 | 82 | } 83 | 84 | /** 85 | Removes a banner from the queue 86 | -parameter banner: A notification banner to remove from the queue. 87 | */ 88 | func removeBanner(_ banner: BaseNotificationBanner) { 89 | 90 | if let index = banners.firstIndex(of: banner) { 91 | banners.remove(at: index) 92 | } 93 | 94 | banners.forEach { 95 | $0.updateBannerPositionFrames() 96 | if $0.isDisplaying { 97 | $0.animateUpdatedBannerPositionFrames() 98 | } 99 | } 100 | } 101 | 102 | /** 103 | Shows the next notificaiton banner on the queue if one exists 104 | -parameter callback: The closure to execute after a banner is shown or when the queue is empty 105 | */ 106 | func showNext(callback: ((_ isEmpty: Bool) -> Void)) { 107 | 108 | if let banner = firstNotDisplayedBanner() { 109 | 110 | if banner.isSuspended { 111 | banner.resume() 112 | } else { 113 | banner.show(placeOnQueue: false, bannerPosition: banner.bannerPosition) 114 | } 115 | 116 | callback(false) 117 | } 118 | else { 119 | callback(true) 120 | return 121 | } 122 | } 123 | 124 | func firstNotDisplayedBanner() -> BaseNotificationBanner? { 125 | return banners.filter { !$0.isDisplaying }.first 126 | } 127 | 128 | /** 129 | Removes all notification banners from the queue 130 | */ 131 | public func removeAll() { 132 | banners.removeAll() 133 | } 134 | 135 | /** 136 | Forced dissmiss all notification banners from the queue 137 | */ 138 | public func dismissAllForced() { 139 | banners.forEach { $0.dismiss(forced: true) } 140 | banners.removeAll() 141 | } 142 | 143 | } 144 | 145 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/NotificationBannerUtilities.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | 21 | class NotificationBannerUtilities: NSObject { 22 | 23 | class func isNotchFeaturedIPhone() -> Bool { 24 | if #available(iOS 11, *) { 25 | if UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0.0 > 0.0 { 26 | return true 27 | } else { 28 | return false 29 | } 30 | } else { 31 | return false 32 | } 33 | } 34 | 35 | class func hasDynamicIsland() -> Bool { 36 | if #available(iOS 11, *) { 37 | if UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0.0 > 50.0 { 38 | return true 39 | } else { 40 | return false 41 | } 42 | } else { 43 | return false 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/StatusBarNotificationBanner.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2017-2018 Dalton Hinterscher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 14 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 15 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | */ 18 | 19 | import UIKit 20 | 21 | import MarqueeLabel 22 | 23 | @objcMembers 24 | open class StatusBarNotificationBanner: BaseNotificationBanner { 25 | 26 | public override var bannerHeight: CGFloat { 27 | get { 28 | if let customBannerHeight = customBannerHeight { 29 | return customBannerHeight 30 | } else if shouldAdjustForDynamicIsland() { 31 | return 70.0 32 | } else if shouldAdjustForNotchFeaturedIphone() { 33 | return 50.0 34 | } else { 35 | return 20.0 + heightAdjustment 36 | } 37 | } set { 38 | customBannerHeight = newValue 39 | } 40 | } 41 | 42 | override init(style: BannerStyle, colors: BannerColorsProtocol? = nil) { 43 | super.init(style: style, colors: colors) 44 | 45 | titleLabel = MarqueeLabel() 46 | (titleLabel as! MarqueeLabel).animationDelay = 2 47 | (titleLabel as! MarqueeLabel).type = .leftRight 48 | titleLabel!.font = UIFont.systemFont(ofSize: 12.5, weight: UIFont.Weight.bold) 49 | titleLabel!.textAlignment = .center 50 | titleLabel!.textColor = .white 51 | contentView.addSubview(titleLabel!) 52 | 53 | titleLabel!.snp.makeConstraints { (make) in 54 | make.top.equalToSuperview().offset(heightAdjustment) 55 | make.left.equalToSuperview().offset(5) 56 | make.right.equalToSuperview().offset(-5) 57 | make.bottom.equalToSuperview() 58 | } 59 | 60 | updateMarqueeLabelsDurations() 61 | } 62 | 63 | public convenience init( 64 | title: String, 65 | style: BannerStyle = .info, 66 | colors: BannerColorsProtocol? = nil 67 | ) { 68 | self.init(style: style, colors: colors) 69 | titleLabel!.text = title 70 | } 71 | 72 | public convenience init( 73 | attributedTitle: NSAttributedString, 74 | style: BannerStyle = .info, 75 | colors: BannerColorsProtocol? = nil 76 | ) { 77 | self.init(style: style, colors: colors) 78 | titleLabel!.attributedText = attributedTitle 79 | } 80 | 81 | public init(customView: UIView) { 82 | super.init(style: .customView) 83 | self.customView = customView 84 | 85 | contentView.addSubview(customView) 86 | customView.snp.makeConstraints { make in 87 | make.edges.equalTo(contentView) 88 | } 89 | 90 | spacerView.backgroundColor = customView.backgroundColor 91 | } 92 | 93 | required public init?(coder aDecoder: NSCoder) { 94 | super.init(coder: aDecoder) 95 | } 96 | 97 | } 98 | 99 | public extension StatusBarNotificationBanner { 100 | 101 | func applyStyling( 102 | titleColor: UIColor? = nil, 103 | titleTextAlign: NSTextAlignment? = nil 104 | ) { 105 | 106 | if let titleColor = titleColor { 107 | titleLabel!.textColor = titleColor 108 | } 109 | 110 | if let titleTextAlign = titleTextAlign { 111 | titleLabel!.textAlignment = titleTextAlign 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/String+heightForConstrainedWidth.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+heightForConstrainedWidth.swift 3 | // NotificationBanner 4 | // 5 | // Created by Sascha Gordner on 03.10.18. 6 | // Copyright © 2018 Dalton Hinterscher. All rights reserved. 7 | // 8 | // https://stackoverflow.com/questions/30450434/figure-out-size-of-uilabel-based-on-string-in-swift 9 | 10 | import UIKit 11 | 12 | public extension String { 13 | 14 | /// Calculates the height a label will need in order to display the String for the given width and font. 15 | /// 16 | /// - Parameters: 17 | /// - width: Max width of the bounding rect 18 | /// - font: Font used to render the string 19 | /// - Returns: Height a string will need to be completely visible 20 | func height(forConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat { 21 | let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) 22 | let boundingBox = self.boundingRect( 23 | with: constraintRect, 24 | options: [.usesLineFragmentOrigin, .usesFontLeading], 25 | attributes: [.font: font], 26 | context: nil 27 | ) 28 | return boundingBox.height 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /NotificationBanner/Classes/UIWindow+orientation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindow+orientation.swift 3 | // NotificationBannerSwift 4 | // 5 | // Created by gabmarfer on 15/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIWindow { 11 | 12 | public var width: CGFloat { 13 | let orientation = UIDevice.current.orientation 14 | switch orientation { 15 | case .landscapeLeft, .landscapeRight: 16 | return max(frame.width, frame.height) 17 | case .portrait, .portraitUpsideDown: 18 | return min(frame.width, frame.height) 19 | default: 20 | return frame.width 21 | } 22 | } 23 | 24 | public var height: CGFloat { 25 | let orientation = UIDevice.current.orientation 26 | switch orientation { 27 | case .landscapeLeft, .landscapeRight: 28 | return min(frame.width, frame.height) 29 | case .portrait, .portraitUpsideDown: 30 | return max(frame.width, frame.height) 31 | default: 32 | return frame.height 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /NotificationBanner/Supporting Files/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 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /NotificationBanner/Supporting Files/NotificationBanner.h: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationBanner.h 3 | // NotificationBanner 4 | // 5 | // Created by Cihat Gündüz on 02.05.17. 6 | // Copyright © 2017-2018 Dalton Hinterscher. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for NotificationBanner. 12 | FOUNDATION_EXPORT double NotificationBannerVersionNumber; 13 | 14 | //! Project version string for NotificationBanner. 15 | FOUNDATION_EXPORT const unsigned char NotificationBannerVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /NotificationBanner/Supporting Files/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyAccessedAPITypes 8 | 9 | NSPrivacyCollectedDataTypes 10 | 11 | NSPrivacyTrackingDomains 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /NotificationBannerSwift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'NotificationBannerSwift' 3 | s.version = '4.0.0' 4 | s.summary = 'The easiest way to display in app notification banners in iOS.' 5 | 6 | s.description = <<-DESC 7 | NotificationBanner is an extremely customizable and lightweight library that makes the task of displaying in app notification banners and drop down alerts an absolute breeze in iOS. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/Daltron/NotificationBanner' 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { 'Daltron' => 'daltonhint4@gmail.com' } 13 | s.source = { :git => 'https://github.com/Daltron/NotificationBanner.git', :tag => s.version.to_s } 14 | 15 | s.ios.deployment_target = '12.0' 16 | s.swift_version = '5.0' 17 | s.source_files = 'NotificationBanner/Classes/**/*' 18 | 19 | s.dependency 'SnapKit', '~> 5.7.1' 20 | s.dependency 'MarqueeLabel', '~> 4.5.0' 21 | 22 | s.resource_bundle = { 23 | "NotificationBannerPrivacyInfo" => "NotificationBanner/Supporting Files/PrivacyInfo.xcprivacy" 24 | } 25 | 26 | end 27 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "MarqueeLabel", 6 | "repositoryURL": "https://github.com/cbpowell/MarqueeLabel", 7 | "state": { 8 | "branch": null, 9 | "revision": "877e810534cda9afabb8143ae319b7c3341b121b", 10 | "version": "4.5.0" 11 | } 12 | }, 13 | { 14 | "package": "SnapKit", 15 | "repositoryURL": "https://github.com/SnapKit/SnapKit", 16 | "state": { 17 | "branch": null, 18 | "revision": "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", 19 | "version": "5.7.1" 20 | } 21 | } 22 | ] 23 | }, 24 | "version": 1 25 | } 26 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "NotificationBannerSwift", 6 | platforms: [ 7 | .iOS(.v12) 8 | ], 9 | products: [ 10 | .library(name: "NotificationBannerSwift", targets: ["NotificationBannerSwift"]) 11 | ], 12 | dependencies: [ 13 | .package(url: "https://github.com/SnapKit/SnapKit", from: "5.7.1"), 14 | .package(url: "https://github.com/cbpowell/MarqueeLabel", from: "4.5.0") 15 | ], 16 | targets: [ 17 | .target( 18 | name: "NotificationBannerSwift", 19 | dependencies: [.byName(name: "MarqueeLabel"), .byName(name: "SnapKit")], 20 | path: "NotificationBanner/Classes" 21 | ) 22 | ], 23 | swiftLanguageVersions: [.v5] 24 | ) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Notification Banner](NotificationBanner/Assets/header.png) 2 | 3 | [![GitHub contributors](https://img.shields.io/github/contributors/daltron/notificationbanner.svg?style=flat)]() 4 | [![License](https://img.shields.io/cocoapods/l/NotificationBannerSwift.svg?style=flat)](http://cocoapods.org/pods/NotificationBannerSwift) 5 | [![PayPal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/donate/?hosted_button_id=QEBVNMTVX6N88) 6 | 7 | ## Written in Swift 5 8 | 9 | NotificationBanner is an extremely customizable and lightweight library that makes the task of displaying in app notification banners and drop down alerts an absolute breeze in iOS. 10 | 11 | | Basic Banners | Banners with Side Views | Status Bar Banners | 12 | | ------------- | ------------- | ------------- | 13 | | ![Basic Banners](NotificationBanner/Assets/basic.gif) | ![Banners with Side Views](NotificationBanner/Assets/side_views.gif) | ![Status Bar Banners](NotificationBanner/Assets/status_bar.gif) | 14 | 15 | | Growing Banners | Floating Banners | Stacked Banners | 16 | | ------------- | ------------- | ------------- | 17 | | ![Growing Banners](NotificationBanner/Assets/growing.gif) | ![Floating Banners](NotificationBanner/Assets/floating.gif) | ![Floating Banners](NotificationBanner/Assets/stacked.gif) | 18 | 19 | ## Features 20 | - Highly customizable ✅ 21 | - `NSAttributedString` support ✅ 22 | - iPhone, iPhoneX, & iPad Support ✅ 23 | - Orientation change support ✅ 24 | - Custom `UIView` support ✅ 25 | - Custom colors support ✅ 26 | - Support for long titles/ subtitles ✅ 27 | - `NotificationBanner` uses horizontal scrolling labels 28 | - `GrowingNotificationBanner` grows in height as needed 29 | - Presenting from top or bottom support ✅ 30 | - Haptic feeback support ✅ 31 | - Built in banner queue ✅ 32 | - Allow to display several banners simultaneously, configurable in banners queue ✅ 33 | - Accessibility support ✅ 34 | - Dynamic Island Support ✅ 35 | 36 | ## Requirements 37 | 38 | - iOS 12.0+ 39 | - Xcode 10.0+ 40 | 41 | ## Installation 42 | 43 | ### Swift Package Manager 44 | 45 | This is the preferred way. All other installation methods are officially deprecated and will no longer be supported in future versions. 46 | 47 | 1. Go to File > Add Packages 48 | 2. The Add Package dialog appears, by default with Apple packages. 49 | 3. In the upper right hand corner, paste https://github.com/Daltron/NotificationBanner into the search bar 50 | 4. Hit Return to kick off the search 51 | 5. Click Add Package. 52 | 53 | ### CocoaPods 54 | 55 | NotificationBanner is available through [CocoaPods](http://cocoapods.org). To install 56 | it, simply add the following line to your Podfile: 57 | 58 | #### Swift 5 + Xcode 11 + iOS 13 Support 59 | 60 | ```ruby 61 | pod 'NotificationBannerSwift', '~> 3.0.0' 62 | ``` 63 | 64 | #### Swift 5 + Xcode 10.x 65 | 66 | ```ruby 67 | pod 'NotificationBannerSwift', '2.5.0' 68 | ``` 69 | 70 | #### Swift 4.2 71 | 72 | ```ruby 73 | pod 'NotificationBannerSwift', '2.0.1' 74 | ``` 75 | 76 | #### Swift 4.0 77 | 78 | ```ruby 79 | pod 'NotificationBannerSwift', '1.6.3' 80 | pod 'MarqueeLabel/Swift', '3.1.6' 81 | ``` 82 | 83 | Then add `import NotificationBannerSwift` at the top of each file you use NotificationBanner in your project. 84 | 85 | ### Carthage 86 | 87 | To use NotificationBanner via Carthage simply add this line to your `Cartfile`: 88 | 89 | #### Swift 5 90 | ```swift 91 | github "Daltron/NotificationBanner" "master" 92 | ``` 93 | 94 | Then add `NotificationBanner.framework` and the dependencies `SnapKit.framework` and `MarqueeLabel.framework` in your project. 95 | 96 | ## Usage 97 | 98 | Creating drop down alerts with NotificationBanner is easy. To create a regular banner (with scrolling labels) and show it, simply: 99 | 100 | ```swift 101 | let banner = NotificationBanner(title: title, subtitle: subtitle, style: .success) 102 | banner.show() 103 | ``` 104 | 105 | If you want to create a banner which grows in height as needed and show it accordingly just use `GrowingNotificationBanner` instead of `NotificationBanner`: 106 | 107 | ```swift 108 | let banner = GrowingNotificationBanner(title: title, subtitle: subtitle, style: .success) 109 | banner.show() 110 | ``` 111 | 112 | To create a status bar alert, simply: 113 | 114 | ```swift 115 | let banner = StatusBarNotificationBanner(title: title, style: .success) 116 | banner.show() 117 | ``` 118 | 119 | By default, each banner will be displayed on the main application window. If you are wanting to show a banner below a navigation bar, simply show on the view controller that is within the navigation system: 120 | 121 | ```swift 122 | banner.show(on: viewController) 123 | ``` 124 | 125 | By default, each banner will present from the top. If you are wanting to display from the bottom, simply: 126 | 127 | ```swift 128 | banner.show(bannerPosition: .bottom) 129 | ``` 130 | 131 | Each of the show properties defined above can be mixed and matched to work flawlessly with eachother. 132 | 133 | By default, each banner will automatically dismiss after 5 seconds. To dismiss programatically, simply: 134 | 135 | ```swift 136 | banner.dismiss() 137 | ``` 138 | 139 | To show a banner infinitely until it is manually dismissed, simply: 140 | 141 | ```swift 142 | banner.autoDismiss = false 143 | ``` 144 | 145 | NotificationBanner has five prebuilt styles that you can choose from: 146 | 147 | ```swift 148 | public enum BannerStyle { 149 | case danger 150 | case info 151 | case customView 152 | case success 153 | case warning 154 | } 155 | ``` 156 | 157 | You can override the predefined colors that NotificationBanner uses for any style by conforming to the `BannerColorsProtocol`: 158 | 159 | ```swift 160 | public protocol BannerColorsProtocol { 161 | func color(for style: BannerStyle) -> UIColor 162 | } 163 | ``` 164 | 165 | Its as easy as creating a custom banner colors class: 166 | 167 | ```swift 168 | class CustomBannerColors: BannerColorsProtocol { 169 | 170 | internal func color(for style: BannerStyle) -> UIColor { 171 | switch style { 172 | case .danger: // Your custom .danger color 173 | case .info: // Your custom .info color 174 | case .customView: // Your custom .customView color 175 | case .success: // Your custom .success color 176 | case .warning: // Your custom .warning color 177 | } 178 | } 179 | 180 | } 181 | ``` 182 | 183 | And then passing in that class to any notification banner you create: 184 | 185 | ```swift 186 | let banner = NotificationBanner(title: title, style: .success, colors: CustomBannerColors()) 187 | banner.show() 188 | ``` 189 | 190 | By default, the `.info` style will be applied to the banner if no style is provided in the init method. You can set the background color of a banner at any time by simply setting the `backgroundColor`. 191 | 192 | 193 | ### Banners with Side Views 194 | 195 | A notification banner can have a left accessory view, a right accessory view, or both: 196 | 197 | ```swift 198 | // Success Style Notification with Left View 199 | let leftView = UIImageView(image: #imageLiteral(resourceName: "success")) 200 | let banner = NotificationBanner(title: title, subtitle: subtitle, leftView: leftView, style: .success) 201 | banner.show() 202 | 203 | // Danger Style Notification with Right View 204 | let rightView = UIImageView(image: #imageLiteral(resourceName: "danger")) 205 | let banner = NotificationBanner(title: title, subtitle: subtitle, rightView: rightView, style: .danger) 206 | banner.show() 207 | 208 | // Info Style Notification with Left and Right Views 209 | let leftView = UIImageView(image: #imageLiteral(resourceName: "info")) 210 | let rightView = UIImageView(image: #imageLiteral(resourceName: "right_chevron")) 211 | let banner = NotificationBanner(title: title, subtitle: subtitle, leftView: leftView, rightView: rightView, style: .info) 212 | banner.show() 213 | ``` 214 | ![Banners with Side Views](NotificationBanner/Assets/side_views.gif) 215 | 216 | Each side view will be automically reisized to fit perfectly 217 | 218 | ### Banners with a Custom View 219 | 220 | A notification banner can also be initalized with a custom view: 221 | 222 | ```swift 223 | let banner = NotificationBanner(customView: NorthCarolinaBannerView()) 224 | banner.show() 225 | ``` 226 | ![Custom Banner](NotificationBanner/Assets/custom.gif) 227 | 228 | ### Handling User Interaction 229 | 230 | By default, when a banner is tapped or swiped up by a user, it will be dismissed. If you want to detect when the user taps or swipes up on a banner, simply: 231 | 232 | ```swift 233 | banner.onTap = { 234 | // Do something regarding the banner 235 | } 236 | ``` 237 | 238 | ```swift 239 | banner.onSwipeUp = { 240 | // Do something regarding the banner 241 | } 242 | ``` 243 | 244 | ### Banner Events 245 | 246 | You can choose to opt into a notification banner's events by registering as its delegate: 247 | 248 | ```swift 249 | banner.delegate = self 250 | ``` 251 | Then just make sure to implement the following methods: 252 | 253 | ```swift 254 | func notificationBannerWillAppear(_ banner: BaseNotificationBanner) 255 | func notificationBannerDidAppear(_ banner: BaseNotificationBanner) 256 | func notificationBannerWillDisappear(_ banner: BaseNotificationBanner) 257 | func notificationBannerDidDisappear(_ banner: BaseNotificationBanner) 258 | ``` 259 | 260 | ## Haptic Feedback Support 261 | By default, when a banner is displayed, a haptic feedback will be generated on devices that support it. The types of haptic feedback are as follows: 262 | 263 | ```swift 264 | public enum BannerHaptic { 265 | case light 266 | case medium 267 | case heavy 268 | case none 269 | } 270 | ``` 271 | 272 | To change the type of haptic feedback to generate when a banner is shown, simply: 273 | 274 | ```swift 275 | banner.haptic = .heavy 276 | ``` 277 | 278 | ## Banner Queue 279 | 280 | By default, each notification banner is placed onto a singleton of an auto-managed `NotificationBannerQueue`. This allows an infinite amount of banners to be displayed without one hiding the other. If you have multiple controllers within your navigation stack that need to be managed by a seperate queue (like a tab bar controller), simply create an instance of a `NotificationBannerQueue` and pass it in to the show function: 281 | 282 | ```swift 283 | banner.show(queue: customQueue) 284 | ``` 285 | 286 | By default, each notification banner is placed on the back of the queue. If you would rather place the banner in the front and show it immediately no matter how many banners are in the queue, simply state it in the `show()` method: 287 | 288 | ```swift 289 | banner.show(queuePosition: .front) 290 | ``` 291 | 292 | Adding a banner to the front of the queue will temporarily suspend the currently displayed banner (if there is one) and will resume it after the banner in front of it dismisses. 293 | 294 | To get the number of banners currently on the queue, simply: 295 | 296 | ```swift 297 | let numberOfBanners = NotificationBannerQueue.default.numberOfBanners 298 | ``` 299 | 300 | This is all automatically managed! 301 | 302 | ## Banner Queue and display banners simultaneously (stacked) 303 | 304 | You can also create the queue to display several banners at once with controlling of maximum number of banners to be displayed simultaneously. You can "show" more banners than allowed by queue settings - banners what exceed this value will be displayed some time later, after some banners already displayed on screen will be closed. In example below we create queue with maximum simultaneous banners allowed - 3: 305 | 306 | ```swift 307 | let bannerQueueToDisplaySeveralBanners = NotificationBannerQueue(maxBannersOnScreenSimultaneously: 3) 308 | ``` 309 | 310 | Create five different banners: 311 | 312 | ```swift 313 | let banner1 = FloatingNotificationBanner( 314 | title: "Success Notification - 1", 315 | subtitle: "First Notification from 5 in current queue with 3 banners allowed simultaneously", 316 | style: .success 317 | ) 318 | banner1.delegate = self 319 | 320 | let banner2 = FloatingNotificationBanner( 321 | title: "Danger Notification - 2", 322 | subtitle: "Second Notification from 5 in current queue with 3 banners allowed simultaneously", 323 | style: .danger 324 | ) 325 | banner2.delegate = self 326 | 327 | let banner3 = FloatingNotificationBanner( 328 | title: "Info Notification - 3", 329 | subtitle: "Third Notification from 5 in current queue with 3 banners allowed simultaneously", 330 | style: .info 331 | ) 332 | banner3.delegate = self 333 | 334 | let banner4 = FloatingNotificationBanner( 335 | title: "Success Notification - 4", 336 | subtitle: "Fourth Notification from 5 in current queue with 3 banners allowed simultaneously", 337 | style: .success 338 | ) 339 | banner4.delegate = self 340 | 341 | let banner5 = FloatingNotificationBanner( 342 | title: "Info Notification - 5", 343 | subtitle: "Fifth Notification from 5 in current queue with 3 banners allowed simultaneously", 344 | style: .info 345 | ) 346 | banner5.delegate = self 347 | ``` 348 | 349 | and show all five banners at once: 350 | ```swift 351 | showBanners( 352 | [banner1, banner2, banner3, banner4, banner5], 353 | in: bannerQueue5AllowedMixed 354 | ) 355 | ``` 356 | 357 | using this supporting method 358 | 359 | ```swift 360 | func showBanners( 361 | _ banners: [FloatingNotificationBanner], 362 | in notificationBannerQueue: NotificationBannerQueue 363 | ) { 364 | banners.forEach { banner in 365 | banner.show( 366 | bannerPosition: selectedBannerPosition(), 367 | queue: notificationBannerQueue, 368 | cornerRadius: 8, 369 | shadowColor: UIColor(red: 0.431, green: 0.459, blue: 0.494, alpha: 1), 370 | shadowBlurRadius: 16, 371 | shadowEdgeInsets: UIEdgeInsets(top: 8, left: 8, bottom: 0, right: 8) 372 | ) 373 | } 374 | } 375 | ``` 376 | 377 | It will display first three banners at once, and after some time (or by user tap) it will be hidden and 4 and 5 banner will be displayed when. All it with fancy animation. 378 | 379 | ## Feature Requests 380 | 381 | I'd love to know anything that you think NotificationBanner is missing. Open an issue and I'll add the `feature request` label to it and I'll do everything I can to accomodate that request if it is in the library's best interest. 😄 382 | 383 | ## Apps that Use NotificationBanner 384 | [![Q - Talk About Music](AppIcons/q_talk_about_music.jpg)](https://itunes.apple.com/us/app/q-talk-about-music/id1071551321?mt=8) 385 | [![VH Dispatch](AppIcons/vh_dispatch.png)](https://apps.apple.com/app/vh-dispatch/id1249569084) 386 | [![Stikkr](AppIcons/stikkr.png)](https://apps.apple.com/app/stikkr/id851375015) 387 | [![CardCast](AppIcons/cardcast.png)](https://apps.apple.com/app/cardcast-business-cards/id1269278947) 388 | [![Happy Scale](AppIcons/happy_scale.png)](https://apps.apple.com/app/happy-scale/id532430574) 389 | [![Wanderings](AppIcons/wanderings.png)](https://apps.apple.com/app/wanderings-travel-tracking/id1292503352) 390 | [![Modern Magic 8 Ball](AppIcons/modernmagic8ball.png)](https://apps.apple.com/app/modern-magic-8-ball/id1381145384) 391 | [![Envision: Habits Tracker](AppIcons/envision.png)](https://apps.apple.com/app/envision-habits-tracker/id1423771095) 392 | [![TSUM](AppIcons/tsum.png)](https://apps.apple.com/ru/app/%D1%86%D1%83%D0%BC-%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B5%D1%82-%D0%BC%D0%B0%D0%B3%D0%B0%D0%B7%D0%B8%D0%BD-%D0%BE%D0%B4%D0%B5%D0%B6%D0%B4%D1%8B/id1089560311) 393 | [![RIS](AppIcons/ris.png)](https://apps.apple.com/ru/app/%D1%80%D0%B5%D1%81%D1%82%D0%BE%D1%80%D0%B0%D0%BD%D1%8B-%D1%80%D0%B8%D1%81-%D0%B7%D0%B0%D0%BA%D0%B0%D0%B7-%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B0/id932844115) 394 | [![LukaPizza](AppIcons/lukapizza.png)](https://apps.apple.com/ru/app/luka-pizza-%D0%B7%D0%B0%D0%BA%D0%B0%D0%B7-%D0%B8-%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B0/id1202155629) 395 | 396 | #### Feel free to add yours! 397 | 398 | ## Author 399 | 400 | Dalton Hinterscher, daltonhint4@gmail.com 401 | 402 | ## License 403 | 404 | NotificationBanner is available under the MIT license. See the LICENSE file for more info. 405 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------