├── 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 | [](https://travis-ci.org/Xopoko/SpaceView)
4 | [](http://cocoapods.org/pods/SpaceView)
5 | [](http://cocoapods.org/pods/SpaceView)
6 | [](http://cocoapods.org/pods/SpaceView)
7 |
8 | 
9 |
10 | ## Example
11 |
12 |
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 |
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 |
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 |
--------------------------------------------------------------------------------