├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── KRTabBar
│ ├── KRTabBarController.swift
│ └── KRTabBarItem.swift
├── Tests
├── KRTabBarTests
│ ├── KRTabBarTests.swift
│ └── XCTestManifests.swift
└── LinuxMain.swift
└── images
├── KRTabBar-01.png
├── KRTabBar-02.png
├── KRTabBar-03.png
├── KRTabBar-04.png
├── KRTabBarGIF.gif
├── KRTabBarGIF2.gif
├── baritem1.png
├── baritem2.png
└── controller.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Kerolles Roshdi
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.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "KRTabBar",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "KRTabBar",
12 | targets: ["KRTabBar"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "KRTabBar",
23 | dependencies: []),
24 | .testTarget(
25 | name: "KRTabBarTests",
26 | dependencies: ["KRTabBar"]),
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KRTabBar
2 |
3 |    
4 |   [](https://twitter.com/intent/follow?screen_name=kerollesroshdi) [](https://www.codetriage.com/kerollesroshdi/krtabbar)
5 | 
6 |
7 | This package provides a beautiful easy to use UITabBarController
8 |
9 |
10 |
11 |
12 |
13 | ## Installation
14 | ### Swift Package Manager
15 | The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler.
16 | To integrate using Apple's Swift package manager from xcode :
17 |
18 | File -> Swift Packages -> Add Package Dependency...
19 |
20 | enter package URL : https://github.com/kerollesroshdi/KRTabBar.git , choose the latest release
21 |
22 | ## Usage
23 | #### set UITabBarController class to KRTabBarController
24 |
25 |
26 | #### set UITabBarItem class to KRTabBarItem
27 |
28 |
29 | #### set UITabBarItem Color and image
30 |
31 |
32 | ## Licence
33 | KRTabBar is released under an MIT license. See [License](https://github.com/kerollesroshdi/KRTabBar/blob/master/LICENSE) for more information.
34 |
--------------------------------------------------------------------------------
/Sources/KRTabBar/KRTabBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KRTabBar.swift
3 | // KRTabBarController
4 | //
5 | // Created by Kerolles Roshdi on 2/14/20.
6 | // Copyright © 2020 Kerolles Roshdi. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @available(iOS 9.0, *)
12 | class KRTabBarController: UITabBarController, UITabBarControllerDelegate {
13 |
14 | private var selectedTab: Int = 0
15 |
16 | private var buttons = [UIButton]()
17 | private var buttonsColors = [UIColor]()
18 |
19 | private let customTabBarView: UIView = {
20 | let view = UIView(frame: .zero)
21 | view.backgroundColor = .systemBackground
22 | view.clipsToBounds = true
23 | view.translatesAutoresizingMaskIntoConstraints = false
24 | return view
25 | }()
26 |
27 | private let indexView: UIView = {
28 | let view = UIView(frame: .zero)
29 | view.layer.shadowOpacity = 1
30 | view.layer.shadowOffset = CGSize(width: 2, height: 2)
31 | view.layer.shadowRadius = 10
32 | view.translatesAutoresizingMaskIntoConstraints = false
33 | return view
34 | }()
35 |
36 | private let stackView: UIStackView = {
37 | let stackView = UIStackView()
38 | stackView.alignment = .center
39 | stackView.distribution = .fillProportionally
40 | stackView.axis = .horizontal
41 | stackView.translatesAutoresizingMaskIntoConstraints = false
42 | return stackView
43 | }()
44 |
45 | override var viewControllers: [UIViewController]? {
46 | didSet {
47 | createButtonsStack(viewControllers!)
48 | }
49 | }
50 |
51 | override func viewDidLoad() {
52 | super.viewDidLoad()
53 | tabBar.isHidden = true
54 | addCustomTabBarView()
55 | createButtonsStack(viewControllers!)
56 | autolayout()
57 | }
58 |
59 | override func viewDidLayoutSubviews() {
60 | super.viewDidLayoutSubviews()
61 | customTabBarView.layer.cornerRadius = customTabBarView.frame.size.height / 2
62 | indexView.layer.cornerRadius = indexView.frame.size.height / 2.5
63 | indexView.backgroundColor = buttonsColors[selectedTab]
64 | indexView.layer.shadowColor = buttonsColors[selectedTab].cgColor
65 | }
66 |
67 | private func createButtonsStack(_ viewControllers: [UIViewController]) {
68 |
69 | // clean :
70 | buttons.removeAll()
71 | buttonsColors.removeAll()
72 |
73 | stackView.arrangedSubviews.forEach {
74 | stackView.removeArrangedSubview($0)
75 | $0.removeFromSuperview()
76 | }
77 |
78 | for (index, viewController) in viewControllers.enumerated() {
79 | guard let tabBarItem = viewController.tabBarItem as? KRTabBarItem else {
80 | assertionFailure("TabBarItems class must be KRTabBarItem")
81 | return
82 | }
83 | buttonsColors.append(tabBarItem.backgroundColor)
84 |
85 | let button = UIButton()
86 | button.tag = index
87 | button.addTarget(self, action: #selector(didSelectIndex(sender:)), for: .touchUpInside)
88 | let image = viewController.tabBarItem.image?.withRenderingMode(.alwaysTemplate)
89 | button.imageView?.tintColor = tabBarItem.iconColor
90 | button.setImage(image, for: .normal)
91 | button.translatesAutoresizingMaskIntoConstraints = false
92 | stackView.addArrangedSubview(button)
93 | buttons.append(button)
94 | }
95 |
96 | view.setNeedsLayout()
97 | }
98 |
99 | private func autolayout() {
100 | let margins = view.layoutMarginsGuide
101 | NSLayoutConstraint.activate([
102 | customTabBarView.widthAnchor.constraint(equalTo: tabBar.widthAnchor, constant: -20),
103 | customTabBarView.heightAnchor.constraint(equalToConstant: 70),
104 | customTabBarView.bottomAnchor.constraint(equalTo: margins.bottomAnchor, constant: -10),
105 | customTabBarView.centerXAnchor.constraint(equalTo: tabBar.centerXAnchor),
106 | stackView.leadingAnchor.constraint(equalTo: customTabBarView.leadingAnchor),
107 | stackView.trailingAnchor.constraint(equalTo: customTabBarView.trailingAnchor),
108 | stackView.heightAnchor.constraint(equalTo: customTabBarView.heightAnchor),
109 | indexView.widthAnchor.constraint(equalToConstant: customTabBarView.bounds.height),
110 | indexView.heightAnchor.constraint(equalToConstant: customTabBarView.bounds.height),
111 | indexView.centerYAnchor.constraint(equalTo: customTabBarView.centerYAnchor),
112 | indexView.centerXAnchor.constraint(equalTo: buttons.first?.centerXAnchor ?? customTabBarView.centerXAnchor)
113 | ])
114 | }
115 |
116 | private func addCustomTabBarView() {
117 | customTabBarView.frame = tabBar.frame
118 | indexView.frame = tabBar.frame
119 | view.addSubview(customTabBarView)
120 |
121 | view.bringSubviewToFront(self.tabBar)
122 |
123 | customTabBarView.addSubview(indexView)
124 | customTabBarView.addSubview(stackView)
125 | }
126 |
127 | @objc private func didSelectIndex(sender: UIButton) {
128 | let index = sender.tag
129 | self.selectedIndex = index
130 | self.selectedTab = index
131 | UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
132 | self.indexView.center.x = self.buttons[index].center.x
133 | self.indexView.backgroundColor = self.buttonsColors[index]
134 | self.indexView.layer.shadowColor = self.buttonsColors[index].cgColor
135 | }, completion: nil)
136 | }
137 |
138 |
139 | // Delegate:
140 | override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
141 | guard
142 | let items = tabBar.items,
143 | let index = items.firstIndex(of: item)
144 | else {
145 | print("not found")
146 | return
147 | }
148 | didSelectIndex(sender: self.buttons[index])
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/Sources/KRTabBar/KRTabBarItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KRTabBarItem.swift
3 | // KRTabBarController
4 | //
5 | // Created by Kerolles Roshdi on 2/15/20.
6 | // Copyright © 2020 Kerolles Roshdi. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class KRTabBarItem: UITabBarItem {
12 | @IBInspectable
13 | var backgroundColor: UIColor = UIColor.systemBlue
14 |
15 | @IBInspectable
16 | var iconColor: UIColor = .darkText
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/KRTabBarTests/KRTabBarTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import KRTabBar
3 |
4 | final class KRTabBarTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | // XCTAssertEqual(KRTabBar().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/KRTabBarTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(KRTabBarTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import KRTabBarTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += KRTabBarTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/images/KRTabBar-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/KRTabBar-01.png
--------------------------------------------------------------------------------
/images/KRTabBar-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/KRTabBar-02.png
--------------------------------------------------------------------------------
/images/KRTabBar-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/KRTabBar-03.png
--------------------------------------------------------------------------------
/images/KRTabBar-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/KRTabBar-04.png
--------------------------------------------------------------------------------
/images/KRTabBarGIF.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/KRTabBarGIF.gif
--------------------------------------------------------------------------------
/images/KRTabBarGIF2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/KRTabBarGIF2.gif
--------------------------------------------------------------------------------
/images/baritem1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/baritem1.png
--------------------------------------------------------------------------------
/images/baritem2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/baritem2.png
--------------------------------------------------------------------------------
/images/controller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerollesroshdi/KRTabBar/63f58eda95ec52bd78c6315bc3f0d27b0952771a/images/controller.png
--------------------------------------------------------------------------------