├── .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 | [![Tutorials on YouTube](https://cdn.ivanvorobei.by/github/readme/youtube-preview.jpg)](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 | --------------------------------------------------------------------------------