├── .gitignore
├── LICENSE
├── Package.swift
├── README.md
├── Resources
├── Navigation Bar.jpg
└── Preview.gif
├── SPFakeBar.podspec
└── Source
└── SPFakeBar
├── SPFakeBarNavigationStyle.swift
└── SPFakeBarView.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ivan Vorobei
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "SPFakeBar",
6 | platforms: [
7 | .iOS(.v10)
8 | ],
9 | products: [
10 | .library(name: "SPFakeBar", targets: ["SPFakeBar"])
11 | ],
12 | targets: [
13 | .target(name: "SPFakeBar", dependencies: [], path: "Source/SPFakeBar")
14 | ]
15 | )
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SPFakeBar
2 |
3 |
4 |
5 | ### About
6 |
7 | Very similar to original Navigation Bar. Full customisable - height, buttons and many other. Used it as simple view.
8 |
9 | Not support transition and native back button.
10 |
11 | If you like the project, don't forget to `put star ★` and follow me on GitHub:
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ## Navigate
23 |
24 | - [Requirements](#requirements)
25 | - [Installation](#installation)
26 | - [CocoaPods](#cocoapods)
27 | - [Manually](#manually)
28 | - [Usage](#usage)
29 | - [Other Projects +gif](#my-projects)
30 | - [SPAlert](#spalert)
31 | - [SPLarkController](#splarkcontroller)
32 | - [SPPermission](#sppermission)
33 | - [Awesome iOS UI](https://github.com/ivanvorobei/awesome-ios-ui)
34 | - [Other Projects](#other-projects)
35 | - [Russian Community](#russian-community)
36 |
37 | ## Requirements
38 |
39 | Swift `4.2` & `5.0`. Ready for use on iOS 10+
40 |
41 | ## Installation
42 |
43 | ### CocoaPods:
44 |
45 | [CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate `SPFakeBar` into your Xcode project using CocoaPods, specify it in your `Podfile`:
46 |
47 | ```ruby
48 | pod 'SPFakeBar'
49 | ```
50 |
51 | ### Manually
52 |
53 | If you prefer not to use any of the aforementioned dependency managers, you can integrate `SPFakeBar` into your project manually. Put `Source/SPFakeBar` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`.
54 |
55 | ## Usage
56 |
57 | You may want to add a navigation bar to your modal controller. Since it became impossible to change or customize the native controller in swift 4 (I couldn’t even find a way to change the height of the bar), I had to recreate navigation bar from the ground up. Visually it looks real, but it doesn’t execute the necessary functions:
58 |
59 | ```swift
60 | import UIKit
61 | import SPFakeBar
62 |
63 | class ModalController: UIViewController {
64 |
65 | let navBar = SPFakeBarView(style: .stork)
66 |
67 | override func viewDidLoad() {
68 | super.viewDidLoad()
69 |
70 | self.view.backgroundColor = UIColor.white
71 |
72 | self.navBar.titleLabel.text = "Title"
73 | self.navBar.leftButton.setTitle("Cancel", for: .normal)
74 | self.navBar.leftButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
75 |
76 | self.view.addSubview(self.navBar)
77 | }
78 | }
79 | ```
80 |
81 | You only need to add a navigation bar to the main view, it will automatically layout. Use style `.stork` in init of `SPFakeBarView`. Here is visual preview with Navigation Bar and without it:
82 |
83 |
84 |
85 | ## Other Projects
86 |
87 | I love being helpful. Here I have provided a list of libraries that I keep up to date. For see `video previews` of libraries without install open [opensource.ivanvorobei.by](https://opensource.ivanvorobei.by) website.
88 | I have libraries with native interface and managing permissions. Also available pack of useful extensions for boost your development process.
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | ## Russian Community
100 |
101 | Подписывайся в телеграмм-канал, если хочешь получать уведомления о новых туториалах.
102 | Со сложными и непонятными задачами помогут в чате.
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | Видео-туториалы выклыдываю на [YouTube](https://tutorials.ivanvorobei.by/youtube):
114 |
115 | [](https://tutorials.ivanvorobei.by/youtube)
116 |
--------------------------------------------------------------------------------
/Resources/Navigation Bar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivanvorobei/SPFakeBar/88fede8343b9f939741f4421c4d4344c95fa7518/Resources/Navigation Bar.jpg
--------------------------------------------------------------------------------
/Resources/Preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivanvorobei/SPFakeBar/88fede8343b9f939741f4421c4d4344c95fa7518/Resources/Preview.gif
--------------------------------------------------------------------------------
/SPFakeBar.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "SPFakeBar"
4 | s.version = "1.0.9"
5 | s.summary = "Fake navigation bar with full customize"
6 | s.homepage = "https://github.com/ivanvorobei/SPFakeBar"
7 | s.source = { :git => "https://github.com/ivanvorobei/SPFakeBar.git", :tag => s.version }
8 | s.license = { :type => "MIT", :file => "LICENSE" }
9 |
10 | s.author = { "Ivan Vorobei" => "hello@ivanvorobei.by" }
11 |
12 | s.platform = :ios
13 | s.ios.framework = 'UIKit'
14 | s.swift_version = ['4.2', '5.0']
15 | s.ios.deployment_target = "10.0"
16 |
17 | s.source_files = "Source/SPFakeBar/**/*.swift"
18 |
19 | end
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Source/SPFakeBar/SPFakeBarNavigationStyle.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | // Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to deal
6 | // in the Software without restriction, including without limitation the rights
7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | // copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in all
12 | // copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | // SOFTWARE.
21 |
22 | import UIKit
23 |
24 | public enum SPFakeBarNavigationStyle {
25 |
26 | case large
27 | case small
28 | case stork
29 | case noContent
30 | }
31 |
--------------------------------------------------------------------------------
/Source/SPFakeBar/SPFakeBarView.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | // Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to deal
6 | // in the Software without restriction, including without limitation the rights
7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | // copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in all
12 | // copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | // SOFTWARE.
21 |
22 | import UIKit
23 |
24 | open class SPFakeBarView: UIView {
25 |
26 | public var style: SPFakeBarNavigationStyle = .small {
27 | didSet {
28 | self.updateStyle()
29 | }
30 | }
31 |
32 | private var settedHeight: CGFloat = 0
33 | public var height: CGFloat {
34 | get {
35 | return (self.settedHeight) + (self.addStatusBarHeight ? UIApplication.shared.statusBarFrame.height : 0)
36 | }
37 | set {
38 | self.settedHeight = newValue
39 | self.updateHeight()
40 | }
41 |
42 | }
43 |
44 | public var addStatusBarHeight: Bool = true {
45 | didSet {
46 | self.updateHeight()
47 | }
48 | }
49 |
50 | public var elementsColor: UIColor = SPFakeBarView.navigationElementsColor {
51 | didSet {
52 | self.leftButton.setTitleColor(self.elementsColor, for: .normal)
53 | self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
54 | self.rightButton.setTitleColor(self.elementsColor, for: .normal)
55 | self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
56 | }
57 | }
58 |
59 | public var closeButtonPossition: CloseButtonPosition = .none {
60 | didSet {
61 | self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
62 | self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
63 | switch self.closeButtonPossition {
64 | case .left:
65 | self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
66 | case .right:
67 | self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
68 | case .none:
69 | break
70 | }
71 | }
72 | }
73 |
74 | public var titleLabel = UILabel.init()
75 | public var subtitleLabel = UILabel.init()
76 | public var leftButton = UIButton.init()
77 | public var rightButton = UIButton.init()
78 |
79 |
80 | public let separatorView = UIView()
81 | public var blurView: UIVisualEffectView!
82 |
83 | private var titleBottomConstraint: NSLayoutConstraint?
84 | private var heightConstraint: NSLayoutConstraint?
85 | private var topConstraint: NSLayoutConstraint?
86 | private var leadingConstraint: NSLayoutConstraint?
87 | private var trailingConstraint: NSLayoutConstraint?
88 |
89 | public init(style: SPFakeBarNavigationStyle) {
90 | super.init(frame: CGRect.zero)
91 | self.style = style
92 | self.commonInit()
93 | }
94 |
95 | required public init?(coder aDecoder: NSCoder) {
96 | super.init(coder: aDecoder)
97 | self.style = .small
98 | self.commonInit()
99 | }
100 |
101 | private func commonInit() {
102 | self.translatesAutoresizingMaskIntoConstraints = false
103 |
104 | /*
105 | REPLACE WITH IT AFTER REALESE iOS 13
106 |
107 | if #available(iOS 13.0, *) {
108 | let effect = UIBlurEffect(style: UIBlurEffect.Style.systemThickMaterial)
109 | return UIVisualEffectView.init(effect: effect)
110 | } else {
111 | let effect = UIBlurEffect(style: .extraLight)
112 | return UIVisualEffectView.init(effect: effect)
113 | }
114 |
115 | */
116 |
117 | var isDarkMode: Bool {
118 | if #available(iOS 12.0, *) {
119 | if self.traitCollection.userInterfaceStyle == .dark {
120 | return true
121 | } else {
122 | return false
123 | }
124 | } else {
125 | return false
126 | }
127 | }
128 |
129 | let effect = UIBlurEffect(style: isDarkMode ? .dark : .extraLight)
130 | self.blurView = UIVisualEffectView.init(effect: effect)
131 | self.addSubview(self.blurView)
132 | self.blurView.translatesAutoresizingMaskIntoConstraints = false
133 | self.blurView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
134 | self.blurView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
135 | self.blurView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
136 | self.blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
137 |
138 | self.addSubview(self.separatorView)
139 | self.separatorView.backgroundColor = UIColor.init(red: 191 / 255.0, green: 191 / 255.0, blue: 191 / 255.0, alpha: 1)
140 | self.separatorView.translatesAutoresizingMaskIntoConstraints = false
141 | self.separatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
142 | self.separatorView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
143 | self.separatorView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
144 | self.separatorView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
145 |
146 | self.addSubview(self.titleLabel)
147 | self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
148 | self.titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
149 | self.titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
150 | self.titleBottomConstraint = self.titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12)
151 | self.titleBottomConstraint?.isActive = true
152 |
153 | self.addSubview(self.subtitleLabel)
154 | self.subtitleLabel.textColor = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 146 / 255.0, alpha: 1)
155 | self.subtitleLabel.font = UIFont.systemFont(ofSize: 13, weight: .semibold)
156 | self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
157 | self.subtitleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
158 | self.subtitleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
159 | self.subtitleLabel.bottomAnchor.constraint(equalTo: self.titleLabel.topAnchor, constant: 0).isActive = true
160 |
161 | self.leftButton.setTitleColor(self.elementsColor, for: .normal)
162 | self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
163 | self.leftButton.titleLabel?.textAlignment = .left
164 | self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
165 | self.leftButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 17, bottom: 0, right: 0)
166 | self.addSubview(self.leftButton)
167 | self.leftButton.translatesAutoresizingMaskIntoConstraints = false
168 | self.leftButton.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
169 | self.leftButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
170 |
171 | self.rightButton.setTitleColor(self.elementsColor, for: .normal)
172 | self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
173 | self.rightButton.titleLabel?.textAlignment = .right
174 | self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
175 | self.rightButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16)
176 | self.addSubview(self.rightButton)
177 | self.rightButton.translatesAutoresizingMaskIntoConstraints = false
178 | self.rightButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
179 | self.rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
180 |
181 | self.closeButtonPossition = .none
182 |
183 | self.setContraints()
184 | self.updateStyle()
185 | }
186 |
187 | override open func layoutSubviews() {
188 | super.layoutSubviews()
189 | self.setContraints()
190 | }
191 |
192 | private func setContraints() {
193 | if let superview = self.superview {
194 | if self.topConstraint == nil {
195 |
196 | self.topConstraint = self.topAnchor.constraint(equalTo: superview.topAnchor)
197 | self.topConstraint?.isActive = true
198 | self.leadingConstraint = self.leadingAnchor.constraint(equalTo: superview.leadingAnchor)
199 | self.leadingConstraint?.isActive = true
200 | self.trailingConstraint = self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
201 | self.trailingConstraint?.isActive = true
202 |
203 | self.heightConstraint = self.heightAnchor.constraint(equalToConstant: self.height)
204 | self.heightConstraint?.isActive = true
205 | self.updateHeight()
206 | }
207 | }
208 | }
209 |
210 | private func updateStyle() {
211 | switch self.style {
212 | case .small:
213 | if UIApplication.shared.statusBarFrame.height == 44 {
214 | self.height = 88 - 44
215 | self.titleBottomConstraint?.constant = -12
216 | } else {
217 | self.height = 64 - 20
218 | self.titleBottomConstraint?.constant = -12
219 | }
220 | self.addStatusBarHeight = true
221 | self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
222 | self.titleLabel.textAlignment = .center
223 | case .stork:
224 | self.height = 66
225 | self.titleBottomConstraint?.constant = -12
226 | self.addStatusBarHeight = false
227 | self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
228 | self.titleLabel.textAlignment = .center
229 | case .large:
230 | if UIApplication.shared.statusBarFrame.height == 44 {
231 | self.height = 140 - 44
232 | self.titleBottomConstraint?.constant = -8
233 | } else {
234 | self.height = 116 - 20
235 | self.titleBottomConstraint?.constant = -4
236 | }
237 | self.addStatusBarHeight = true
238 | self.titleLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
239 | self.titleLabel.textAlignment = .left
240 | break
241 | case .noContent:
242 | self.height = 0
243 | self.addStatusBarHeight = true
244 | }
245 |
246 | self.updateConstraints()
247 | }
248 |
249 | private func updateHeight() {
250 | self.heightConstraint?.constant = self.height
251 | self.updateConstraints()
252 | }
253 |
254 | public enum CloseButtonPosition {
255 | case left
256 | case right
257 | case none
258 | }
259 | }
260 |
261 | extension SPFakeBarView {
262 |
263 | static var navigationElementsColor: UIColor {
264 | get {
265 | if UINavigationBar.appearance().tintColor != nil {
266 | return UINavigationBar.appearance().tintColor
267 | } else {
268 | return UIColor.init(red: 0 / 255.0, green: 122 / 255.0, blue: 255 / 255.0, alpha: 1)
269 | }
270 | }
271 | set {
272 | UINavigationBar.appearance().tintColor = newValue
273 | }
274 | }
275 | }
276 |
--------------------------------------------------------------------------------