├── SpaceView ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ └── SpaceView.swift ├── Example ├── Assets.xcassets │ ├── Contents.json │ ├── ic_error.imageset │ │ ├── close.png │ │ ├── close@2x.png │ │ ├── close@3x.png │ │ └── Contents.json │ ├── ic_success.imageset │ │ ├── checked.png │ │ ├── checked@2x.png │ │ ├── checked@3x.png │ │ └── Contents.json │ ├── ic_warning.imageset │ │ ├── warning__1_.png │ │ ├── warning__1_@2x.png │ │ ├── warning__1_@3x.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── AppDelegate.swift ├── Info.plist ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard └── ViewController.swift ├── SpaceViewExample.xcodeproj ├── xcuserdata │ └── user.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── SpaceViewExample.xcscheme ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── user.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── project.pbxproj ├── SpaceView.podspec ├── LICENSE └── README.md /SpaceView/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SpaceView/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_error.imageset/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_error.imageset/close.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_error.imageset/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_error.imageset/close@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_error.imageset/close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_error.imageset/close@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_success.imageset/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_success.imageset/checked.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_success.imageset/checked@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_success.imageset/checked@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_success.imageset/checked@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_success.imageset/checked@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_warning.imageset/warning__1_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_warning.imageset/warning__1_.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_warning.imageset/warning__1_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_warning.imageset/warning__1_@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_warning.imageset/warning__1_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/Example/Assets.xcassets/ic_warning.imageset/warning__1_@3x.png -------------------------------------------------------------------------------- /SpaceViewExample.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /SpaceViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SpaceViewExample.xcodeproj/project.xcworkspace/xcuserdata/user.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xopoko/SpaceView/HEAD/SpaceViewExample.xcodeproj/project.xcworkspace/xcuserdata/user.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SpaceViewExample 4 | // 5 | // Created by user on 25.12.16. 6 | // Copyright © 2016 horoko. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_error.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "close.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "close@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "close@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_success.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "checked.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "checked@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "checked@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/ic_warning.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "warning__1_.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "warning__1_@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "warning__1_@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SpaceViewExample.xcodeproj/xcuserdata/user.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SpaceViewExample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | CE10B5B81E0F360500E57358 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SpaceView.podspec: -------------------------------------------------------------------------------- 1 | # Be sure to run `pod lib lint SpaceView.podspec' to ensure this is a 2 | # valid spec before submitting. 3 | 4 | Pod::Spec.new do |s| 5 | s.name = 'SpaceView' 6 | s.version = '1.0.6' 7 | s.summary = 'Swift library, for showing awesome messages!' 8 | s.description = <<-DESC 9 | Library to display the amazing messages to alert the user. You can easily call them. Incredibly simple and easy to change to suit your requirements. 10 | DESC 11 | s.homepage = 'https://github.com/Xopoko/SpaceView' 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | s.author = { 'Horoko' => 'alonsik1@gmail.com' } 14 | s.source = { :git => 'https://github.com/Xopoko/SpaceView.git', :tag => s.version.to_s } 15 | s.swift_version = '5.0' 16 | s.ios.deployment_target = '8.0' 17 | 18 | s.source_files = 'SpaceView/Classes/*' 19 | end 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Xopoko 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 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.2 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpaceView 2 | 3 | [![CI Status](http://img.shields.io/travis/Xopoko/SpaceView.svg?style=flat)](https://travis-ci.org/Xopoko/SpaceView) 4 | [![Version](https://img.shields.io/cocoapods/v/SpaceView.svg?style=flat)](http://cocoapods.org/pods/SpaceView) 5 | [![License](https://img.shields.io/cocoapods/l/SpaceView.svg?style=flat)](http://cocoapods.org/pods/SpaceView) 6 | [![Platform](https://img.shields.io/cocoapods/p/SpaceView.svg?style=flat)](http://cocoapods.org/pods/SpaceView) 7 | 8 | ![space](https://cloud.githubusercontent.com/assets/6337061/21842749/2f803ae8-d7f9-11e6-8cb6-f0dd2628205a.png) 9 | 10 | ## Example 11 | 12 | SpaceView 13 | 14 | - SpaceView On top 15 | ```swift 16 | //View will be shown on the top 17 | self.showSpace(title: "title", description: "description", spaceOptions: [.spacePosition(position: .top) 18 | ]) 19 | ``` 20 | 21 | SpaceView 22 | 23 | - SpaceView On bottom 24 | ```swift 25 | //View will be shown at the bottom 26 | self.showSpace(title: "title", description: "description", spaceOptions: [.spacePosition(position: .bot) 27 | ]) 28 | ``` 29 | 30 | SpaceView 31 | 32 | - SpaceView with default styles 33 | ```swift 34 | self.showSpace(title: "title", description: "description", spaceOptions: [.spaceStyle(style: .success) 35 | ]) 36 | ``` 37 | 38 | - SpaceView set time to hide 39 | ```swift 40 | //SpaceView will not hide 41 | self.showSpace(title: "title", description: "description", spaceOptions: [.spaceTimer(timer: 3.0) 42 | ]) 43 | ``` 44 | 45 | - SpaceView set autohide 46 | ```swift 47 | //View will hide after 3 second 48 | self.showSpace(title: "title", description: "description", spaceOptions: [ .shouldAutoHide(should: false) 49 | ]) 50 | ``` 51 | 52 | - SpaceView set image 53 | ```swift 54 | //Image which will be shown on the right side of spaceView 55 | self.showSpace(title: "title", description: "description", spaceOptions: [.image(img: UIImage()), 56 | ]) 57 | ``` 58 | 59 | - SpaceView set swipe and tap handler 60 | ```swift 61 | //Set the your custom handlers. By default tap handler will hide SpaceView. 62 | //SwipeAction will perform after user did swipe SpaceView 63 | self.showSpace(title: "title", description: "description", spaceOptions: [ 64 | .swipeAction {print("SPACE VIEW DID SWIPE")}, 65 | .tapAction {print("SPACE VIEW DID TAP")} 66 | ]) 67 | ``` 68 | 69 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 70 | 71 | ## Requirements 72 | - iOS 8.0 73 | - Xcode 8.2 74 | 75 | ## Installation 76 | 77 | SpaceView is available through [CocoaPods](http://cocoapods.org). To install 78 | it, simply add the following line to your Podfile: 79 | 80 | ```ruby 81 | pod "SpaceView" 82 | ``` 83 | 84 | ## Author 85 | 86 | Xopoko, alonsik1@gmail.com 87 | 88 | ## License 89 | 90 | SpaceView is available under the MIT license. See the LICENSE file for more info. -------------------------------------------------------------------------------- /SpaceViewExample.xcodeproj/xcuserdata/user.xcuserdatad/xcschemes/SpaceViewExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SpaceViewExample 4 | // 5 | // Created by user on 25.12.16. 6 | // Copyright © 2016 horoko. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | 14 | @IBOutlet weak var timeStepper: UIStepper! 15 | @IBOutlet weak var autohideTimeLabel: UILabel! 16 | 17 | private var timeToAutoHide = 3.0 18 | private var shouldAutoHide = true 19 | private var leftSwipeAccepted = true 20 | private var topSwipeAccepted = true 21 | private var rightSwipeAccepted = true 22 | private var botSwipeAccepted = true 23 | private var spaceStyle: SpaceStyles? = nil 24 | private var spaceTitle = "Title" 25 | private var spaceDescr = "Description" 26 | private var image: UIImage? = nil 27 | private var withImage = false 28 | private var spacePosition: SpacePosition = .top 29 | 30 | var possibleDirectionsToHide: [HideDirection] = [] 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | autohideTimeLabel.text = String(timeStepper.value) 35 | timeToAutoHide = timeStepper.value 36 | 37 | } 38 | 39 | @IBAction func positionsChanged(_ sender: Any) { 40 | if let sc = sender as? UISegmentedControl { 41 | switch sc.selectedSegmentIndex { 42 | case 0: 43 | spacePosition = .top 44 | case 1: 45 | spacePosition = .bot 46 | default: 47 | break 48 | } 49 | } 50 | } 51 | 52 | @IBAction func styleChanged(_ sender: Any) { 53 | if let sc = sender as? UISegmentedControl { 54 | switch sc.selectedSegmentIndex { 55 | case 0: 56 | spaceStyle = .success 57 | case 1: 58 | spaceStyle = .error 59 | case 2: 60 | spaceStyle = .warning 61 | case 3: 62 | spaceStyle = nil 63 | default: 64 | break 65 | } 66 | } 67 | } 68 | 69 | @IBAction func showSpace(_ sender: Any) { 70 | image = nil 71 | possibleDirectionsToHide.removeAll() 72 | 73 | if spaceStyle != nil { 74 | switch spaceStyle! { 75 | case .success: 76 | if withImage { 77 | image = UIImage(named: "ic_success") 78 | } 79 | spaceTitle = "Success" 80 | spaceDescr = "Operation complete" 81 | case .error: 82 | if withImage { 83 | image = UIImage(named: "ic_error") 84 | } 85 | spaceTitle = "Error" 86 | spaceDescr = "Operation failed" 87 | case .warning: 88 | if withImage { 89 | image = UIImage(named: "ic_warning") 90 | } 91 | spaceTitle = "Warning!" 92 | spaceDescr = "Look at this!" 93 | } 94 | } 95 | 96 | if leftSwipeAccepted { 97 | possibleDirectionsToHide.append(.left) 98 | } 99 | if rightSwipeAccepted { 100 | possibleDirectionsToHide.append(.right) 101 | } 102 | if topSwipeAccepted { 103 | possibleDirectionsToHide.append(.top) 104 | } 105 | if botSwipeAccepted { 106 | possibleDirectionsToHide.append(.bot) 107 | } 108 | 109 | self.showSpace(title: spaceTitle, description: spaceDescr, spaceOptions: [ 110 | .spaceHideTimer(timer: timeToAutoHide), 111 | .possibleDirectionToHide(possibleDirectionsToHide), 112 | .shouldAutoHide(should: shouldAutoHide), 113 | .spaceStyle(style: spaceStyle), 114 | .image(img: image), 115 | .spacePosition(position: spacePosition), 116 | .swipeAction {print("SWIPE")}, 117 | ]) 118 | } 119 | 120 | @IBAction func withImageSwitch(_ sender: Any) { 121 | if let switcher = sender as? UISwitch { 122 | withImage = switcher.isOn 123 | } 124 | } 125 | 126 | @IBAction func stepperAutohide(_ sender: Any) { 127 | if let stepper = sender as? UIStepper { 128 | timeToAutoHide = stepper.value 129 | autohideTimeLabel.text = String(stepper.value) 130 | } 131 | } 132 | 133 | @IBAction func BotSwipeDirectionSwitch(_ sender: UISwitch) { 134 | botSwipeAccepted = sender.isOn 135 | } 136 | 137 | @IBAction func TopSwipeDirectionSwitch(_ sender: Any) { 138 | if let switcher = sender as? UISwitch { 139 | topSwipeAccepted = switcher.isOn 140 | } 141 | } 142 | 143 | @IBAction func RightSwipeDirectionSwitch(_ sender: Any) { 144 | if let switcher = sender as? UISwitch { 145 | rightSwipeAccepted = switcher.isOn 146 | } 147 | } 148 | 149 | @IBAction func LeftSwipeDirectionSwitch(_ sender: Any) { 150 | if let switcher = sender as? UISwitch { 151 | leftSwipeAccepted = switcher.isOn 152 | } 153 | } 154 | 155 | @IBAction func shouldAutohideSwitch(_ sender: Any) { 156 | if let switcher = sender as? UISwitch { 157 | shouldAutoHide = switcher.isOn 158 | } 159 | } 160 | } 161 | 162 | -------------------------------------------------------------------------------- /SpaceViewExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CEF67A871E11736500787032 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEF67A861E11736500787032 /* Assets.xcassets */; }; 11 | CEF67A8E1E11739500787032 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF67A8C1E11739500787032 /* AppDelegate.swift */; }; 12 | CEF67A8F1E11739500787032 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF67A8D1E11739500787032 /* ViewController.swift */; }; 13 | CEF67A911E1173A900787032 /* SpaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF67A901E1173A900787032 /* SpaceView.swift */; }; 14 | CEF67A961E1173BF00787032 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEF67A921E1173BF00787032 /* LaunchScreen.storyboard */; }; 15 | CEF67A971E1173BF00787032 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEF67A941E1173BF00787032 /* Main.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | CE10B5B91E0F360500E57358 /* SpaceViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpaceViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | CEF67A861E11736500787032 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Example/Assets.xcassets; sourceTree = SOURCE_ROOT; }; 21 | CEF67A8A1E11737F00787032 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Example/Info.plist; sourceTree = SOURCE_ROOT; }; 22 | CEF67A8C1E11739500787032 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Example/AppDelegate.swift; sourceTree = SOURCE_ROOT; }; 23 | CEF67A8D1E11739500787032 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = Example/ViewController.swift; sourceTree = SOURCE_ROOT; }; 24 | CEF67A901E1173A900787032 /* SpaceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SpaceView.swift; path = SpaceView/Classes/SpaceView.swift; sourceTree = ""; }; 25 | CEF67A931E1173BF00787032 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Example/Base.lproj/LaunchScreen.storyboard; sourceTree = SOURCE_ROOT; }; 26 | CEF67A951E1173BF00787032 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Example/Base.lproj/Main.storyboard; sourceTree = SOURCE_ROOT; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | CE10B5B61E0F360500E57358 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | CE10B5B01E0F360500E57358 = { 41 | isa = PBXGroup; 42 | children = ( 43 | CEF67A901E1173A900787032 /* SpaceView.swift */, 44 | CE10B5BB1E0F360500E57358 /* SpaceViewExample */, 45 | CE10B5BA1E0F360500E57358 /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | CE10B5BA1E0F360500E57358 /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | CE10B5B91E0F360500E57358 /* SpaceViewExample.app */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | CE10B5BB1E0F360500E57358 /* SpaceViewExample */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | CEF67A921E1173BF00787032 /* LaunchScreen.storyboard */, 61 | CEF67A941E1173BF00787032 /* Main.storyboard */, 62 | CEF67A8C1E11739500787032 /* AppDelegate.swift */, 63 | CEF67A8D1E11739500787032 /* ViewController.swift */, 64 | CEF67A861E11736500787032 /* Assets.xcassets */, 65 | CEF67A8A1E11737F00787032 /* Info.plist */, 66 | ); 67 | path = SpaceViewExample; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | CE10B5B81E0F360500E57358 /* SpaceViewExample */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = CE10B5CB1E0F360500E57358 /* Build configuration list for PBXNativeTarget "SpaceViewExample" */; 76 | buildPhases = ( 77 | CE10B5B51E0F360500E57358 /* Sources */, 78 | CE10B5B61E0F360500E57358 /* Frameworks */, 79 | CE10B5B71E0F360500E57358 /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = SpaceViewExample; 86 | productName = SpaceViewExample; 87 | productReference = CE10B5B91E0F360500E57358 /* SpaceViewExample.app */; 88 | productType = "com.apple.product-type.application"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | CE10B5B11E0F360500E57358 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastSwiftUpdateCheck = 0820; 97 | LastUpgradeCheck = 1020; 98 | ORGANIZATIONNAME = horoko; 99 | TargetAttributes = { 100 | CE10B5B81E0F360500E57358 = { 101 | CreatedOnToolsVersion = 8.2; 102 | DevelopmentTeam = DX48S76K53; 103 | LastSwiftMigration = 0820; 104 | ProvisioningStyle = Automatic; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = CE10B5B41E0F360500E57358 /* Build configuration list for PBXProject "SpaceViewExample" */; 109 | compatibilityVersion = "Xcode 3.2"; 110 | developmentRegion = en; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | Base, 115 | ); 116 | mainGroup = CE10B5B01E0F360500E57358; 117 | productRefGroup = CE10B5BA1E0F360500E57358 /* Products */; 118 | projectDirPath = ""; 119 | projectRoot = ""; 120 | targets = ( 121 | CE10B5B81E0F360500E57358 /* SpaceViewExample */, 122 | ); 123 | }; 124 | /* End PBXProject section */ 125 | 126 | /* Begin PBXResourcesBuildPhase section */ 127 | CE10B5B71E0F360500E57358 /* Resources */ = { 128 | isa = PBXResourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | CEF67A961E1173BF00787032 /* LaunchScreen.storyboard in Resources */, 132 | CEF67A871E11736500787032 /* Assets.xcassets in Resources */, 133 | CEF67A971E1173BF00787032 /* Main.storyboard in Resources */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | CE10B5B51E0F360500E57358 /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | CEF67A8F1E11739500787032 /* ViewController.swift in Sources */, 145 | CEF67A8E1E11739500787032 /* AppDelegate.swift in Sources */, 146 | CEF67A911E1173A900787032 /* SpaceView.swift in Sources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXSourcesBuildPhase section */ 151 | 152 | /* Begin PBXVariantGroup section */ 153 | CEF67A921E1173BF00787032 /* LaunchScreen.storyboard */ = { 154 | isa = PBXVariantGroup; 155 | children = ( 156 | CEF67A931E1173BF00787032 /* Base */, 157 | ); 158 | name = LaunchScreen.storyboard; 159 | sourceTree = ""; 160 | }; 161 | CEF67A941E1173BF00787032 /* Main.storyboard */ = { 162 | isa = PBXVariantGroup; 163 | children = ( 164 | CEF67A951E1173BF00787032 /* Base */, 165 | ); 166 | name = Main.storyboard; 167 | sourceTree = ""; 168 | }; 169 | /* End PBXVariantGroup section */ 170 | 171 | /* Begin XCBuildConfiguration section */ 172 | CE10B5C91E0F360500E57358 /* Debug */ = { 173 | isa = XCBuildConfiguration; 174 | buildSettings = { 175 | ALWAYS_SEARCH_USER_PATHS = NO; 176 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 177 | CLANG_ANALYZER_NONNULL = YES; 178 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 179 | CLANG_CXX_LIBRARY = "libc++"; 180 | CLANG_ENABLE_MODULES = YES; 181 | CLANG_ENABLE_OBJC_ARC = YES; 182 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 183 | CLANG_WARN_BOOL_CONVERSION = YES; 184 | CLANG_WARN_COMMA = YES; 185 | CLANG_WARN_CONSTANT_CONVERSION = YES; 186 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 188 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 189 | CLANG_WARN_EMPTY_BODY = YES; 190 | CLANG_WARN_ENUM_CONVERSION = YES; 191 | CLANG_WARN_INFINITE_RECURSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 194 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 195 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 198 | CLANG_WARN_STRICT_PROTOTYPES = YES; 199 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 200 | CLANG_WARN_UNREACHABLE_CODE = YES; 201 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 202 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 203 | COPY_PHASE_STRIP = NO; 204 | DEBUG_INFORMATION_FORMAT = dwarf; 205 | ENABLE_STRICT_OBJC_MSGSEND = YES; 206 | ENABLE_TESTABILITY = YES; 207 | GCC_C_LANGUAGE_STANDARD = gnu99; 208 | GCC_DYNAMIC_NO_PIC = NO; 209 | GCC_NO_COMMON_BLOCKS = YES; 210 | GCC_OPTIMIZATION_LEVEL = 0; 211 | GCC_PREPROCESSOR_DEFINITIONS = ( 212 | "DEBUG=1", 213 | "$(inherited)", 214 | ); 215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 217 | GCC_WARN_UNDECLARED_SELECTOR = YES; 218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 219 | GCC_WARN_UNUSED_FUNCTION = YES; 220 | GCC_WARN_UNUSED_VARIABLE = YES; 221 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 222 | MTL_ENABLE_DEBUG_INFO = YES; 223 | ONLY_ACTIVE_ARCH = YES; 224 | SDKROOT = iphoneos; 225 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 226 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 227 | TARGETED_DEVICE_FAMILY = "1,2"; 228 | }; 229 | name = Debug; 230 | }; 231 | CE10B5CA1E0F360500E57358 /* Release */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | ALWAYS_SEARCH_USER_PATHS = NO; 235 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 236 | CLANG_ANALYZER_NONNULL = YES; 237 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 238 | CLANG_CXX_LIBRARY = "libc++"; 239 | CLANG_ENABLE_MODULES = YES; 240 | CLANG_ENABLE_OBJC_ARC = YES; 241 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 242 | CLANG_WARN_BOOL_CONVERSION = YES; 243 | CLANG_WARN_COMMA = YES; 244 | CLANG_WARN_CONSTANT_CONVERSION = YES; 245 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 246 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 247 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 248 | CLANG_WARN_EMPTY_BODY = YES; 249 | CLANG_WARN_ENUM_CONVERSION = YES; 250 | CLANG_WARN_INFINITE_RECURSION = YES; 251 | CLANG_WARN_INT_CONVERSION = YES; 252 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 253 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 254 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 256 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 257 | CLANG_WARN_STRICT_PROTOTYPES = YES; 258 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 264 | ENABLE_NS_ASSERTIONS = NO; 265 | ENABLE_STRICT_OBJC_MSGSEND = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu99; 267 | GCC_NO_COMMON_BLOCKS = YES; 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 275 | MTL_ENABLE_DEBUG_INFO = NO; 276 | SDKROOT = iphoneos; 277 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 278 | TARGETED_DEVICE_FAMILY = "1,2"; 279 | VALIDATE_PRODUCT = YES; 280 | }; 281 | name = Release; 282 | }; 283 | CE10B5CC1E0F360500E57358 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 287 | CLANG_ENABLE_MODULES = YES; 288 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 289 | DEVELOPMENT_TEAM = DX48S76K53; 290 | INFOPLIST_FILE = Example/Info.plist; 291 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 293 | PRODUCT_BUNDLE_IDENTIFIER = ru.horoko.SpaceViewExample; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 296 | SWIFT_VERSION = 5.0; 297 | }; 298 | name = Debug; 299 | }; 300 | CE10B5CD1E0F360500E57358 /* Release */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 304 | CLANG_ENABLE_MODULES = YES; 305 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 306 | DEVELOPMENT_TEAM = DX48S76K53; 307 | INFOPLIST_FILE = Example/Info.plist; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 310 | PRODUCT_BUNDLE_IDENTIFIER = ru.horoko.SpaceViewExample; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | SWIFT_VERSION = 5.0; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | CE10B5B41E0F360500E57358 /* Build configuration list for PBXProject "SpaceViewExample" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | CE10B5C91E0F360500E57358 /* Debug */, 323 | CE10B5CA1E0F360500E57358 /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | CE10B5CB1E0F360500E57358 /* Build configuration list for PBXNativeTarget "SpaceViewExample" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | CE10B5CC1E0F360500E57358 /* Debug */, 332 | CE10B5CD1E0F360500E57358 /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = CE10B5B11E0F360500E57358 /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | 91 | 98 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 119 | 126 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /SpaceView/Classes/SpaceView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpaceView.swift 3 | // SpaceView 4 | // 5 | // Created by user on 25.12.16. 6 | // Copyright © 2016 horoko. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class SpaceView { 12 | var spaceColor: UIColor = UIColor(white: 0, alpha: 0.9) 13 | var spaceDescription = "" 14 | var spaceTitle = "" 15 | var spaceHeight: CGFloat = UIDevice.current.hasNotch ? 88 : 64 16 | var canHideByTap = true 17 | var tapAction: (() -> Void)? 18 | var swipeAction: (() -> Void)? 19 | var buttonAction: (() -> Void)? 20 | var spaceView: UIView? 21 | var spaceHideTimer = 2.0 22 | var spaceShowDuration = 0.3 23 | var spaceHideDuration = 0.25 24 | var spaceReturnDuration = 0.25 25 | var spaceHideDelay = 0.0 26 | var spaceShowDelay = 0.0 27 | var spaceReturnDelay = 0.0 28 | var possibleDirectionToHide: [HideDirection] = [.left, .right, .top, .bot] 29 | var image: UIImage? 30 | var titleView = UILabel() 31 | var descriptionView = UILabel() 32 | var titleColor = UIColor.white 33 | var descriptionColor = UIColor.white 34 | var titleFont = UIFont.systemFont(ofSize: 17) 35 | var descriptionFont = UIFont.systemFont(ofSize: 17) 36 | var shouldAutoHide = true 37 | var spaceStyle: SpaceStyles? 38 | var spacePosition: SpacePosition = .top 39 | 40 | private var screenOrientation: UIInterfaceOrientation { 41 | return UIApplication.shared.statusBarOrientation 42 | } 43 | 44 | private var screenWidth: CGFloat { 45 | if UIInterfaceOrientation.portrait == screenOrientation { 46 | return UIScreen.main.bounds.size.width 47 | } else { 48 | return UIScreen.main.bounds.size.height 49 | } 50 | } 51 | 52 | private var screenHeight: CGFloat { 53 | if UIInterfaceOrientation.portrait == screenOrientation { 54 | return UIScreen.main.bounds.size.height 55 | } else { 56 | return UIScreen.main.bounds.size.width 57 | } 58 | } 59 | 60 | private var topInset: CGFloat { 61 | if #available(iOS 11.0, *) { 62 | return spaceWindow?.safeAreaInsets.top ?? 44 63 | } else { 64 | return 0 65 | } 66 | } 67 | 68 | private var spaceWindow: UIWindow? 69 | private var offsetX: CGFloat = 0.0 70 | private var offsetY: CGFloat = 0.0 71 | private var offsetAlpha: CGFloat = 0.0 72 | private var isTouched = false 73 | private var canRemoveWindow = true 74 | 75 | init(spaceOptions: [SpaceOptions]?) { 76 | if let options = spaceOptions { 77 | setOptions(options: options) 78 | } 79 | } 80 | 81 | init(title: String, spaceOptions: [SpaceOptions]?) { 82 | spaceTitle = title 83 | if let options = spaceOptions { 84 | setOptions(options: options) 85 | } 86 | } 87 | 88 | init(title: String, description: String, spaceOptions: [SpaceOptions]?) { 89 | spaceTitle = title 90 | spaceDescription = description 91 | if let options = spaceOptions { 92 | setOptions(options: options) 93 | } 94 | } 95 | 96 | deinit { 97 | NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil) 98 | } 99 | 100 | func show() { 101 | var windowFrame = CGRect(x: 0, y: 0, width: screenWidth, height: spaceHeight) 102 | var spaceFrame = CGRect(x: 0, y: 0 - spaceHeight, width: screenWidth, height: spaceHeight) 103 | if spacePosition == .bot { 104 | windowFrame = CGRect(x: 0, y: screenHeight - spaceHeight, width: screenWidth, height: spaceHeight) 105 | spaceFrame = CGRect(x: 0, y: screenHeight, width: screenWidth, height: spaceHeight) 106 | } 107 | spaceWindow = UIWindow(frame: windowFrame) 108 | spaceWindow?.windowLevel = UIWindow.Level.statusBar 109 | spaceWindow?.isHidden = false 110 | spaceWindow?.makeKeyAndVisible() 111 | spaceWindow?.windowLevel = (UIWindow.Level.statusBar + 1) 112 | if spaceView == nil { 113 | spaceView = makeContent() 114 | } 115 | spaceWindow?.addSubview(spaceView!) 116 | spaceView?.frame = spaceFrame 117 | 118 | let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) 119 | spaceView?.addGestureRecognizer(tap) 120 | spaceView?.isUserInteractionEnabled = true 121 | 122 | animateShow() 123 | addHideGesture() 124 | } 125 | 126 | @objc func handleTap(_ sender: UITapGestureRecognizer? = nil) { 127 | tapAction?() 128 | if canHideByTap { 129 | hideSpaceView(direction: .top, isManualy: true) 130 | } 131 | } 132 | 133 | @objc func handleHideGesture(_ gesture: UIPanGestureRecognizer) { 134 | guard let spaceView = spaceView else { return } 135 | switch (gesture.state) { 136 | case .ended, .cancelled, .possible: 137 | isTouched = false 138 | offsetX = spaceView.frame.origin.x 139 | offsetY = spaceView.frame.origin.y 140 | let translation = gesture.translation(in: spaceView) 141 | switch true { 142 | case translation.x > screenWidth / 4: 143 | hideSpaceView(direction: .right, isManualy: false) 144 | swipeAction?() 145 | case translation.x < -(screenWidth / 4): 146 | hideSpaceView(direction: .left, isManualy: false) 147 | swipeAction?() 148 | case spacePosition == .top ? translation.y > spaceView.h : translation.y < -(spaceView.h / 3): 149 | hideSpaceView(direction: spacePosition == .bot ? .top : .bot, isManualy: false) 150 | swipeAction?() 151 | case spacePosition == .top ? translation.y < -(spaceView.h / 3) : translation.y > spaceView.h / 3: 152 | hideSpaceView(direction: spacePosition == .top ? .top : .bot, isManualy: false) 153 | swipeAction?() 154 | default: 155 | returnView() 156 | } 157 | case.changed: 158 | self.isTouched = true 159 | let velocity = gesture.velocity(in: spaceView) 160 | let translation = gesture.translation(in: spaceView) 161 | if abs(velocity.x) > abs(velocity.y) && spaceView.frame.origin.y == 0 { 162 | for direction in possibleDirectionToHide { 163 | switch direction { 164 | case .left: 165 | if translation.x <= 0 { 166 | spaceView.frame.origin.x = translation.x 167 | spaceView.alpha = 1.00 + ((gesture.translation(in: spaceView).x) / 200) 168 | } else { 169 | if possibleDirectionToHide.contains(.right) { 170 | break 171 | } else { 172 | spaceView.frame.origin.x = 0 173 | } 174 | } 175 | case .right: 176 | if translation.x >= 0 { 177 | spaceView.frame.origin.x = translation.x 178 | spaceView.alpha = 1.00 - ((gesture.translation(in: spaceView).x) / 200) 179 | } else { 180 | if possibleDirectionToHide.contains(.left) { 181 | break 182 | } else { 183 | spaceView.frame.origin.x = 0 184 | } 185 | } 186 | default: 187 | break 188 | } 189 | } 190 | } 191 | if abs(velocity.y) > abs(velocity.x) && spaceView.frame.origin.x == 0 { 192 | for direction in possibleDirectionToHide { 193 | switch direction { 194 | case .top: 195 | if translation.y <= 0 { 196 | spaceView.frame.origin.y = translation.y 197 | spaceView.alpha = 1.00 + ((gesture.translation(in: spaceView).y) / 100) 198 | } else { 199 | if possibleDirectionToHide.contains(.bot) { 200 | break 201 | } else { 202 | spaceView.frame.origin.y = 0 203 | } 204 | } 205 | case .bot: 206 | if translation.y >= 0 { 207 | spaceView.frame.origin.y = translation.y 208 | spaceView.alpha = 1.00 - ((gesture.translation(in: spaceView).y) / 100) 209 | } else { 210 | if possibleDirectionToHide.contains(.top) { 211 | break 212 | } else { 213 | spaceView.frame.origin.y = 0 214 | } 215 | } 216 | default: 217 | break 218 | } 219 | } 220 | } 221 | break 222 | case .began: 223 | self.isTouched = true 224 | default: 225 | break 226 | } 227 | } 228 | 229 | private func hideSpaceView(direction: HideDirection, isManualy: Bool) { 230 | UIView.animate(withDuration: spaceHideDuration, delay: spaceHideDelay, options: .curveEaseOut, animations: ({ 231 | guard let spaceView = self.spaceView else { return } 232 | switch direction { 233 | case .left: 234 | if self.possibleDirectionToHide.contains(.left) { 235 | spaceView.x = -spaceView.w 236 | spaceView.alpha = 0 237 | self.canRemoveWindow = true 238 | break 239 | } 240 | self.canRemoveWindow = false 241 | case .right: 242 | if self.possibleDirectionToHide.contains(.right) { 243 | spaceView.x = spaceView.w 244 | spaceView.alpha = 0 245 | self.canRemoveWindow = true 246 | break 247 | } 248 | self.canRemoveWindow = false 249 | case .top: 250 | if self.possibleDirectionToHide.contains(.top) || isManualy { 251 | spaceView.y = self.offsetY - self.spaceHeight 252 | spaceView.alpha = 0 253 | self.canRemoveWindow = true 254 | break 255 | } 256 | self.canRemoveWindow = false 257 | case .bot: 258 | if self.possibleDirectionToHide.contains(.bot) || isManualy { 259 | spaceView.frame.origin.y = self.offsetY + self.spaceHeight 260 | spaceView.alpha = 0 261 | self.canRemoveWindow = true 262 | break 263 | } 264 | self.canRemoveWindow = false 265 | } 266 | }), completion: ({ a in 267 | if self.canRemoveWindow { 268 | self.removeWindow() 269 | } 270 | self.canRemoveWindow = true 271 | })) 272 | } 273 | 274 | private func setOptions(options: [SpaceOptions]) { 275 | for option in options { 276 | switch (option) { 277 | case let .spaceColor(color): spaceColor = color 278 | case let .spaceDescription(text): spaceDescription = text 279 | case let .spaceTitle(text) : spaceTitle = text 280 | case let .spaceHeight(height) : spaceHeight = height 281 | case let .spaceHideTimer(timer) : spaceHideTimer = timer 282 | case let .customView(view) : spaceView = view 283 | case let .tapAction(action) : tapAction = action 284 | case let .spaceShowDuration(duration) : spaceShowDuration = duration 285 | case let .spaceHideDuration(duration) : spaceHideDuration = duration 286 | case let .spaceReturnDuration(duration) : spaceReturnDuration = duration 287 | case let .spaceHideDelay(delay) : spaceHideDelay = delay 288 | case let .spaceShowDelay(delay) : spaceShowDelay = delay 289 | case let .spaceReturnDelay(delay) : spaceReturnDelay = delay 290 | case let .possibleDirectionToHide(directions) : possibleDirectionToHide = directions 291 | case let .image(img) : image = img 292 | case let .titleView(view) : titleView = view 293 | case let .descriptionView(view) : descriptionView = view 294 | case let .titleColor(color) : titleColor = color 295 | case let .descriptionColor(color) : descriptionColor = color 296 | case let .titleFont(font) : titleFont = font 297 | case let .descriptionFont(font) : descriptionFont = font 298 | case let .shouldAutoHide(should) : shouldAutoHide = should 299 | case let .spaceStyle(style) : spaceStyle = style 300 | case let .spacePosition(position) : spacePosition = position 301 | case let .swipeAction(action) : swipeAction = action 302 | case let .buttonAction(action) : buttonAction = action 303 | } 304 | } 305 | } 306 | 307 | private func animateShow() { 308 | UIView.animate(withDuration: spaceShowDuration, delay: spaceShowDelay, options: .curveEaseOut, animations: ({ 309 | self.spaceView?.y = 0 310 | }), completion: ({ _ in 311 | self.startTimer() 312 | })) 313 | } 314 | 315 | private func returnView() { 316 | UIView.animate(withDuration: spaceReturnDuration, delay: spaceReturnDelay, options: .curveEaseOut, animations: ({ 317 | self.spaceView?.x = 0 318 | self.spaceView?.y = 0 319 | self.spaceView?.alpha = 1.0 320 | }), completion: ({ _ in 321 | self.offsetX = 0 322 | self.offsetY = 0 323 | self.startTimer() 324 | })) 325 | } 326 | 327 | private func startTimer() { 328 | runThisAfterDelay(seconds: shouldAutoHide && !self.isTouched ? self.spaceHideTimer : Double(INT32_MAX)) { () -> () in 329 | self.hideSpaceView(direction: self.spacePosition == .top ? .top : .bot, isManualy: true) 330 | } 331 | } 332 | 333 | private func removeWindow() { 334 | spaceWindow?.windowLevel = (UIWindow.Level.statusBar - 1) 335 | spaceWindow?.removeFromSuperview() 336 | spaceWindow = nil 337 | spaceView = nil 338 | image = nil 339 | tapAction = nil 340 | } 341 | 342 | private func addHideGesture() { 343 | let tap = UIPanGestureRecognizer(target: self, action: #selector(handleHideGesture(_:))) 344 | self.spaceView?.addGestureRecognizer(tap) 345 | } 346 | 347 | private func makeContent() -> UIView { 348 | let contentView = UIView() 349 | if spaceStyle != nil { 350 | switch spaceStyle! { 351 | case .success: 352 | contentView.backgroundColor = UIColor(red: 27.0/255.0, green: 94.0/255.0, blue: 32.0/255.0, alpha: 0.90) 353 | case .error: 354 | contentView.backgroundColor = UIColor(red: 183.0/255.0, green: 28.0/255.0, blue: 28.0/255.0, alpha: 0.90) 355 | case .warning: 356 | contentView.backgroundColor = UIColor(red: 245.0/255.0, green: 127.0/255.0, blue: 23.0/255.0, alpha: 0.90) 357 | } 358 | } else { 359 | contentView.backgroundColor = spaceColor 360 | } 361 | 362 | var imageView: UIImageView? = nil 363 | 364 | if image != nil { 365 | imageView = UIImageView() 366 | imageView!.image = image 367 | contentView.addSubview(imageView!) 368 | imageView!.translatesAutoresizingMaskIntoConstraints = false 369 | 370 | let imageCenterY = NSLayoutConstraint(item: imageView!, 371 | attribute: NSLayoutConstraint.Attribute.centerY, 372 | relatedBy: NSLayoutConstraint.Relation.equal, 373 | toItem: contentView, 374 | attribute: NSLayoutConstraint.Attribute.centerY, 375 | multiplier: 1, 376 | constant: 0) 377 | let imageLeading = NSLayoutConstraint(item: imageView!, 378 | attribute: NSLayoutConstraint.Attribute.leading, 379 | relatedBy: NSLayoutConstraint.Relation.equal, 380 | toItem: contentView, 381 | attribute: NSLayoutConstraint.Attribute.leading, 382 | multiplier: 1, 383 | constant: 8) 384 | let imageHeight = NSLayoutConstraint(item: imageView!, 385 | attribute: NSLayoutConstraint.Attribute.height, 386 | relatedBy: NSLayoutConstraint.Relation.equal, 387 | toItem: nil, 388 | attribute: NSLayoutConstraint.Attribute.notAnAttribute, 389 | multiplier: 1, 390 | constant: 35) 391 | let imageWidth = NSLayoutConstraint(item: imageView!, 392 | attribute: NSLayoutConstraint.Attribute.width, 393 | relatedBy: NSLayoutConstraint.Relation.equal, 394 | toItem: nil, 395 | attribute: NSLayoutConstraint.Attribute.notAnAttribute, 396 | multiplier: 1, 397 | constant: 35) 398 | contentView.addConstraints([imageCenterY, imageLeading, imageHeight, imageWidth]) 399 | } 400 | 401 | titleView.text = spaceTitle 402 | titleView.textColor = .white 403 | titleView.numberOfLines = 2 404 | titleView.textAlignment = .center 405 | contentView.addSubview(titleView) 406 | titleView.translatesAutoresizingMaskIntoConstraints = false 407 | 408 | let titleCenterY = NSLayoutConstraint(item: titleView, 409 | attribute: NSLayoutConstraint.Attribute.centerY, 410 | relatedBy: NSLayoutConstraint.Relation.equal, 411 | toItem: contentView, 412 | attribute: NSLayoutConstraint.Attribute.centerY, 413 | multiplier: 1, 414 | constant: 0) 415 | var titleLeading = NSLayoutConstraint(item: titleView, 416 | attribute: NSLayoutConstraint.Attribute.leading, 417 | relatedBy: NSLayoutConstraint.Relation.equal, 418 | toItem: contentView, 419 | attribute: NSLayoutConstraint.Attribute.leading, 420 | multiplier: 1, 421 | constant: 16) 422 | let titleTrailing = NSLayoutConstraint(item: titleView, 423 | attribute: NSLayoutConstraint.Attribute.trailing, 424 | relatedBy: NSLayoutConstraint.Relation.equal, 425 | toItem: contentView, 426 | attribute: NSLayoutConstraint.Attribute.trailing, 427 | multiplier: 1, 428 | constant: -16) 429 | 430 | if let imgView = imageView { 431 | titleLeading = NSLayoutConstraint(item: titleView, 432 | attribute: NSLayoutConstraint.Attribute.leading, 433 | relatedBy: NSLayoutConstraint.Relation.equal, 434 | toItem: imgView, 435 | attribute: NSLayoutConstraint.Attribute.trailing, 436 | multiplier: 1, 437 | constant: 8) 438 | } 439 | 440 | if !spaceDescription.isEmpty { 441 | descriptionView.text = spaceDescription 442 | descriptionView.textColor = .white 443 | descriptionView.lineBreakMode = .byTruncatingTail 444 | descriptionView.numberOfLines = 2 445 | descriptionView.textAlignment = .center 446 | contentView.addSubview(descriptionView) 447 | 448 | descriptionView.translatesAutoresizingMaskIntoConstraints = false 449 | let titleTop = NSLayoutConstraint(item: titleView, 450 | attribute: NSLayoutConstraint.Attribute.top, 451 | relatedBy: NSLayoutConstraint.Relation.equal, 452 | toItem: contentView, 453 | attribute: NSLayoutConstraint.Attribute.top, 454 | multiplier: 1, 455 | constant: UIDevice.current.hasNotch ? topInset - 8 : 8) 456 | 457 | let descrTop = NSLayoutConstraint(item: descriptionView, 458 | attribute: NSLayoutConstraint.Attribute.top, 459 | relatedBy: NSLayoutConstraint.Relation.lessThanOrEqual, 460 | toItem: titleView, 461 | attribute: NSLayoutConstraint.Attribute.bottom, 462 | multiplier: 1, 463 | constant: 8) 464 | let descBot = NSLayoutConstraint(item: descriptionView, 465 | attribute: NSLayoutConstraint.Attribute.bottom, 466 | relatedBy: NSLayoutConstraint.Relation.lessThanOrEqual, 467 | toItem: contentView, 468 | attribute: NSLayoutConstraint.Attribute.bottom, 469 | multiplier: 1, 470 | constant: -8) 471 | var descLeading = NSLayoutConstraint(item: descriptionView, 472 | attribute: NSLayoutConstraint.Attribute.leading, 473 | relatedBy: NSLayoutConstraint.Relation.equal, 474 | toItem: contentView, 475 | attribute: NSLayoutConstraint.Attribute.leading, 476 | multiplier: 1, 477 | constant: 16) 478 | let descTrailing = NSLayoutConstraint(item: descriptionView, 479 | attribute: NSLayoutConstraint.Attribute.trailing, 480 | relatedBy: NSLayoutConstraint.Relation.equal, 481 | toItem: contentView, 482 | attribute: NSLayoutConstraint.Attribute.trailing, 483 | multiplier: 1, 484 | constant: -16) 485 | if let imgView = imageView { 486 | descLeading = NSLayoutConstraint(item: descriptionView, 487 | attribute: NSLayoutConstraint.Attribute.leading, 488 | relatedBy: NSLayoutConstraint.Relation.equal, 489 | toItem: imgView, 490 | attribute: NSLayoutConstraint.Attribute.trailing, 491 | multiplier: 1, 492 | constant: 16) 493 | } 494 | contentView.addConstraints([titleTop, titleLeading, titleTrailing, descrTop, descBot, descLeading, descTrailing]) 495 | } else { 496 | contentView.addConstraints([titleCenterY, titleLeading, titleTrailing]) 497 | } 498 | 499 | return contentView 500 | } 501 | 502 | private func runThisAfterDelay(seconds: Double, after: @escaping () -> ()) { 503 | let time = DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) 504 | DispatchQueue.main.asyncAfter(deadline: time, execute: after) 505 | } 506 | } 507 | 508 | public enum HideDirection { 509 | case right 510 | case left 511 | case top 512 | case bot 513 | } 514 | 515 | public enum SpacePosition { 516 | case top 517 | case bot 518 | } 519 | 520 | public enum SpaceStyles { 521 | case success 522 | case error 523 | case warning 524 | } 525 | 526 | public enum SpaceOptions { 527 | case spaceColor(color: UIColor) 528 | case spaceDescription(text: String) 529 | case spaceTitle(text: String) 530 | case spaceHeight(height: CGFloat) 531 | case spaceHideTimer(timer: Double) 532 | case customView(view: UIView) 533 | case tapAction(() -> ()) 534 | case spaceShowDuration(duration: Double) 535 | case spaceHideDuration(duration: Double) 536 | case spaceReturnDuration(duration: Double) 537 | case spaceHideDelay(delay: Double) 538 | case spaceShowDelay(delay: Double) 539 | case spaceReturnDelay(delay: Double) 540 | case possibleDirectionToHide([HideDirection]) 541 | case image(img: UIImage?) 542 | case titleView(view: UILabel) 543 | case descriptionView(view: UILabel) 544 | case titleColor(color: UIColor) 545 | case descriptionColor(color: UIColor) 546 | case titleFont(font: UIFont) 547 | case descriptionFont(font: UIFont) 548 | case shouldAutoHide(should: Bool) 549 | case spaceStyle(style: SpaceStyles?) 550 | case spacePosition(position: SpacePosition) 551 | case swipeAction(() -> ()) 552 | case buttonAction(() -> ()) 553 | } 554 | 555 | internal extension UIView { 556 | var x: CGFloat { 557 | get { 558 | return frame.origin.x 559 | } set { 560 | frame = CGRect(x: newValue, y: y, width: w, height: h) 561 | } 562 | } 563 | 564 | var y: CGFloat { 565 | get { 566 | return frame.origin.y 567 | } set { 568 | frame = CGRect(x: x, y: newValue, width: w, height: h) 569 | } 570 | } 571 | 572 | var w: CGFloat { 573 | get { 574 | return frame.size.width 575 | } set { 576 | frame = CGRect(x: x, y: y, width: newValue, height: h) 577 | } 578 | } 579 | 580 | var h: CGFloat { 581 | get { 582 | return frame.size.height 583 | } set { 584 | frame = CGRect(x: x, y: y, width: w, height: newValue) 585 | } 586 | } 587 | } 588 | 589 | public extension UIViewController { 590 | func showSpace(spaceOptions: [SpaceOptions]?) { 591 | SpaceView(spaceOptions: nil).show() 592 | } 593 | 594 | func showSpace(title: String, spaceOptions: [SpaceOptions]?) { 595 | SpaceView(title: title, spaceOptions: spaceOptions).show() 596 | } 597 | 598 | func showSpace(title: String, description: String, spaceOptions: [SpaceOptions]?) { 599 | SpaceView(title: title, description: description, spaceOptions: spaceOptions).show() 600 | } 601 | } 602 | 603 | internal extension UIDevice { 604 | var hasNotch: Bool { 605 | if #available(iOS 11.0, *) { 606 | return UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0 > 20 607 | } else { 608 | return false 609 | } 610 | } 611 | } 612 | --------------------------------------------------------------------------------