├── CBTabBarController
├── .gitkeep
├── Classes
│ ├── .gitkeep
│ ├── .DS_Store
│ ├── Helpers
│ │ ├── Bundle.swift
│ │ └── CGPoint.swift
│ ├── Menu
│ │ ├── CBMenuTabItem.swift
│ │ ├── CBMenuTabButtonFactory.swift
│ │ ├── CBMenuButton.swift
│ │ ├── CBMenuController.swift
│ │ ├── CBMenuDismissAnimationController.swift
│ │ ├── CBMenuPresenter.swift
│ │ └── CBMenuPresentAnimationController.swift
│ ├── Fade
│ │ ├── CBFadeTabButtonFactory.swift
│ │ └── CBFadeTabBarButton.swift
│ ├── Animation
│ │ └── CBTabItemAnimation.swift
│ ├── CBTabButtonFactory.swift
│ ├── Flashy
│ │ ├── CBFlashyTabButtonFactory.swift
│ │ ├── Animations
│ │ │ ├── CBFlashyTabItemBasicAnimation.swift
│ │ │ ├── CBTabItemSelectAnimation.swift
│ │ │ └── CBTabItemDeselectAnimation.swift
│ │ └── CBFlashyTabBarButton.swift
│ ├── CBTabBarStyle.swift
│ ├── CBTabBarController.swift
│ ├── CBBaseTabButton.swift
│ └── CBTabBar.swift
├── .DS_Store
├── Assets
│ ├── .DS_Store
│ ├── Images
│ │ ├── btnClose.png
│ │ ├── btnMenu.png
│ │ ├── btnMenu@2x.png
│ │ └── btnClose@2x.png
│ └── Storyboard
│ │ └── CBMenuController.storyboard
├── CBFlashyTabBarController.h
└── Info.plist
├── Screenshots
├── flashy.gif
└── gooey.gif
├── Example
├── Images.xcassets
│ ├── Contents.json
│ ├── Events.imageset
│ │ ├── Events.png
│ │ ├── Events@2x.png
│ │ ├── Events@3x.png
│ │ └── Contents.json
│ ├── Search.imageset
│ │ ├── Search.png
│ │ ├── Search@2x.png
│ │ ├── Search@3x.png
│ │ └── Contents.json
│ ├── Settings.imageset
│ │ ├── Settings.png
│ │ ├── Settings@2x.png
│ │ ├── Settings@3x.png
│ │ └── Contents.json
│ ├── btnClose.imageset
│ │ ├── btnClose.png
│ │ ├── btnClose@2x.png
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Icon_60@2x.png
│ │ ├── Icon_60@3x.png
│ │ └── Contents.json
│ ├── Highlights.imageset
│ │ ├── Highlights.png
│ │ ├── Highlights@2x.png
│ │ ├── Highlights@3x.png
│ │ └── Contents.json
│ └── cuberto-logo.imageset
│ │ ├── cuberto-logo.png
│ │ ├── cuberto-logo@2x.png
│ │ ├── cuberto-logo@3x.png
│ │ └── Contents.json
├── AppDelegate.swift
├── Info.plist
├── CBSampleViewController.swift
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
└── ViewController.swift
├── CBTabBar.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ └── CBFlashyTabBarController.xcscheme
└── project.pbxproj
├── CBTabBarController.podspec
├── LICENSE
├── .gitignore
└── README.md
/CBTabBarController/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Screenshots/flashy.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Screenshots/flashy.gif
--------------------------------------------------------------------------------
/Screenshots/gooey.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Screenshots/gooey.gif
--------------------------------------------------------------------------------
/CBTabBarController/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/.DS_Store
--------------------------------------------------------------------------------
/CBTabBarController/Assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/Assets/.DS_Store
--------------------------------------------------------------------------------
/Example/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/CBTabBarController/Classes/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/Classes/.DS_Store
--------------------------------------------------------------------------------
/CBTabBarController/Assets/Images/btnClose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/Assets/Images/btnClose.png
--------------------------------------------------------------------------------
/CBTabBarController/Assets/Images/btnMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/Assets/Images/btnMenu.png
--------------------------------------------------------------------------------
/CBTabBarController/Assets/Images/btnMenu@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/Assets/Images/btnMenu@2x.png
--------------------------------------------------------------------------------
/CBTabBarController/Assets/Images/btnClose@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/CBTabBarController/Assets/Images/btnClose@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Events.imageset/Events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Events.imageset/Events.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Search.imageset/Search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Search.imageset/Search.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Events.imageset/Events@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Events.imageset/Events@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Events.imageset/Events@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Events.imageset/Events@3x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Search.imageset/Search@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Search.imageset/Search@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Search.imageset/Search@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Search.imageset/Search@3x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Settings.imageset/Settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Settings.imageset/Settings.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/btnClose.imageset/btnClose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/btnClose.imageset/btnClose.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/AppIcon.appiconset/Icon_60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/AppIcon.appiconset/Icon_60@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/AppIcon.appiconset/Icon_60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/AppIcon.appiconset/Icon_60@3x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Settings.imageset/Settings@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Settings.imageset/Settings@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Settings.imageset/Settings@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Settings.imageset/Settings@3x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/btnClose.imageset/btnClose@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/btnClose.imageset/btnClose@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Highlights.imageset/Highlights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Highlights.imageset/Highlights.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Highlights.imageset/Highlights@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Highlights.imageset/Highlights@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/Highlights.imageset/Highlights@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/Highlights.imageset/Highlights@3x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/cuberto-logo.imageset/cuberto-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/cuberto-logo.imageset/cuberto-logo.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/cuberto-logo.imageset/cuberto-logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/cuberto-logo.imageset/cuberto-logo@2x.png
--------------------------------------------------------------------------------
/Example/Images.xcassets/cuberto-logo.imageset/cuberto-logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cuberto/cb-tabbar/HEAD/Example/Images.xcassets/cuberto-logo.imageset/cuberto-logo@3x.png
--------------------------------------------------------------------------------
/CBTabBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CBTabBar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Helpers/Bundle.swift:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // Bundle.swift
4 | // liquid-swipe
5 | //
6 | // Created by Anton Skopin on 06/01/2019.
7 | //
8 | import Foundation
9 |
10 | extension Bundle {
11 | static var resourseBundle: Bundle {
12 | let frameworkBundle = Bundle(for: CBTabBar.self)
13 | return frameworkBundle
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Helpers/CGPoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 22/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension CGPoint {
12 | func offsetBy(dx: CGFloat = 0, dy: CGFloat = 0) -> CGPoint {
13 | return CGPoint(x: x + dx, y: y + dy)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuTabItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBMenuTabItem.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 20/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBMenuTabItem: UITabBarItem, CBExtendedTabItem {
12 |
13 | var attributedTitle: NSAttributedString? {
14 | return nil
15 | }
16 |
17 | var tintColor: UIColor?
18 | }
19 |
--------------------------------------------------------------------------------
/Example/Images.xcassets/btnClose.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btnClose.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "btnClose@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Fade/CBFadeTabButtonFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBFadeTabButtonFactory.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by Anton Skopin on 22/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBFadeTabButtonFactory: CBTabButtonFactory {
12 |
13 | func buttons(forItems items: [UITabBarItem]) -> [CBTabBarButton] {
14 | return items.map { CBFadeTabBarButton(item: $0) }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Example/Images.xcassets/Events.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Events.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Events@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Events@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Example/Images.xcassets/Search.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Search.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Search@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Search@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Example/Images.xcassets/Settings.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Settings.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Settings@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Settings@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Example/Images.xcassets/Highlights.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Highlights.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Highlights@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Highlights@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Example/Images.xcassets/cuberto-logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cuberto-logo.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "cuberto-logo@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "cuberto-logo@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/CBTabBarController/CBFlashyTabBarController.h:
--------------------------------------------------------------------------------
1 | //
2 | // CBFlashyTabBarController.h
3 | // CBFlashyTabBarController
4 | //
5 | // Created by Anton Skopin on 04/12/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for test.
12 | FOUNDATION_EXPORT double testVersionNumber;
13 |
14 | //! Project version string for test.
15 | FOUNDATION_EXPORT const unsigned char testVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Animation/CBTabItemAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabItemAnimation.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol CBTabItemAnimation {
12 |
13 | func playAnimation(forTabBarItem item: CBTabBarButton)
14 | func playAnimation(forTabBarItem item: CBTabBarButton, completion: (() -> Void)?)
15 |
16 | }
17 |
18 | extension CBTabItemAnimation {
19 |
20 | func playAnimation(forTabBarItem item: CBTabBarButton) {
21 | playAnimation(forTabBarItem: item, completion: nil)
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/CBTabBarController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'CBTabBarController'
3 | s.version = '0.9.5'
4 | s.summary = 'One another nice animated tabbar'
5 | s.homepage = 'https://github.com/Cuberto/cb-tabbar'
6 | s.license = 'MIT'
7 | s.author = { 'askopin@gmail.com' => 'askopin@gmail.com' }
8 | s.source = { :git => 'https://github.com/Cuberto/cb-tabbar.git', :tag => s.version.to_s }
9 | s.social_media_url = 'https://twitter.com/cuberto'
10 | s.ios.deployment_target = '10.0'
11 | s.swift_version = '4.2'
12 | s.source_files = 'CBTabBarController/Classes/**/*'
13 | s.resources = 'CBTabBarController/Assets/**/*.{png,storyboard}'
14 | end
15 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/CBTabButtonFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabBarStyle.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by Anton Skopin on 19/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | typealias CBTabBarButton = UIControl & CBTabBarButtonProtocol
12 |
13 | protocol CBTabBarButtonProtocol {
14 | init(item: UITabBarItem)
15 | var item: UITabBarItem? { get set }
16 | func setSelected(_ selected: Bool, animated: Bool)
17 | var requiredSize: CGSize? { get }
18 | }
19 |
20 |
21 | extension CBTabBarButtonProtocol {
22 | var requiredSize: CGSize? {
23 | return nil
24 | }
25 | }
26 |
27 | protocol CBTabButtonFactory {
28 | func buttons(forItems item: [UITabBarItem]) -> [CBTabBarButton]
29 | }
30 |
--------------------------------------------------------------------------------
/CBTabBarController/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.9.5
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Cuberto Design
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/Images.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 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "Icon_60@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "Icon_60@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "idiom" : "ios-marketing",
47 | "size" : "1024x1024",
48 | "scale" : "1x"
49 | }
50 | ],
51 | "info" : {
52 | "version" : 1,
53 | "author" : "xcode"
54 | }
55 | }
--------------------------------------------------------------------------------
/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by askopin@gmail.com on 11/29/2018.
6 | // Copyright (c) 2018 askopin@gmail.com. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CBTabBarController
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 | func application(_ application: UIApplication,
18 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
19 | CBTabBar.appearance().tintColor = #colorLiteral(red: 0.1176470588, green: 0.1176470588, blue: 0.431372549, alpha: 1)
20 | CBTabBar.appearance().barTintColor = .white
21 | return true
22 | }
23 |
24 | func applicationWillResignActive(_ application: UIApplication) {
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 |
29 | }
30 |
31 | func applicationWillEnterForeground(_ application: UIApplication) {
32 |
33 | }
34 |
35 | func applicationDidBecomeActive(_ application: UIApplication) {
36 |
37 | }
38 |
39 | func applicationWillTerminate(_ application: UIApplication) {
40 |
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Flashy/CBFlashyTabButtonFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBFlashyTabButtonFactory.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 20/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public struct CBFlashyTabBarConfig {
12 | let deselectedOpacity: CGFloat
13 |
14 | public init(deselectedOpacity: CGFloat) {
15 | self.deselectedOpacity = max(0.0, min(1.0, deselectedOpacity))
16 | }
17 | }
18 |
19 | class CBFlashyTabButtonFactory: CBTabButtonFactory {
20 |
21 | let config: CBFlashyTabBarConfig?
22 |
23 | init(config: CBFlashyTabBarConfig? = nil) {
24 | self.config = config
25 | }
26 |
27 | private var animationSpeed: Double = 1.0
28 |
29 | func buttons(forItems items: [UITabBarItem]) -> [CBTabBarButton] {
30 | return items.map { item -> CBTabBarButton in
31 | let button = CBFlashyTabBarButton(item: item)
32 | button.config = config
33 | button.selectAnimation = CBFlashyTabItemSelectAnimation(duration: 0.3 / animationSpeed)
34 | button.deselectAnimation = CBFlashyTabItemDeselectAnimation(duration: 0.3 / animationSpeed)
35 | return button
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuTabButtonFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBSimpleTabButtonFactory.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 20/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBMenuTabButtonFactory: CBTabButtonFactory {
12 | private let menu: CBTabMenu
13 | private let menuItem = CBMenuTabItem()
14 | weak var presentationController: UIViewController?
15 |
16 | init(menu: CBTabMenu) {
17 | self.menu = menu
18 | }
19 |
20 | func buttons(forItems items: [UITabBarItem]) -> [CBTabBarButton] {
21 | var buttons: [CBTabBarButton] = items.map { CBFadeTabBarButton(item: $0) }
22 | if buttons.count >= menu.menuButtonIndex {
23 | let menuItem = CBMenuTabItem()
24 | menuItem.image = menu.icon ?? UIImage(named: "btnMenu", in: Bundle.resourseBundle, compatibleWith: nil)
25 | menuItem.tintColor = menu.menuColor
26 | let button = CBMenuButton(item: menuItem)
27 | let presenter = CBMenuPresenter(menu: menu)
28 | button.presenter = presenter
29 | presenter.menuButton = button
30 | presenter.presentationController = presentationController
31 | buttons.insert(button, at: menu.menuButtonIndex)
32 | }
33 | return buttons
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Animated Bar
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationPortraitUpsideDown
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBMenuButton.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 20/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBMenuButton: CBBaseTabButton {
12 |
13 | private let btnSize: CGFloat = 60.0
14 | private let tabImage: UIImageView = {
15 | let imageView = UIImageView()
16 | imageView.contentMode = .center
17 | return imageView
18 | }()
19 | private let bgView = UIView()
20 | var presenter: CBMenuPresenter?
21 |
22 | override var requiredSize: CGSize? {
23 | return CGSize(width: btnSize, height: btnSize)
24 | }
25 |
26 | override var tintColor: UIColor! {
27 | didSet {
28 | bgView.backgroundColor = tintColor
29 | }
30 | }
31 |
32 | override func configure() {
33 | super.configure()
34 | bgView.frame = CGRect(origin: .zero, size: CGSize(width: btnSize, height: btnSize))
35 | tabImage.frame = bgView.frame
36 | addSubview(bgView)
37 | addSubview(tabImage)
38 | bgView.layer.cornerRadius = btnSize / 2.0
39 | tabImage.isUserInteractionEnabled = false
40 | bgView.isUserInteractionEnabled = false
41 | }
42 |
43 | override func layoutSubviews() {
44 | super.layoutSubviews()
45 | bgView.center = CGPoint(x: bounds.midX, y: bounds.midY)
46 | tabImage.frame = bgView.frame
47 | }
48 |
49 | override func didUpdateItem() {
50 | tabImage.image = item?.image
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 | *~
5 | .DS_Store
6 |
7 | ## Build generated
8 | build/
9 | DerivedData/
10 |
11 | ## Various settings
12 | *.pbxuser
13 | !default.pbxuser
14 | *.mode1v3
15 | !default.mode1v3
16 | *.mode2v3
17 | !default.mode2v3
18 | *.perspectivev3
19 | !default.perspectivev3
20 | xcuserdata/
21 |
22 | ## Other
23 | *.moved-aside
24 | *.xccheckout
25 | *.xcscmblueprint
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | .build/
44 |
45 | # CocoaPods
46 | #
47 | # We recommend against adding the Pods directory to your .gitignore. However
48 | # you should judge for yourself, the pros and cons are mentioned at:
49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
50 | #
51 | # Pods/
52 |
53 | # Carthage
54 | #
55 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
56 | # Carthage/Checkouts
57 |
58 | Carthage/Build
59 |
60 | # fastlane
61 | #
62 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
63 | # screenshots whenever they are needed.
64 | # For more information about the recommended setup visit:
65 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
66 |
67 | fastlane/report.xml
68 | fastlane/Preview.html
69 | fastlane/screenshots/**/*.png
70 | fastlane/test_output
71 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/CBTabBarStyle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabBarStyle.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 22/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol CBExtendedTabItem {
12 | var attributedTitle: NSAttributedString? { get }
13 | var tintColor: UIColor? { get }
14 | }
15 |
16 | public extension CBExtendedTabItem {
17 | var attributedTitle: NSAttributedString? {
18 | return nil
19 | }
20 | var tintColor: UIColor? {
21 | return nil
22 | }
23 | }
24 |
25 | public protocol CBTabMenuItem {
26 | var title: String? { get }
27 | var attributedTitle: NSAttributedString? { get }
28 | }
29 |
30 | public struct CBTabMenu {
31 | let menuButtonIndex: Int
32 | let menuColor: UIColor
33 | let items: [CBTabMenuItem]
34 | let icon: UIImage?
35 | let callback: (UIViewController, CBTabMenuItem) -> Void
36 |
37 | public init(menuButtonIndex: Int,
38 | menuColor: UIColor,
39 | items: [CBTabMenuItem],
40 | icon: UIImage?,
41 | callback: @escaping (UIViewController, CBTabMenuItem) -> Void){
42 | self.menuButtonIndex = menuButtonIndex
43 | self.menuColor = menuColor
44 | self.items = items
45 | self.icon = icon
46 | self.callback = callback
47 | }
48 | }
49 |
50 | public enum CBTabBarStyle {
51 | case flashy(config: CBFlashyTabBarConfig?)
52 | case fade
53 | case gooey(menu: CBTabMenu)
54 | }
55 |
56 |
57 | internal extension CBTabBarStyle {
58 | func configure(tabBar: CBTabBar, with controller: CBTabBarController) {
59 | switch self {
60 | case let .gooey(menu):
61 | let menuFactory = CBMenuTabButtonFactory(menu: menu)
62 | menuFactory.presentationController = controller
63 | tabBar.layer.borderWidth = 0.0
64 | tabBar.clipsToBounds = true
65 | tabBar.buttonFactory = menuFactory
66 | case .fade:
67 | tabBar.buttonFactory = CBFadeTabButtonFactory()
68 | case let .flashy(config):
69 | tabBar.buttonFactory = CBFlashyTabButtonFactory(config: config)
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Fade/CBFadeTabBarButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBSimpleTabBarButton.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 20/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBFadeTabBarButton: CBBaseTabButton {
12 |
13 | private let deselectedAlpha: CGFloat = 0.5
14 | private let animationDuration: Double = 0.15
15 |
16 | override func configure() {
17 | super.configure()
18 | }
19 |
20 | private var _isSelected: Bool = false
21 | override var isSelected: Bool {
22 | get {
23 | return _isSelected
24 | }
25 | set {
26 | guard newValue != _isSelected else {
27 | return
28 | }
29 | if newValue {
30 | select(animated: false)
31 | } else {
32 | deselect(animated: false)
33 | }
34 | }
35 | }
36 |
37 | override func layoutSubviews() {
38 | super.layoutSubviews()
39 | imageView?.alpha = _isSelected ? 1 : deselectedAlpha
40 | titleLabel?.alpha = _isSelected ? 1 : deselectedAlpha
41 | }
42 |
43 | override func select(animated: Bool = true) {
44 | guard !_isSelected else {
45 | return
46 | }
47 | _isSelected = true
48 | let animation: () -> Void = {[weak self] in
49 | self?.imageView?.alpha = 1
50 | self?.titleLabel?.alpha = 1
51 | }
52 | guard animated else {
53 | animation()
54 | return
55 | }
56 | UIView.animate(withDuration: animationDuration, animations: animation)
57 | }
58 |
59 | override func deselect(animated: Bool = true) {
60 | guard _isSelected else {
61 | return
62 | }
63 | _isSelected = false
64 | let animation: () -> Void = {[weak self] in
65 | self?.imageView?.alpha = self?.deselectedAlpha ?? 0
66 | self?.titleLabel?.alpha = self?.deselectedAlpha ?? 0
67 | }
68 | guard animated else {
69 | animation()
70 | return
71 | }
72 | UIView.animate(withDuration: animationDuration, animations: animation)
73 | }
74 |
75 | override func didUpdateItem() {
76 | super.didUpdateItem()
77 | setImage(item?.image?.withRenderingMode(.alwaysTemplate), for: .normal)
78 | setNeedsLayout()
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Example/CBSampleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBSampleViewController.swift
3 | // CBFlashyTabBarController_Example
4 | //
5 | // Created by Anton Skopin on 03/12/2018.
6 | // Copyright © 2018 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBSampleViewController: UIViewController {
12 |
13 | var lblTitle: UILabel = {
14 | var label = UILabel()
15 | label.textColor = #colorLiteral(red: 0.1579992771, green: 0.1818160117, blue: 0.5072338581, alpha: 1)
16 | label.font = UIFont.systemFont(ofSize: 55.0, weight: .bold)
17 | label.translatesAutoresizingMaskIntoConstraints = false
18 | return label
19 | }()
20 |
21 | var btnClose: UIButton = {
22 | var btn = UIButton()
23 | btn.tintColor = #colorLiteral(red: 0.1579992771, green: 0.1818160117, blue: 0.5072338581, alpha: 1)
24 | btn.setImage(#imageLiteral(resourceName: "btnClose").withRenderingMode(.alwaysTemplate), for: .normal)
25 | btn.translatesAutoresizingMaskIntoConstraints = false
26 | return btn
27 | }()
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 | view.backgroundColor = UIColor.white
32 | lblTitle.text = tabBarItem.title
33 | view.addSubview(lblTitle)
34 |
35 | view.addSubview(btnClose)
36 | btnClose.widthAnchor.constraint(equalToConstant: 45).isActive = true
37 | btnClose.heightAnchor.constraint(equalToConstant: 45).isActive = true
38 | let guide = view.layoutMarginsGuide
39 | btnClose.topAnchor.constraint(equalToSystemSpacingBelow: guide.topAnchor, multiplier: 1.0).isActive = true
40 | btnClose.trailingAnchor.constraint(equalToSystemSpacingAfter: guide.trailingAnchor, multiplier: 1.0).isActive = true
41 | btnClose.addTarget(self, action: #selector(btnClosePressed), for: .touchUpInside)
42 |
43 | lblTitle.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
44 | lblTitle.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
45 | view.setNeedsLayout()
46 |
47 | }
48 |
49 | func inverseColor() {
50 | view.backgroundColor = lblTitle.textColor
51 | lblTitle.textColor = UIColor.white
52 | btnClose.tintColor = .white
53 | }
54 |
55 | override var preferredStatusBarStyle: UIStatusBarStyle {
56 | return view.backgroundColor == UIColor.white ? .default : .lightContent
57 | }
58 |
59 | @objc func btnClosePressed() {
60 | navigationController?.popToRootViewController(animated: true)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBMenuController.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 21/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private class CBMenuListButton: UIButton {
12 | var menuItem: CBTabMenuItem? {
13 | didSet {
14 | if let attributedTitle = menuItem?.attributedTitle {
15 | setAttributedTitle(attributedTitle, for: .normal)
16 | } else if let title = menuItem?.title {
17 | setAttributedTitle(nil, for: .normal)
18 | setTitle(title, for: .normal)
19 | } else {
20 | setAttributedTitle(nil, for: .normal)
21 | setTitle(nil, for: .normal)
22 | }
23 | }
24 | }
25 | }
26 |
27 | public class CBMenuController: UIViewController {
28 |
29 | @IBOutlet weak var dismissButton: UIButton!
30 | @IBOutlet weak var menuContainer: UIView!
31 | @IBOutlet private weak var menuStack: UIStackView!
32 | var menu: CBTabMenu? {
33 | didSet {
34 | loadMenu()
35 | }
36 | }
37 |
38 | @IBAction func btnDismissPressed(_ sender: AnyObject) {
39 | dismiss(animated: true, completion: nil)
40 | }
41 |
42 | override public func viewDidLoad() {
43 | super.viewDidLoad()
44 | loadMenu()
45 | }
46 |
47 | override public func viewDidLayoutSubviews() {
48 | super.viewDidLayoutSubviews()
49 | dismissButton.layer.cornerRadius = min(dismissButton.frame.height/2.0, dismissButton.frame.width/2.0)
50 | }
51 |
52 | private func loadMenu() {
53 | guard let menu = menu, isViewLoaded else { return }
54 | view.backgroundColor = menu.menuColor
55 | let dismissImage = UIImage(named: "btnClose",
56 | in: Bundle.resourseBundle,
57 | compatibleWith: nil)
58 | dismissButton.backgroundColor = .white
59 | dismissButton.tintColor = menu.menuColor
60 | dismissButton.setImage(dismissImage?.withRenderingMode(.alwaysTemplate), for: .normal)
61 | for menuItem in menu.items {
62 | let button = CBMenuListButton(type: .custom)
63 | button.titleLabel?.font = UIFont.systemFont(ofSize: 17)
64 | button.setTitleColor(.white, for: .normal)
65 | button.menuItem = menuItem
66 | menuStack.addArrangedSubview(button)
67 | button.addTarget(self, action: #selector(btnMenuPressed), for: .touchUpInside)
68 | button.heightAnchor.constraint(greaterThanOrEqualToConstant: 50.0).isActive = true
69 | }
70 | }
71 |
72 | @objc private func btnMenuPressed(sender: CBMenuListButton) {
73 | if let item = sender.menuItem {
74 | menu?.callback(self, item)
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuDismissAnimationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBMenuDismissAnimationController.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 22/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 | import UIKit
9 |
10 |
11 | class CBMenuDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
12 |
13 | private let duration: Double = 0.25
14 | weak var menuButton: UIView?
15 |
16 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
17 | return duration
18 | }
19 |
20 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
21 | guard let fromView = transitionContext.view(forKey: .from),
22 | let toView = transitionContext.view(forKey: .to) else { return }
23 |
24 | let fromViewController = transitionContext.viewController(forKey: .from) as? CBMenuController
25 | let container = transitionContext.containerView
26 | let animationDismissButton = fromViewController?.dismissButton?.snapshotView(afterScreenUpdates: false)
27 | let animationPresentButton = menuButton?.snapshotView(afterScreenUpdates: true)
28 | var targetFrame: CGRect?
29 |
30 | container.insertSubview(toView, at: 0)
31 | if let menuButton = menuButton,
32 | let presentButton = animationPresentButton,
33 | let dismissButton = animationDismissButton,
34 | let sourceFrame = fromViewController?.dismissButton?.frame {
35 |
36 | targetFrame = fromView.convert(menuButton.frame, from: menuButton.superview)
37 | container.addSubview(presentButton)
38 | container.addSubview(dismissButton)
39 | fromViewController?.dismissButton?.isHidden = true
40 | presentButton.frame = sourceFrame
41 | dismissButton.frame = sourceFrame
42 | }
43 | menuButton?.isHidden = true
44 | UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
45 | fromView.alpha = 0
46 | if let targetFrame = targetFrame {
47 | animationPresentButton?.frame = targetFrame
48 | animationDismissButton?.frame = targetFrame
49 | animationDismissButton?.alpha = 0
50 | }
51 | }) { (_) in
52 | self.menuButton?.isHidden = false
53 | animationPresentButton?.removeFromSuperview()
54 | animationDismissButton?.removeFromSuperview()
55 | transitionContext.completeTransition(true)
56 | }
57 | }
58 |
59 | func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
60 | if let completion: () -> Void = anim.value(forKey: "completion") as? (() -> Void) {
61 | completion()
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CBTabBarController
2 |
3 | [](https://raw.githubusercontent.com/Cuberto/cb-tabbar/master/LICENSE)
4 | [](http://cocoapods.org/pods/CBTabBarController)
5 | [](https://github.com/Cuberto/cb-tabbar)
6 | [](https://developer.apple.com/swift/)
7 |
8 | 
9 |
10 | ## Example
11 |
12 | To run the example project, clone the repo, and run `ExampleApp` scheme from CBTabBar.xcodeproj
13 |
14 | ## Requirements
15 |
16 | - iOS 10.0+
17 | - Xcode 9
18 |
19 | ## Installation
20 |
21 | ### CocoaPods
22 | To install CBTabBarController add the following line to your Podfile:
23 | ```ruby
24 | pod 'CBTabBarController'
25 | ```
26 | Then run `pod install`.
27 |
28 | ### Carthage
29 |
30 | Make the following entry in your Cartfile:
31 |
32 | ```
33 | github "Cuberto/cb-tabbar"
34 | ```
35 |
36 | Then run `carthage update`.
37 |
38 | If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
39 |
40 | ### Manual
41 |
42 | Add CBTabBarController folder to your project
43 |
44 | ## Usage
45 |
46 | ### With Storyboard
47 |
48 | 1. Create a new UITabBarController in your storyboard or nib.
49 |
50 | 2. Set the class of the UITabBarController to `CBTabBarController` in your Storyboard or nib.
51 |
52 | 3. Add a custom image icon and title for UITabBarItem of each child ViewContrroller
53 |
54 |
55 | ### Without Storyboard
56 |
57 | 1. Import CBTabBarController
58 | 2. Instantiate `CBTabBarController`
59 | 3. Add some child controllers and don't forget to set them tabBar items with title and image
60 |
61 | ## Styling
62 |
63 | Set appropriate style for tab bar using `style` property of `CBTabbarController` instance.
64 | You can subclass `UITabBarItem` and conform to `CBExtendedTabItem` protocol to define custom title style and tint for tab buttons.
65 |
66 | Styles supported at the moment:
67 |
68 | #### Gooey with menu:
69 |
70 | 
71 |
72 | Requies configured `CBTabMenu` instance as paramenter (see example). Any object conforming to `CBTabMenuItem` can be used as menu item
73 |
74 | #### Flashy:
75 |
76 | 
77 |
78 | #### Fade (default):
79 |
80 | Same as gooey but without menu button
81 |
82 | ## Author
83 |
84 | Cuberto Design, info@cuberto.com
85 |
86 | ## License
87 |
88 | CB
89 | TabBarController is available under the MIT license. See the LICENSE file for more info.
90 |
--------------------------------------------------------------------------------
/CBTabBar.xcodeproj/xcshareddata/xcschemes/CBFlashyTabBarController.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/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 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/CBTabBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBFlashyTabBarController.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | open class CBTabBarController: UITabBarController {
12 |
13 | fileprivate var shouldSelectOnTabBar = true
14 |
15 | public var style: CBTabBarStyle = .fade {
16 | didSet {
17 | updateTabBarStyle()
18 | }
19 | }
20 |
21 | open override var selectedViewController: UIViewController? {
22 | willSet {
23 | guard shouldSelectOnTabBar,
24 | let newValue = newValue else {
25 | shouldSelectOnTabBar = true
26 | return
27 | }
28 | guard let tabBar = tabBar as? CBTabBar, let index = viewControllers?.index(of: newValue) else {
29 | return
30 | }
31 | tabBar.select(itemAt: index, animated: false)
32 | }
33 | }
34 |
35 | open override var selectedIndex: Int {
36 | willSet {
37 | guard shouldSelectOnTabBar else {
38 | shouldSelectOnTabBar = true
39 | return
40 | }
41 | guard let tabBar = tabBar as? CBTabBar else {
42 | return
43 | }
44 | tabBar.select(itemAt: selectedIndex, animated: false)
45 | }
46 | }
47 |
48 | open override func viewDidLoad() {
49 | super.viewDidLoad()
50 | defer {
51 | updateTabBarStyle()
52 | }
53 | guard self.tabBar as? CBTabBar == nil else {
54 | return
55 | }
56 | let tabBar = CBTabBar()
57 | if let barTint = self.tabBar.barTintColor {
58 | tabBar.barTintColor = barTint
59 | }
60 | self.setValue(tabBar, forKey: "tabBar")
61 | }
62 |
63 | private func updateTabBarStyle() {
64 | guard let tabBar = tabBar as? CBTabBar else {
65 | return
66 | }
67 | style.configure(tabBar: tabBar, with: self)
68 | }
69 |
70 | open override func viewDidAppear(_ animated: Bool) {
71 | super.viewDidAppear(animated)
72 | }
73 |
74 | open var barHeight: CGFloat {
75 | get {
76 | return (tabBar as? CBTabBar)?.barHeight ?? tabBar.frame.height
77 | }
78 | set {
79 | (tabBar as? CBTabBar)?.barHeight = newValue
80 | self.setValue(tabBar, forKey: "tabBar")
81 | }
82 | }
83 |
84 | open override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
85 | guard let idx = tabBar.items?.index(of: item) else {
86 | return
87 | }
88 | if let controller = viewControllers?[idx] {
89 | shouldSelectOnTabBar = false
90 | selectedIndex = idx
91 | delegate?.tabBarController?(self, didSelect: controller)
92 | }
93 | }
94 |
95 | private func updateTabBarFrame() {
96 | var tabFrame = tabBar.frame
97 | if #available(iOS 11.0, *) {
98 | tabFrame.size.height = barHeight + view.safeAreaInsets.bottom
99 | } else {
100 | tabFrame.size.height = barHeight
101 | }
102 | tabFrame.origin.y = self.view.frame.size.height - tabFrame.size.height
103 | tabBar.frame = tabFrame
104 | tabBar.setNeedsLayout()
105 | }
106 |
107 | @available(iOS 11.0, *)
108 | open override func viewSafeAreaInsetsDidChange() {
109 | super.viewSafeAreaInsetsDidChange()
110 | updateTabBarFrame()
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Flashy/Animations/CBFlashyTabItemBasicAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabItemBasicAnimation.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol CBFlashyTabItemBasicAnimation: CBTabItemAnimation {
12 |
13 | func imageOffsetAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation
14 | func imageMaskAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation
15 | func labelOffsetAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation
16 | func labelMaskAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation
17 | func dotScaleAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation
18 |
19 | var duration: Double { get }
20 |
21 | }
22 |
23 | extension CBFlashyTabItemBasicAnimation {
24 |
25 | func playAnimation(forTabBarItem item: CBTabBarButton, completion: (() -> Void)? = nil) {
26 | guard let item = item as? CBFlashyTabBarButton else {
27 | fatalError("This animation can be used only with CBFlashyTabBarButton")
28 | }
29 | let animateImageOffset = imageOffsetAnimation(forTabBarItem: item)
30 | let animateImageMask = imageMaskAnimation(forTabBarItem: item)
31 | animateImageMask.isRemovedOnCompletion = false
32 |
33 | let animateLabelOffset = labelOffsetAnimation(forTabBarItem: item)
34 | let animateLabelMask = labelMaskAnimation(forTabBarItem: item)
35 | animateLabelMask.isRemovedOnCompletion = false
36 |
37 | let dotAnimation = dotScaleAnimation(forTabBarItem: item)
38 |
39 | let mainAnimations: [CAAnimation] = [animateImageOffset,
40 | animateImageMask,
41 | animateLabelOffset,
42 | animateLabelMask,
43 | dotAnimation]
44 | mainAnimations.forEach { (animation) in
45 | animation.duration = duration
46 | animation.isRemovedOnCompletion = false
47 | animation.fillMode = .forwards
48 | }
49 | if item.tabImage.layer.mask == nil {
50 | let imageMask = CAShapeLayer()
51 | imageMask.frame = item.tabImage.bounds
52 | item.tabImage.layer.mask = imageMask
53 | }
54 |
55 | if let animateImageMask = animateImageMask as? CAKeyframeAnimation,
56 | let imageMask = item.tabImage.layer.mask as? CAShapeLayer {
57 | imageMask.path = (animateImageMask.values as? [CGPath])?.last
58 | }
59 |
60 | if item.tabLabel.layer.mask == nil {
61 | let labelMask = CAShapeLayer()
62 | labelMask.frame = item.tabLabel.bounds
63 | item.tabLabel.layer.mask = labelMask
64 | }
65 |
66 | if let animateLabelMask = animateLabelMask as? CAKeyframeAnimation,
67 | let labelMask = item.tabLabel.layer.mask as? CAShapeLayer {
68 | labelMask.path = (animateLabelMask.values as? [CGPath])?.last
69 | }
70 |
71 | let timing = CAMediaTimingFunction(name: .easeOut)
72 | CATransaction.begin()
73 | CATransaction.setAnimationTimingFunction(timing)
74 | CATransaction.setCompletionBlock { completion?() }
75 | item.tabImage.layer.add(animateImageOffset, forKey: "offset")
76 | item.tabLabel.layer.add(animateLabelOffset, forKey: "offset")
77 | item.dotView.layer.add(dotAnimation, forKey: "scale")
78 | item.tabImage.layer.mask?.add(animateImageMask, forKey: "path")
79 | item.tabLabel.layer.mask?.add(animateLabelMask, forKey: "path")
80 | CATransaction.commit()
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by askopin@gmail.com on 11/29/2018.
6 | // Copyright (c) 2018 askopin@gmail.com. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CBTabBarController
11 |
12 | extension String: CBTabMenuItem {
13 | public var title: String? { return self }
14 | public var attributedTitle: NSAttributedString? { return nil }
15 | }
16 |
17 | class SampleTabItem: UITabBarItem, CBExtendedTabItem {
18 |
19 | public var attributedTitle: NSAttributedString? {
20 | guard let title = title else { return nil }
21 | return NSAttributedString(string: title, attributes: [NSAttributedString.Key.foregroundColor: UIColor.red,
22 | NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 15)])
23 | }
24 | }
25 |
26 | class ViewController: UIViewController {
27 |
28 | override func viewDidLoad() {
29 | super.viewDidLoad()
30 | self.navigationController?.isNavigationBarHidden = true
31 | }
32 |
33 | func createSampleTabController() -> CBTabBarController {
34 | let eventsVC = CBSampleViewController()
35 | eventsVC.tabBarItem = UITabBarItem(title: "Events", image: #imageLiteral(resourceName: "Events"), tag: 0)
36 | let searchVC = CBSampleViewController()
37 | searchVC.tabBarItem = UITabBarItem(title: "Search", image: #imageLiteral(resourceName: "Search"), tag: 0)
38 | let activityVC = CBSampleViewController()
39 | activityVC.tabBarItem = UITabBarItem(title: "Activity", image: #imageLiteral(resourceName: "Highlights"), tag: 0)
40 | let settingsVC = CBSampleViewController()
41 | settingsVC.tabBarItem = SampleTabItem(title: "Settings", image: #imageLiteral(resourceName: "Settings"), tag: 0)
42 | settingsVC.tabBarItem?.badgeColor = .red
43 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
44 | settingsVC.tabBarItem?.badgeValue = "20"
45 | }
46 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
47 | settingsVC.tabBarItem?.badgeValue = "200"
48 | }
49 | settingsVC.tabBarItem?.badgeValue = "1"
50 | settingsVC.inverseColor()
51 |
52 | let tabBarController = CBTabBarController()
53 | tabBarController.viewControllers = [eventsVC, searchVC, activityVC, settingsVC]
54 | return tabBarController
55 | }
56 |
57 | @IBAction func btnFlashyPressed(_ sender: AnyObject) {
58 | let tabBarController = createSampleTabController()
59 | tabBarController.style = .flashy(config: nil)
60 | self.navigationController?.pushViewController(tabBarController, animated: true)
61 | }
62 |
63 | @IBAction func btnGooeyPressed(_ sender: AnyObject) {
64 | let tabBarController = createSampleTabController()
65 | let menuEntries = ["Reminder", "Camera", "Attachment", "Text Note"]
66 | let menu = CBTabMenu(menuButtonIndex: 2,
67 | menuColor: #colorLiteral(red: 0.368781209, green: 0.6813176274, blue: 1, alpha: 1),
68 | items: menuEntries,
69 | icon: nil,
70 | callback: { controller, item in
71 | controller.dismiss(animated: true, completion: {
72 | print("\(item) selected")
73 | })
74 | })
75 | tabBarController.style = .gooey(menu: menu)
76 |
77 | self.navigationController?.pushViewController(tabBarController, animated: true)
78 | }
79 |
80 | override var preferredStatusBarStyle: UIStatusBarStyle {
81 | return .lightContent
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Flashy/Animations/CBTabItemSelectAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabItemSelectAnimation.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct CBFlashyTabItemSelectAnimation: CBFlashyTabItemBasicAnimation {
12 |
13 | let duration: Double
14 |
15 | func imageOffsetAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
16 | let imageOffsetAnimation = CABasicAnimation(keyPath: "transform.translation.y")
17 | imageOffsetAnimation.fromValue = 0.0
18 | imageOffsetAnimation.toValue = -((item.bounds.height - item.tabImage.frame.height) / 2.0 - 5.0)
19 | return imageOffsetAnimation
20 | }
21 |
22 | func imageMaskAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
23 | let maskBounds = item.tabImage.bounds
24 |
25 | let path1 = CGMutablePath()
26 | path1.move(to: .zero)
27 | path1.addLine(to: CGPoint(x: maskBounds.width, y: 0))
28 | path1.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height))
29 | path1.addLine(to: CGPoint(x: 0, y: maskBounds.height))
30 | path1.closeSubpath()
31 |
32 | let path2 = CGMutablePath()
33 | path2.move(to: .zero)
34 | path2.addLine(to: CGPoint(x: maskBounds.width, y: 0))
35 | path2.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.9))
36 | path2.addLine(to: CGPoint(x: 0, y: maskBounds.height * 0.6))
37 | path2.closeSubpath()
38 |
39 | let path3 = CGMutablePath()
40 | path3.move(to: .zero)
41 | path3.addLine(to: CGPoint(x: maskBounds.width, y: 0))
42 | path3.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.25))
43 | path3.closeSubpath()
44 |
45 | let path4 = CGMutablePath()
46 | path4.move(to: CGPoint(x: maskBounds.width * 0.8, y: 0))
47 | path4.addLine(to: CGPoint(x: maskBounds.width, y: 0))
48 | path4.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.15))
49 | path4.closeSubpath()
50 |
51 | let path5 = CGMutablePath()
52 |
53 | let maskAnimation = CAKeyframeAnimation(keyPath: "path")
54 | maskAnimation.values = [path1, path2, path3, path4, path5]
55 | maskAnimation.keyTimes = [0, 0.1, 0.5, 0.9, 1.0]
56 |
57 | return maskAnimation
58 | }
59 |
60 | func labelOffsetAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
61 | let labelOffsetAnimation = CABasicAnimation(keyPath: "transform.translation.y")
62 | labelOffsetAnimation.fromValue = (item.bounds.height - item.tabLabel.frame.height)/2.0 - 5.0
63 | labelOffsetAnimation.toValue = 0.0
64 | return labelOffsetAnimation
65 | }
66 |
67 | func labelMaskAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
68 | let maskBounds = item.tabLabel.bounds
69 |
70 | let path1 = CGMutablePath()
71 | path1.move(to: .zero)
72 |
73 | let path2 = CGMutablePath()
74 | path2.move(to: .zero)
75 | path2.addLine(to: CGPoint(x: maskBounds.width, y: 0))
76 | path2.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.2))
77 | path2.closeSubpath()
78 |
79 | let path3 = CGMutablePath()
80 | path3.move(to: .zero)
81 | path3.addLine(to: CGPoint(x: maskBounds.width, y: 0))
82 | path3.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.5))
83 | path3.addLine(to: CGPoint(x: 0, y: maskBounds.height * 0.1))
84 | path3.closeSubpath()
85 |
86 | let path4 = CGMutablePath()
87 | path4.move(to: .zero)
88 | path4.addLine(to: CGPoint(x: maskBounds.width, y: 0))
89 | path4.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height))
90 | path4.addLine(to: CGPoint(x: 0, y: maskBounds.height))
91 | path4.closeSubpath()
92 |
93 | let maskAnimation = CAKeyframeAnimation(keyPath: "path")
94 | maskAnimation.values = [path1, path2, path3, path4]
95 | maskAnimation.keyTimes = [0, 0.1, 0.3, 1.0]
96 |
97 | return maskAnimation
98 | }
99 |
100 | func dotScaleAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
101 | let dotAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
102 | dotAnimation.values = [0.0, 0.0, 1.0, 1.0]
103 | dotAnimation.keyTimes = [0, 0.2, 0.7, 1.0]
104 | return dotAnimation
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Flashy/Animations/CBTabItemDeselectAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabItemDeselectAnimation.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct CBFlashyTabItemDeselectAnimation: CBFlashyTabItemBasicAnimation {
12 |
13 | let duration: Double
14 |
15 | func imageOffsetAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
16 | let imageOffsetAnimation = CABasicAnimation(keyPath: "transform.translation.y")
17 | imageOffsetAnimation.fromValue = -((item.bounds.height - item.tabImage.frame.height) / 2.0 - 5.0)
18 | imageOffsetAnimation.toValue = 0.0
19 | return imageOffsetAnimation
20 | }
21 |
22 | func imageMaskAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
23 | let maskBounds = item.tabImage.bounds
24 |
25 | let path1 = CGMutablePath()
26 |
27 | let path2 = CGMutablePath()
28 | path2.move(to: CGPoint(x: maskBounds.width * 0.8, y: 0))
29 | path2.addLine(to: CGPoint(x: maskBounds.width, y: 0))
30 | path2.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.15))
31 | path2.closeSubpath()
32 |
33 | let path3 = CGMutablePath()
34 | path3.move(to: .zero)
35 | path3.addLine(to: CGPoint(x: maskBounds.width, y: 0))
36 | path3.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.25))
37 | path3.closeSubpath()
38 |
39 | let path4 = CGMutablePath()
40 | path4.move(to: .zero)
41 | path4.addLine(to: CGPoint(x: maskBounds.width, y: 0))
42 | path4.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.9))
43 | path4.addLine(to: CGPoint(x: 0, y: maskBounds.height * 0.6))
44 | path4.closeSubpath()
45 |
46 | let path5 = CGMutablePath()
47 | path5.move(to: .zero)
48 | path5.addLine(to: CGPoint(x: maskBounds.width, y: 0))
49 | path5.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height))
50 | path5.addLine(to: CGPoint(x: 0, y: maskBounds.height))
51 | path5.closeSubpath()
52 |
53 | let maskAnimation = CAKeyframeAnimation(keyPath: "path")
54 | maskAnimation.values = [path1, path2, path3, path4, path5]
55 | maskAnimation.keyTimes = [0.0, 0.1, 0.3, 0.7, 1.0]
56 |
57 | return maskAnimation
58 | }
59 |
60 | func labelOffsetAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
61 | let labelOffsetAnimation = CABasicAnimation(keyPath: "transform.translation.y")
62 | labelOffsetAnimation.fromValue = 0.0
63 | labelOffsetAnimation.toValue = (item.bounds.height - item.tabLabel.frame.height)/2.0 - 5.0
64 | return labelOffsetAnimation
65 | }
66 |
67 | func labelMaskAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
68 | let maskBounds = item.tabLabel.bounds
69 |
70 | let path1 = CGMutablePath()
71 | path1.move(to: .zero)
72 |
73 | let path2 = CGMutablePath()
74 | path2.move(to: .zero)
75 | path2.addLine(to: CGPoint(x: maskBounds.width, y: 0))
76 | path2.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.2))
77 | path2.closeSubpath()
78 |
79 | let path3 = CGMutablePath()
80 | path3.move(to: .zero)
81 | path3.addLine(to: CGPoint(x: maskBounds.width, y: 0))
82 | path3.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height * 0.5))
83 | path3.addLine(to: CGPoint(x: 0, y: maskBounds.height * 0.1))
84 | path3.closeSubpath()
85 |
86 | let path4 = CGMutablePath()
87 | path4.move(to: .zero)
88 | path4.addLine(to: CGPoint(x: maskBounds.width, y: 0))
89 | path4.addLine(to: CGPoint(x: maskBounds.width, y: maskBounds.height))
90 | path4.addLine(to: CGPoint(x: 0, y: maskBounds.height))
91 | path4.closeSubpath()
92 |
93 | let maskAnimation = CAKeyframeAnimation(keyPath: "path")
94 | maskAnimation.values = [path1, path2, path3, path4].reversed()
95 | maskAnimation.keyTimes = [0.0, 0.5, 0.7, 1.0]
96 |
97 | return maskAnimation
98 | }
99 |
100 | func dotScaleAnimation(forTabBarItem item: CBFlashyTabBarButton) -> CAAnimation {
101 | let dotAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
102 | dotAnimation.values = [1.0, 1.0, 0.0, 0.0]
103 | dotAnimation.keyTimes = [0, 0.2, 0.7, 1.0]
104 | return dotAnimation
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/CBBaseTabButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBBaseTabButton.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 20/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBBaseTabButton: UIButton, CBTabBarButtonProtocol {
12 |
13 | var badgeContainer = UIView()
14 | var badgeLabel = UILabel()
15 | var item: UITabBarItem? {
16 | set {
17 | guard let item = newValue else { return }
18 | _item = item
19 | }
20 | get {
21 | return _item
22 | }
23 | }
24 |
25 | @objc dynamic var _item: UITabBarItem = UITabBarItem() {
26 | didSet {
27 | didUpdateItem()
28 | }
29 | }
30 |
31 | var requiredSize: CGSize? {
32 | return nil
33 | }
34 |
35 | override init(frame: CGRect) {
36 | super.init(frame: frame)
37 | configure()
38 | addObservers()
39 | }
40 |
41 | required init?(coder aDecoder: NSCoder) {
42 | super.init(coder: aDecoder)
43 | configure()
44 | addObservers()
45 | }
46 |
47 | required init(item: UITabBarItem) {
48 | super.init(frame: .zero)
49 | configure()
50 | addObservers()
51 | self.item = item
52 | }
53 |
54 | deinit {
55 | removeObserver(self, forKeyPath: #keyPath(_item.badgeValue))
56 | removeObserver(self, forKeyPath: #keyPath(_item.badgeColor))
57 | }
58 |
59 | func addObservers() {
60 | addObserver(self, forKeyPath: #keyPath(_item.badgeValue), options: [.initial, .new], context: nil)
61 | addObserver(self, forKeyPath: #keyPath(_item.badgeColor), options: [.initial, .new], context: nil)
62 | }
63 |
64 | func configure() {
65 | badgeContainer.isHidden = true
66 | badgeContainer.addSubview(badgeLabel)
67 | addSubview(badgeContainer)
68 | badgeLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
69 | badgeLabel.textColor = .white
70 | }
71 |
72 | override var tintColor: UIColor! {
73 | didSet {
74 | badgeContainer.backgroundColor = item?.badgeColor ?? tintColor
75 | }
76 | }
77 |
78 | override func layoutSubviews() {
79 | super.layoutSubviews()
80 | let badgeMargin: CGFloat = 3
81 | badgeLabel.sizeToFit()
82 | let badgeWidth = max(20, min(badgeLabel.frame.width + 2 * badgeMargin, bounds.width - 2 * badgeMargin))
83 | badgeContainer.frame = CGRect(x: min(bounds.width - badgeWidth - badgeMargin,
84 | max(titleLabel?.frame.maxX ?? 0, imageView?.frame.maxX ?? 0)),
85 | y: badgeMargin,
86 | width: badgeWidth,
87 | height: 20)
88 | let lblWidth = min(badgeLabel.frame.width, badgeWidth - 2 * badgeMargin)
89 | badgeLabel.frame = CGRect(x: (badgeContainer.frame.width - lblWidth)/2.0,
90 | y: badgeMargin,
91 | width: lblWidth,
92 | height: badgeContainer.frame.height - 2 * badgeMargin)
93 | badgeContainer.layer.cornerRadius = badgeContainer.frame.height/2.0
94 | }
95 |
96 | private var _isSelected: Bool = false
97 | override var isSelected: Bool {
98 | get {
99 | return _isSelected
100 | }
101 | set {
102 | guard newValue != _isSelected else {
103 | return
104 | }
105 | if newValue {
106 | select(animated: false)
107 | } else {
108 | deselect(animated: false)
109 | }
110 | }
111 | }
112 |
113 | func setSelected(_ selected: Bool, animated: Bool) {
114 | if selected {
115 | select(animated: animated)
116 | } else {
117 | deselect(animated: animated)
118 | }
119 | }
120 |
121 | func select(animated: Bool = true) {
122 | }
123 |
124 | func deselect(animated: Bool = true) {
125 | }
126 |
127 | func didUpdateItem() {
128 | badgeContainer.backgroundColor = item?.badgeColor ?? tintColor
129 | badgeLabel.text = item?.badgeValue
130 | badgeContainer.isHidden = item?.badgeValue == nil
131 | setNeedsLayout()
132 | }
133 |
134 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
135 | switch keyPath {
136 | case #keyPath(_item.badgeValue), #keyPath(_item.badgeColor):
137 | didUpdateItem()
138 | default:
139 | break
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBMenuPresenter.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 21/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBMenuPresenter: NSObject {
12 |
13 | private let menu: CBTabMenu
14 |
15 | init(menu: CBTabMenu) {
16 | self.menu = menu
17 | }
18 |
19 | weak var presentationController: UIViewController?
20 | weak var menuButton: CBMenuButton? {
21 | didSet {
22 | guard let button = menuButton else { return }
23 | let panGesture = UIPanGestureRecognizer(target: self, action: #selector(menuBtnPan))
24 | button.addGestureRecognizer(panGesture)
25 | button.addTarget(self, action: #selector(menuBtnPressed), for: .touchUpInside)
26 | }
27 | }
28 |
29 | var presentAnimationController: CBMenuPresentAnimationController?
30 | var presentInteractionController: CBMenuPresentAnimationController?
31 | var dismissAnimationController: CBMenuDismissAnimationController?
32 |
33 | @objc func menuBtnPressed() {
34 | guard let menuController = createMenuController() else {
35 | return
36 | }
37 | configureAnimationControllers()
38 | presentInteractionController = nil
39 | menuController.loadViewIfNeeded()
40 | menuController.transitioningDelegate = self
41 | presentationController?.present(menuController, animated: true, completion: nil)
42 | }
43 |
44 | private func configureAnimationControllers() {
45 | presentAnimationController = CBMenuPresentAnimationController()
46 | presentAnimationController?.menuButton = menuButton
47 | presentAnimationController?.maxOffset = maxOffset
48 | dismissAnimationController = CBMenuDismissAnimationController()
49 | dismissAnimationController?.menuButton = menuButton
50 | }
51 |
52 | @objc func menuBtnPan(sender: UIPanGestureRecognizer) {
53 | switch sender.state {
54 | case .began:
55 | didBeganPan(withGesture: sender)
56 | case .changed:
57 | didChangePan(withGesture: sender)
58 | default:
59 | didEndPan(withGesture: sender)
60 | }
61 | }
62 |
63 | private func createMenuController() -> UIViewController? {
64 | let storyboard = UIStoryboard(name: "CBMenuController", bundle: Bundle.resourseBundle)
65 | let controller = storyboard.instantiateInitialViewController() as? CBMenuController
66 | controller?.menu = menu
67 | return controller
68 | }
69 |
70 | private var maxOffset: CGFloat = 180
71 | private var startY: CGFloat?
72 | private var lastProgress: CGFloat?
73 | private func didBeganPan(withGesture gesture: UIPanGestureRecognizer) {
74 | guard let container = gesture.view?.superview else {
75 | return
76 | }
77 | startY = gesture.location(in: container).y
78 |
79 | guard let menuController = createMenuController() else {
80 | return
81 | }
82 | configureAnimationControllers()
83 | presentInteractionController = presentAnimationController
84 | menuController.loadViewIfNeeded()
85 | menuController.transitioningDelegate = self
86 | presentationController?.present(menuController, animated: true, completion: nil)
87 | }
88 |
89 | private func didChangePan(withGesture gesture: UIPanGestureRecognizer) {
90 | guard let container = gesture.view?.superview else {
91 | return
92 | }
93 | let currentY = gesture.location(in: container).y
94 | let diff = max(0, (startY ?? 0) - currentY)
95 | let progress = min(1.0, diff / (maxOffset * 2))
96 | lastProgress = progress
97 | if diff >= maxOffset {
98 | gesture.isEnabled = false
99 | presentAnimationController?.finishTransition()
100 | gesture.isEnabled = true
101 | return
102 | }
103 | presentAnimationController?.update(to: progress, offset: diff)
104 | }
105 |
106 | private func didEndPan(withGesture gesture: UIPanGestureRecognizer) {
107 | defer {
108 | startY = nil
109 | lastProgress = nil
110 | }
111 | guard let lastProgress = lastProgress else {
112 | return
113 | }
114 | if lastProgress > 0.4 {
115 | presentAnimationController?.finishTransition()
116 | } else {
117 | presentAnimationController?.cancelTransition()
118 | }
119 | }
120 | }
121 |
122 | extension CBMenuPresenter: UIViewControllerTransitioningDelegate {
123 |
124 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
125 | return presentAnimationController
126 | }
127 |
128 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
129 | return dismissAnimationController
130 | }
131 |
132 |
133 | func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
134 | return presentInteractionController
135 | }
136 |
137 | func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
138 | return nil
139 | }
140 | }
141 |
142 |
143 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/CBTabBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBFlashyTabBar.swift
3 | // CBFlashyTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | open class CBTabBar: UITabBar {
12 |
13 | private var buttons: [CBTabBarButton] = []
14 |
15 | fileprivate var shouldSelectOnTabBar = true
16 | var buttonFactory: CBTabButtonFactory? {
17 | didSet {
18 | reloadViews()
19 | }
20 | }
21 |
22 | open override var selectedItem: UITabBarItem? {
23 | willSet {
24 | guard let newValue = newValue else {
25 | buttons.forEach { $0.setSelected(false, animated: false) }
26 | return
27 | }
28 |
29 | let btnItems: [UITabBarItem?] = buttons.map { $0.item }
30 | for (index, value) in btnItems.enumerated() {
31 | if value === newValue {
32 | select(itemAt: index, animated: false)
33 | }
34 | }
35 | }
36 | }
37 |
38 | open override var tintColor: UIColor! {
39 | didSet {
40 | buttons.forEach { button in
41 | if let item = button.item as? CBExtendedTabItem {
42 | button.tintColor = item.tintColor ?? tintColor
43 | } else {
44 | button.tintColor = tintColor
45 | }
46 | }
47 | }
48 | }
49 |
50 | var barHeight: CGFloat = 60
51 |
52 | override open func sizeThatFits(_ size: CGSize) -> CGSize {
53 | var sizeThatFits = super.sizeThatFits(size)
54 | sizeThatFits.height = barHeight
55 | if #available(iOS 11.0, *) {
56 | sizeThatFits.height = sizeThatFits.height + safeAreaInsets.bottom
57 | }
58 | return sizeThatFits
59 | }
60 |
61 | open override var items: [UITabBarItem]? {
62 | didSet {
63 | reloadViews()
64 | }
65 | }
66 |
67 | open override func setItems(_ items: [UITabBarItem]?, animated: Bool) {
68 | super.setItems(items, animated: animated)
69 | reloadViews()
70 | }
71 |
72 | open override func layoutSubviews() {
73 | super.layoutSubviews()
74 |
75 | let sizedButtons = buttons.filter { $0.requiredSize != nil }
76 | let minWidth = bounds.width / CGFloat(buttons.count)
77 | let predefinedWidth: CGFloat = sizedButtons.compactMap { $0.requiredSize?.width }
78 | .map { max(minWidth, $0) }
79 | .reduce(0, +)
80 |
81 | let btnWidth = max(0, (bounds.width - predefinedWidth) / CGFloat(buttons.count - sizedButtons.count))
82 | let bottomOffset: CGFloat
83 | if #available(iOS 11.0, *) {
84 | bottomOffset = safeAreaInsets.bottom
85 | } else {
86 | bottomOffset = 0
87 | }
88 | let btnHeight = bounds.height - bottomOffset
89 |
90 | var lastX: CGFloat = 0
91 | for button in buttons {
92 | var padding: CGFloat = 0
93 | if let btnSize = button.requiredSize {
94 | let btnY = (btnHeight - btnSize.height)/2.0
95 | padding = max(0, (minWidth - btnSize.width)/2.0)
96 | button.frame = CGRect(origin: CGPoint(x: lastX + padding, y: btnY),
97 | size: btnSize)
98 | } else {
99 | button.frame = CGRect(x: lastX, y: 0, width: btnWidth, height: btnHeight)
100 | }
101 | lastX = button.frame.maxX + padding
102 | button.setNeedsLayout()
103 | }
104 | }
105 |
106 | private func reloadViews() {
107 | subviews.filter { String(describing: type(of: $0)) == "UITabBarButton" }.forEach { $0.removeFromSuperview() }
108 | buttons.forEach { $0.removeFromSuperview()}
109 | buttons = buttonFactory?.buttons(forItems: items ?? []) ?? []
110 | buttons.forEach { button in
111 | if let item = button.item as? CBExtendedTabItem {
112 | button.tintColor = item.tintColor ?? tintColor
113 | } else {
114 | button.tintColor = tintColor
115 | }
116 | if selectedItem != nil && button.item === selectedItem {
117 | button.setSelected(true, animated: false)
118 | }
119 | button.addTarget(self, action: #selector(btnPressed), for: .touchUpInside)
120 | addSubview(button)
121 | }
122 | setNeedsLayout()
123 | }
124 |
125 | @objc private func btnPressed(sender: UIControl) {
126 | guard let sender = sender as? CBTabBarButton else {
127 | return
128 | }
129 |
130 | buttons.forEach { (button) in
131 | guard button !== sender else {
132 | return
133 | }
134 | button.setSelected(false, animated: true)
135 | }
136 | sender.setSelected(true, animated: true)
137 | if let item = sender.item,
138 | let items = items,
139 | items.contains(item) {
140 | delegate?.tabBar?(self, didSelect: item)
141 | }
142 | }
143 |
144 | func select(itemAt index: Int, animated: Bool = false) {
145 | guard index < buttons.count else {
146 | return
147 | }
148 | let selectedbutton = buttons[index]
149 | buttons.forEach { (button) in
150 | guard button !== selectedbutton else {
151 | return
152 | }
153 | button.setSelected(false, animated: false)
154 | }
155 | selectedbutton.setSelected(true, animated: false)
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/CBTabBarController/Assets/Storyboard/CBMenuController.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 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/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 |
41 |
42 |
54 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Flashy/CBFlashyTabBarButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBTabBarButton.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 28/11/2018.
6 | // Copyright © 2018 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBFlashyTabBarButton: CBTabBarButton {
12 |
13 | var tabImage = UIImageView()
14 | var tabLabel = UILabel()
15 | var badgeContainer = UIView()
16 | var badgeLabel = UILabel()
17 | var dotView = UIView()
18 |
19 | var selectAnimation: CBTabItemAnimation?
20 | var deselectAnimation: CBTabItemAnimation?
21 | var config: CBFlashyTabBarConfig?
22 |
23 | private var _isSelected: Bool = false
24 | override var isSelected: Bool {
25 | get {
26 | return _isSelected
27 | }
28 | set {
29 | guard newValue != _isSelected else {
30 | return
31 | }
32 | if newValue {
33 | select(animated: false)
34 | } else {
35 | deselect(animated: false)
36 | }
37 | }
38 | }
39 |
40 | override init(frame: CGRect) {
41 | super.init(frame: frame)
42 | configureSubviews()
43 | addObservers()
44 | }
45 |
46 | required init?(coder aDecoder: NSCoder) {
47 | super.init(coder: aDecoder)
48 | configureSubviews()
49 | addObservers()
50 | }
51 |
52 | required init(item: UITabBarItem) {
53 | super.init(frame: .zero)
54 | addObservers()
55 | configureSubviews()
56 | self.item = item
57 | }
58 |
59 | deinit {
60 | removeObserver(self, forKeyPath: #keyPath(_item.badgeValue))
61 | removeObserver(self, forKeyPath: #keyPath(_item.badgeColor))
62 | }
63 |
64 | private func addObservers() {
65 | addObserver(self, forKeyPath: #keyPath(_item.badgeValue), options: [.initial, .new], context: nil)
66 | addObserver(self, forKeyPath: #keyPath(_item.badgeColor), options: [.initial, .new], context: nil)
67 | }
68 |
69 | var item: UITabBarItem? {
70 | set {
71 | guard let item = newValue else { return }
72 | _item = item
73 | }
74 | get {
75 | return _item
76 | }
77 | }
78 |
79 | @objc dynamic var _item: UITabBarItem = UITabBarItem(){
80 | didSet {
81 | didUpdateItem()
82 | }
83 | }
84 |
85 | override var tintColor: UIColor! {
86 | didSet {
87 | tabImage.tintColor = tintColor.withAlphaComponent(config?.deselectedOpacity ?? 0.4)
88 | tabLabel.attributedText = (item as? CBExtendedTabItem)?.attributedTitle ?? attributedText(fortitle: item?.title)
89 | dotView.backgroundColor = tintColor
90 | badgeContainer.backgroundColor = item?.badgeColor ?? tintColor
91 | }
92 | }
93 |
94 | private func attributedText(fortitle title: String?) -> NSAttributedString {
95 | var attrs: [NSAttributedString.Key: Any] = [:]
96 | attrs[.kern] = -0.2
97 | attrs[.foregroundColor] = tintColor
98 | attrs[.font] = UIFont.systemFont(ofSize: 12, weight: .semibold)
99 | return NSAttributedString(string: title ?? "", attributes: attrs)
100 | }
101 |
102 | private func configureSubviews() {
103 | addSubview(tabLabel)
104 | addSubview(tabImage)
105 | addSubview(dotView)
106 | badgeContainer.addSubview(badgeLabel)
107 | addSubview(badgeContainer)
108 | tabLabel.numberOfLines = 2
109 | tabLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
110 | tabLabel.isHidden = true
111 | tabImage.contentMode = .center
112 | let dotSize: CGFloat = 5.0
113 | dotView.frame = CGRect(origin: .zero, size: CGSize(width: dotSize, height: dotSize))
114 | dotView.layer.cornerRadius = dotSize / 2.0
115 | dotView.layer.shouldRasterize = true
116 | dotView.layer.rasterizationScale = UIScreen.main.scale
117 | dotView.isHidden = true
118 | badgeContainer.isHidden = true
119 | badgeLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
120 | badgeLabel.textColor = .white
121 | }
122 |
123 | override func layoutSubviews() {
124 | super.layoutSubviews()
125 | tabImage.sizeToFit()
126 | tabImage.center = CGPoint(x: bounds.width/2.0, y: bounds.height/2.0)
127 | tabLabel.frame = bounds
128 | tabLabel.sizeToFit()
129 | tabLabel.center = tabImage.center
130 | let dotX: CGFloat = tabImage.center.x - dotView.frame.width/2.0
131 | let dotY: CGFloat = tabLabel.frame.maxY + 13.0
132 | dotView.frame = CGRect(origin: CGPoint(x: dotX, y: dotY), size: dotView.frame.size)
133 |
134 | let badgeMargin: CGFloat = 3
135 | badgeLabel.sizeToFit()
136 | let badgeWidth = max(20, min(badgeLabel.frame.width + 2 * badgeMargin, bounds.width - 2 * badgeMargin))
137 | badgeContainer.frame = CGRect(x: min(bounds.width - badgeWidth - badgeMargin, tabLabel.frame.maxX),
138 | y: badgeMargin,
139 | width: badgeWidth,
140 | height: 20)
141 | let lblWidth = min(badgeLabel.frame.width, badgeWidth - 2 * badgeMargin)
142 | badgeLabel.frame = CGRect(x: (badgeContainer.frame.width - lblWidth)/2.0,
143 | y: badgeMargin,
144 | width: lblWidth,
145 | height: badgeContainer.frame.height - 2 * badgeMargin)
146 | badgeContainer.layer.cornerRadius = badgeContainer.frame.height/2.0
147 | }
148 |
149 | func setSelected(_ selected: Bool, animated: Bool) {
150 | if selected {
151 | select(animated: animated)
152 | } else {
153 | deselect(animated: animated)
154 | }
155 | }
156 |
157 | func select(animated: Bool = true) {
158 | guard !_isSelected else {
159 | return
160 | }
161 | _isSelected = true
162 | guard animated, let selectAnimation = selectAnimation else {
163 | tabLabel.isHidden = false
164 | tabImage.isHidden = true
165 | dotView.isHidden = false
166 | return
167 | }
168 | tabLabel.isHidden = false
169 | dotView.isHidden = false
170 | selectAnimation.playAnimation(forTabBarItem: self) {[weak self] in
171 | if self?._isSelected ?? false {
172 | self?.tabImage.isHidden = true
173 | }
174 | }
175 | }
176 |
177 | func deselect(animated: Bool = true) {
178 | guard _isSelected else {
179 | return
180 | }
181 | _isSelected = false
182 | guard animated, let deselectAnimation = deselectAnimation else {
183 | tabLabel.isHidden = true
184 | tabImage.isHidden = false
185 | dotView.isHidden = true
186 | return
187 | }
188 | tabImage.isHidden = false
189 | deselectAnimation.playAnimation(forTabBarItem: self) {[weak self] in
190 | if !(self?._isSelected ?? true) {
191 | self?.tabLabel.isHidden = true
192 | self?.dotView.isHidden = true
193 | }
194 | }
195 | }
196 |
197 | private func didUpdateItem() {
198 | tabImage.image = item?.image?.withRenderingMode(.alwaysTemplate)
199 | tabLabel.attributedText = (item as? CBExtendedTabItem)?.attributedTitle ?? attributedText(fortitle: item?.title)
200 | badgeContainer.backgroundColor = item?.badgeColor ?? tintColor
201 | badgeLabel.text = item?.badgeValue
202 | badgeContainer.isHidden = item?.badgeValue == nil
203 | setNeedsLayout()
204 | }
205 |
206 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
207 | switch keyPath {
208 | case #keyPath(_item.badgeValue), #keyPath(_item.badgeColor):
209 | didUpdateItem()
210 | default:
211 | break
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/CBTabBarController/Classes/Menu/CBMenuPresentAnimationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CBMenuPresentAnimationController.swift
3 | // CBTabBarController
4 | //
5 | // Created by Anton Skopin on 21/03/2019.
6 | // Copyright © 2019 cuberto. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CBMenuPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerInteractiveTransitioning {
12 |
13 | var maxOffset: CGFloat = 0
14 | let offsetSpeed: CGFloat = 650
15 | private let duration: Double = 0.5
16 | private var interactiveContext: UIViewControllerContextTransitioning?
17 | private var transitionContext: UIViewControllerContextTransitioning?
18 | private var fromView: UIView?
19 | private var toView: UIView?
20 | private var toViewController: CBMenuController?
21 | private var displayLink: CADisplayLink?
22 | private var lastProgress: CGFloat?
23 | private var lastOffset: CGFloat = 0
24 | private var startTime: TimeInterval = CACurrentMediaTime()
25 | weak var menuButton: UIView?
26 | private var animationButton: UIView?
27 | private var animationButtonStart: CGRect = .zero
28 | deinit {
29 | clear()
30 | }
31 |
32 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
33 | return TimeInterval(maxOffset/offsetSpeed)
34 | }
35 |
36 | private func startTransition(using transitionContext: UIViewControllerContextTransitioning) {
37 | guard let fromView = transitionContext.view(forKey: .from) else { return }
38 | guard let toView = transitionContext.view(forKey: .to) else { return }
39 | toViewController = transitionContext.viewController(forKey: .to) as? CBMenuController
40 | self.fromView = fromView
41 | self.toView = toView
42 | self.transitionContext = transitionContext
43 | let mask = CAShapeLayer()
44 |
45 | let container = transitionContext.containerView
46 | container.addSubview(toView)
47 | if let menuButton = menuButton,
48 | let animationButton = menuButton.snapshotView(afterScreenUpdates: false) {
49 | container.addSubview(animationButton)
50 | animationButton.frame = fromView.convert(menuButton.frame, from: menuButton.superview)
51 | animationButtonStart = animationButton.frame
52 | self.animationButton = animationButton
53 | }
54 | toViewController?.menuContainer.alpha = 0
55 | toViewController?.dismissButton.alpha = 0
56 | menuButton?.isHidden = true
57 | toView.layer.mask = mask
58 |
59 | startTime = CACurrentMediaTime()
60 | lastOffset = 0
61 | let displayLink = CADisplayLink(target: self, selector: #selector(screenUpdate))
62 | displayLink.add(to: .current, forMode: .common)
63 | self.displayLink = displayLink
64 |
65 | }
66 |
67 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
68 | startTransition(using: transitionContext)
69 | }
70 |
71 | func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
72 | interactiveContext = transitionContext
73 | startTransition(using: transitionContext)
74 | }
75 |
76 | func update(to progress: CGFloat, offset: CGFloat) {
77 | lastProgress = progress
78 | lastOffset = offset
79 | }
80 |
81 | var finishing: Bool = false
82 | var cancelling: Bool = false
83 | var lastScreenUpdateTime: TimeInterval = CACurrentMediaTime()
84 |
85 | @objc func screenUpdate() {
86 | defer {
87 | lastScreenUpdateTime = CACurrentMediaTime()
88 | }
89 |
90 | let progress = lastProgress ?? CGFloat((CACurrentMediaTime() - startTime)/duration)
91 | let timeDiff = CGFloat(CACurrentMediaTime() - lastScreenUpdateTime)
92 | var alphaProgress = min(1.0, progress/0.3)
93 |
94 | if cancelling || finishing {
95 | lastOffset -= offsetSpeed * timeDiff
96 | lastOffset = max(minOffset, lastOffset)
97 | } else if lastProgress == nil {
98 | lastOffset += offsetSpeed * timeDiff
99 | finishing = lastOffset > maxOffset
100 | finishOffset = -lastOffset
101 | }
102 |
103 | var btnOffset = -lastOffset
104 | var buttonOnPlace: Bool = false
105 | var buttonAnimationFinished: Bool = false
106 | let maskOnPlace: Bool = (lastOffset <= minOffset)
107 | let menuTargetOffsetY = (toViewController?.dismissButton.frame.minY ?? 0) - animationButtonStart.minY
108 |
109 | if finishing {
110 | let btnOffsetSpeed = offsetSpeed * (abs(menuTargetOffsetY)/(maxOffset + 100))
111 | finishOffset -= btnOffsetSpeed * timeDiff
112 | buttonOnPlace = finishOffset <= menuTargetOffsetY
113 | btnOffset = max(menuTargetOffsetY, finishOffset)
114 | let finishPercent = (maxOffset + finishOffset)/(menuTargetOffsetY + maxOffset)
115 | toViewController?.menuContainer.alpha = min(1.0, finishPercent)
116 | if buttonOnPlace && !buttonAnimationFinished {
117 | let btnAlphaSpeed: CGFloat = 4
118 | if let btnDismiss = toViewController?.dismissButton {
119 | btnDismiss.alpha = min(1.0, btnDismiss.alpha + btnAlphaSpeed * timeDiff)
120 | }
121 | if let btnPresent = animationButton {
122 | btnPresent.alpha = max(0.0, btnPresent.alpha - btnAlphaSpeed * timeDiff)
123 | buttonAnimationFinished = btnPresent.alpha <= 0
124 | } else {
125 | buttonAnimationFinished = true
126 | }
127 | }
128 | }
129 |
130 | if cancelling {
131 | alphaProgress = min(alphaProgress, (lastOffset - minOffset)/(cancelOffset - minOffset))
132 | }
133 |
134 | if let toView = toView {
135 | toView.alpha = alphaProgress
136 | if finishing {
137 | (toView.layer.mask as? CAShapeLayer)?.path = finishPath(forView: toView,
138 | offset: lastOffset)
139 | } else {
140 | (toView.layer.mask as? CAShapeLayer)?.path = path(forView: toView,
141 | offset: lastOffset)
142 | }
143 |
144 | }
145 | if let animationButton = animationButton {
146 | animationButton.transform = CGAffineTransform(translationX: 0, y: min(0, btnOffset))
147 | .rotated(by: btnOffset/menuTargetOffsetY * CGFloat.pi/4.0)
148 | }
149 |
150 | if finishing && buttonOnPlace && maskOnPlace && buttonAnimationFinished {
151 | transitionContext?.completeTransition(true)
152 | menuButton?.isHidden = false
153 | toViewController?.menuContainer.alpha = 1.0
154 | toViewController?.dismissButton.alpha = 1.0
155 | clear()
156 | return
157 | }
158 | if cancelling && maskOnPlace {
159 | toView?.removeFromSuperview()
160 | menuButton?.isHidden = false
161 | transitionContext?.completeTransition(false)
162 | clear()
163 | return
164 | }
165 | }
166 |
167 | var minOffset: CGFloat {
168 | return 0
169 | }
170 |
171 | func lineCoeff(pt1: CGPoint, pt2: CGPoint) -> (k: CGFloat, b: CGFloat) {
172 | if pt1.x == pt2.y {
173 | return (k: 0, b: 0)
174 | }
175 | let k = (pt1.y - pt2.y)/(pt1.x - pt2.x)
176 | let b = pt1.y - k * pt1.x
177 | return (k: k, b: b)
178 | }
179 |
180 | private let defaultCentralOffsetY: CGFloat = 60
181 | func path(forView view: UIView, offset: CGFloat) -> CGPath {
182 | let centerX = menuButton?.frame.midX ?? view.bounds.width/2
183 | let path = UIBezierPath()
184 | path.move(to: .zero)
185 | path.addLine(to: CGPoint(x: view.bounds.width, y: 0))
186 | path.addLine(to: CGPoint(x: view.bounds.width, y: view.bounds.height))
187 | let bottomOffset: CGFloat = 0
188 | let pt1 = CGPoint(x: centerX + view.bounds.width/2, y: view.bounds.height + bottomOffset)
189 | path.addLine(to: pt1)
190 | let centralOffsetX = max(28.0, 50 - abs(offset * 0.2))
191 | let centralOffsetY = 60 / 375 * view.bounds.width + offset * (0.83 / 320 * view.bounds.width)
192 | let pt2 = CGPoint(x: centerX + centralOffsetX, y: view.bounds.height - centralOffsetY)
193 | let pt3 = CGPoint(x: centerX - centralOffsetX, y: view.bounds.height - centralOffsetY)
194 | let pt4 = CGPoint(x: centerX - view.bounds.width/2, y: view.bounds.height + bottomOffset)
195 | let dcp21x: CGFloat = 55 / 375 * view.bounds.width
196 | let cpt21 = pt2.offsetBy(dx: max(-(centralOffsetX + 10), dcp21x - offset * 0.6),
197 | dy: 50 + max(0, offset * 0.5))
198 |
199 | let rightLineCoeff = lineCoeff(pt1: cpt21, pt2: pt2)
200 | let cpt22y: CGFloat = pt2.y - max(10.0, dcp21x - max(0, offset * 0.1))
201 | let cpt22x: CGFloat = rightLineCoeff.k != 0 ? (cpt22y - rightLineCoeff.b)/rightLineCoeff.k : 0
202 | let cpt22 = CGPoint(x: cpt22x, y: cpt22y)
203 | let cpt31 = CGPoint(x: pt3.x + (pt2.x - cpt22.x), y: cpt22y)
204 | let cpt32 = CGPoint(x: pt3.x - (cpt21.x - pt2.x), y: cpt21.y)
205 |
206 | let dcpt12x = -44 / 375 * view.bounds.width - max(0, offset * 0.5)
207 |
208 | path.addCurve(to: pt2,
209 | controlPoint1: pt1.offsetBy(dx: dcpt12x, dy: 0),
210 | controlPoint2: cpt21)
211 | path.addCurve(to: pt3,
212 | controlPoint1: cpt22,
213 | controlPoint2: cpt31)
214 | path.addCurve(to: pt4,
215 | controlPoint1: cpt32,
216 | controlPoint2: pt4.offsetBy(dx: -dcpt12x, dy: 0))
217 | path.addLine(to: CGPoint(x: 0, y: view.bounds.height))
218 | path.close()
219 | return path.cgPath
220 | }
221 |
222 | func finishPath(forView view: UIView, offset: CGFloat) -> CGPath {
223 | let centerX = menuButton?.frame.midX ?? view.bounds.width/2
224 | let correctedOffset = offset - 50
225 | let path = UIBezierPath()
226 | path.move(to: .zero)
227 | path.addLine(to: CGPoint(x: view.bounds.width, y: 0))
228 | path.addLine(to: CGPoint(x: view.bounds.width, y: view.bounds.height))
229 | let bottomOffset: CGFloat = 0
230 | let pt1 = CGPoint(x: centerX + view.bounds.width/2, y: view.bounds.height + bottomOffset)
231 | path.addLine(to: pt1)
232 | let centralOffsetX: CGFloat = 10
233 | let centralOffsetY = correctedOffset * 0.9
234 | let pt2 = CGPoint(x: centerX + centralOffsetX, y: view.bounds.height - centralOffsetY)
235 | let pt3 = CGPoint(x: centerX - centralOffsetX, y: view.bounds.height - centralOffsetY)
236 | let pt4 = CGPoint(x: centerX - view.bounds.width/2, y: view.bounds.height + bottomOffset)
237 | let dcp21x: CGFloat = 10 / 375 * view.bounds.width
238 | let dcp21xCorrected: CGFloat = dcp21x
239 | let cpt21 = pt2.offsetBy(dx: dcp21xCorrected,
240 | dy: min(view.bounds.height - pt2.y, 50 + max(0, correctedOffset * 0.5)))
241 |
242 | let rightLineCoeff = lineCoeff(pt1: cpt21, pt2: pt2)
243 | let cpt22y: CGFloat = pt2.y - min(cpt21.y - pt2.y,
244 | max(10.0, dcp21x - max(0, correctedOffset * 0.1)))
245 | let cpt22x: CGFloat = rightLineCoeff.k != 0 ? (cpt22y - rightLineCoeff.b)/rightLineCoeff.k : 0
246 | let cpt22 = CGPoint(x: cpt22x, y: cpt22y)
247 | let cpt31 = CGPoint(x: pt3.x + (pt2.x - cpt22.x), y: cpt22y)
248 | let cpt32 = CGPoint(x: pt3.x - (cpt21.x - pt2.x), y: cpt21.y)
249 |
250 | let dcpt12x = -44 / 375 * view.bounds.width - max(0, correctedOffset * 0.5)
251 |
252 | path.addCurve(to: pt2,
253 | controlPoint1: pt1.offsetBy(dx: dcpt12x, dy: 0),
254 | controlPoint2: cpt21)
255 | path.addCurve(to: pt3,
256 | controlPoint1: cpt22,
257 | controlPoint2: cpt31)
258 | path.addCurve(to: pt4,
259 | controlPoint1: cpt32,
260 | controlPoint2: pt4.offsetBy(dx: -dcpt12x, dy: 0))
261 | path.addLine(to: CGPoint(x: 0, y: view.bounds.height))
262 | path.close()
263 | return path.cgPath
264 | }
265 |
266 | var cancelOffset: CGFloat = 0
267 | var finishOffset: CGFloat = 0
268 |
269 | func finishTransition() {
270 | finishOffset = -lastOffset
271 | interactiveContext?.finishInteractiveTransition()
272 | finishing = true
273 | }
274 |
275 | func cancelTransition() {
276 | cancelOffset = lastOffset
277 | interactiveContext?.cancelInteractiveTransition()
278 | cancelling = true
279 | }
280 |
281 | func clear() {
282 | toView?.layer.mask = nil
283 | fromView = nil
284 | toView = nil
285 | interactiveContext = nil
286 | displayLink?.remove(from: .current, forMode: .common)
287 | displayLink = nil
288 | cancelling = false
289 | cancelling = false
290 | menuButton = nil
291 | animationButton?.removeFromSuperview()
292 | animationButton = nil
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/CBTabBar.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CE53232E2241270C00D6C307 /* CBTabButtonFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53232D2241270C00D6C307 /* CBTabButtonFactory.swift */; };
11 | CE53233122424F2E00D6C307 /* CBFlashyTabButtonFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53233022424F2E00D6C307 /* CBFlashyTabButtonFactory.swift */; };
12 | CE532335224258CB00D6C307 /* CBFadeTabBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE532334224258CB00D6C307 /* CBFadeTabBarButton.swift */; };
13 | CE53233722425A9F00D6C307 /* CBMenuTabButtonFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53233622425A9F00D6C307 /* CBMenuTabButtonFactory.swift */; };
14 | CE53233A2242B6D000D6C307 /* CBMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5323392242B6D000D6C307 /* CBMenuButton.swift */; };
15 | CE53233C2242B71D00D6C307 /* CBBaseTabButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53233B2242B71D00D6C307 /* CBBaseTabButton.swift */; };
16 | CE5323402242BD3B00D6C307 /* CBMenuTabItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53233F2242BD3B00D6C307 /* CBMenuTabItem.swift */; };
17 | CE5323432242F1FD00D6C307 /* btnMenu.png in Resources */ = {isa = PBXBuildFile; fileRef = CE5323412242F1FC00D6C307 /* btnMenu.png */; };
18 | CE5323442242F1FD00D6C307 /* btnMenu@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = CE5323422242F1FC00D6C307 /* btnMenu@2x.png */; };
19 | CE53234622431B1800D6C307 /* CBMenuPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53234522431B1800D6C307 /* CBMenuPresenter.swift */; };
20 | CE53234C2243332900D6C307 /* CBMenuController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE53234B2243332900D6C307 /* CBMenuController.storyboard */; };
21 | CE53234E2243353B00D6C307 /* CBMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53234D2243353B00D6C307 /* CBMenuController.swift */; };
22 | CE5323502243376200D6C307 /* CBMenuPresentAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53234F2243376200D6C307 /* CBMenuPresentAnimationController.swift */; };
23 | CE5323532243BB8500D6C307 /* btnClose@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = CE5323512243BB8500D6C307 /* btnClose@2x.png */; };
24 | CE5323542243BB8500D6C307 /* btnClose.png in Resources */ = {isa = PBXBuildFile; fileRef = CE5323522243BB8500D6C307 /* btnClose.png */; };
25 | CE5323572244AB5900D6C307 /* CGPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5323562244AB5900D6C307 /* CGPoint.swift */; };
26 | CE5323592244ACA700D6C307 /* CBMenuDismissAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5323582244ACA700D6C307 /* CBMenuDismissAnimationController.swift */; };
27 | CE53235B2244BF9E00D6C307 /* CBTabBarStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53235A2244BF9D00D6C307 /* CBTabBarStyle.swift */; };
28 | CE53235E2244C33A00D6C307 /* CBFadeTabButtonFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53235D2244C33A00D6C307 /* CBFadeTabButtonFactory.swift */; };
29 | CE53236022454B2400D6C307 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE53235F22454B2400D6C307 /* Bundle.swift */; };
30 | CE5323832245583300D6C307 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE5323812245583300D6C307 /* LaunchScreen.storyboard */; };
31 | CECEA1D821B6767100F15B7A /* CBTabBarController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CECEA1D121B6767100F15B7A /* CBTabBarController.framework */; };
32 | CECEA1D921B6767100F15B7A /* CBTabBarController.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CECEA1D121B6767100F15B7A /* CBTabBarController.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
33 | CECEA1E821B676D000F15B7A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1DF21B676D000F15B7A /* ViewController.swift */; };
34 | CECEA1EA21B676D000F15B7A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CECEA1E221B676D000F15B7A /* Main.storyboard */; };
35 | CECEA1EB21B676D000F15B7A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CECEA1E421B676D000F15B7A /* Images.xcassets */; };
36 | CECEA1EC21B676D000F15B7A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1E521B676D000F15B7A /* AppDelegate.swift */; };
37 | CECEA1ED21B676D000F15B7A /* CBSampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1E621B676D000F15B7A /* CBSampleViewController.swift */; };
38 | CECEA1FD21B676FF00F15B7A /* CBTabItemSelectAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1F421B676FF00F15B7A /* CBTabItemSelectAnimation.swift */; };
39 | CECEA1FE21B676FF00F15B7A /* CBFlashyTabItemBasicAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1F521B676FF00F15B7A /* CBFlashyTabItemBasicAnimation.swift */; };
40 | CECEA1FF21B676FF00F15B7A /* CBTabItemAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1F621B676FF00F15B7A /* CBTabItemAnimation.swift */; };
41 | CECEA20021B676FF00F15B7A /* CBTabItemDeselectAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1F721B676FF00F15B7A /* CBTabItemDeselectAnimation.swift */; };
42 | CECEA20121B676FF00F15B7A /* CBTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1F821B676FF00F15B7A /* CBTabBar.swift */; };
43 | CECEA20221B676FF00F15B7A /* CBFlashyTabBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1F921B676FF00F15B7A /* CBFlashyTabBarButton.swift */; };
44 | CECEA20321B676FF00F15B7A /* CBTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECEA1FA21B676FF00F15B7A /* CBTabBarController.swift */; };
45 | CECEA21A21B679EE00F15B7A /* CBFlashyTabBarController.h in Headers */ = {isa = PBXBuildFile; fileRef = CECEA21921B679EE00F15B7A /* CBFlashyTabBarController.h */; settings = {ATTRIBUTES = (Public, ); }; };
46 | /* End PBXBuildFile section */
47 |
48 | /* Begin PBXContainerItemProxy section */
49 | CECEA1D621B6767100F15B7A /* PBXContainerItemProxy */ = {
50 | isa = PBXContainerItemProxy;
51 | containerPortal = CECEA19E21B6758200F15B7A /* Project object */;
52 | proxyType = 1;
53 | remoteGlobalIDString = CECEA1D021B6767100F15B7A;
54 | remoteInfo = CBFlashyTabBarController;
55 | };
56 | /* End PBXContainerItemProxy section */
57 |
58 | /* Begin PBXCopyFilesBuildPhase section */
59 | CECEA1DD21B6767100F15B7A /* Embed Frameworks */ = {
60 | isa = PBXCopyFilesBuildPhase;
61 | buildActionMask = 2147483647;
62 | dstPath = "";
63 | dstSubfolderSpec = 10;
64 | files = (
65 | CECEA1D921B6767100F15B7A /* CBTabBarController.framework in Embed Frameworks */,
66 | );
67 | name = "Embed Frameworks";
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXCopyFilesBuildPhase section */
71 |
72 | /* Begin PBXFileReference section */
73 | CE53232D2241270C00D6C307 /* CBTabButtonFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBTabButtonFactory.swift; sourceTree = ""; };
74 | CE53233022424F2E00D6C307 /* CBFlashyTabButtonFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBFlashyTabButtonFactory.swift; sourceTree = ""; };
75 | CE532334224258CB00D6C307 /* CBFadeTabBarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBFadeTabBarButton.swift; sourceTree = ""; };
76 | CE53233622425A9F00D6C307 /* CBMenuTabButtonFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuTabButtonFactory.swift; sourceTree = ""; };
77 | CE5323392242B6D000D6C307 /* CBMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuButton.swift; sourceTree = ""; };
78 | CE53233B2242B71D00D6C307 /* CBBaseTabButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBBaseTabButton.swift; sourceTree = ""; };
79 | CE53233F2242BD3B00D6C307 /* CBMenuTabItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuTabItem.swift; sourceTree = ""; };
80 | CE5323412242F1FC00D6C307 /* btnMenu.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = btnMenu.png; sourceTree = ""; };
81 | CE5323422242F1FC00D6C307 /* btnMenu@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btnMenu@2x.png"; sourceTree = ""; };
82 | CE53234522431B1800D6C307 /* CBMenuPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuPresenter.swift; sourceTree = ""; };
83 | CE53234B2243332900D6C307 /* CBMenuController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CBMenuController.storyboard; sourceTree = ""; };
84 | CE53234D2243353B00D6C307 /* CBMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuController.swift; sourceTree = ""; };
85 | CE53234F2243376200D6C307 /* CBMenuPresentAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuPresentAnimationController.swift; sourceTree = ""; };
86 | CE5323512243BB8500D6C307 /* btnClose@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btnClose@2x.png"; sourceTree = ""; };
87 | CE5323522243BB8500D6C307 /* btnClose.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = btnClose.png; sourceTree = ""; };
88 | CE5323562244AB5900D6C307 /* CGPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGPoint.swift; sourceTree = ""; };
89 | CE5323582244ACA700D6C307 /* CBMenuDismissAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBMenuDismissAnimationController.swift; sourceTree = ""; };
90 | CE53235A2244BF9D00D6C307 /* CBTabBarStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBTabBarStyle.swift; sourceTree = ""; };
91 | CE53235D2244C33A00D6C307 /* CBFadeTabButtonFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBFadeTabButtonFactory.swift; sourceTree = ""; };
92 | CE53235F22454B2400D6C307 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; };
93 | CE5323822245583300D6C307 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
94 | CECEA1A621B6758200F15B7A /* ExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
95 | CECEA1D121B6767100F15B7A /* CBTabBarController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CBTabBarController.framework; sourceTree = BUILT_PRODUCTS_DIR; };
96 | CECEA1D421B6767100F15B7A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
97 | CECEA1DF21B676D000F15B7A /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
98 | CECEA1E321B676D000F15B7A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
99 | CECEA1E421B676D000F15B7A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
100 | CECEA1E521B676D000F15B7A /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
101 | CECEA1E621B676D000F15B7A /* CBSampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBSampleViewController.swift; sourceTree = ""; };
102 | CECEA1E721B676D000F15B7A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
103 | CECEA1F021B676FF00F15B7A /* .gitkeep */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = .gitkeep; path = ../.gitkeep; sourceTree = ""; };
104 | CECEA1F221B676FF00F15B7A /* .gitkeep */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; };
105 | CECEA1F421B676FF00F15B7A /* CBTabItemSelectAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBTabItemSelectAnimation.swift; sourceTree = ""; };
106 | CECEA1F521B676FF00F15B7A /* CBFlashyTabItemBasicAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBFlashyTabItemBasicAnimation.swift; sourceTree = ""; };
107 | CECEA1F621B676FF00F15B7A /* CBTabItemAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBTabItemAnimation.swift; sourceTree = ""; };
108 | CECEA1F721B676FF00F15B7A /* CBTabItemDeselectAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBTabItemDeselectAnimation.swift; sourceTree = ""; };
109 | CECEA1F821B676FF00F15B7A /* CBTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBTabBar.swift; sourceTree = ""; };
110 | CECEA1F921B676FF00F15B7A /* CBFlashyTabBarButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBFlashyTabBarButton.swift; sourceTree = ""; };
111 | CECEA1FA21B676FF00F15B7A /* CBTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBTabBarController.swift; sourceTree = ""; };
112 | CECEA20521B6789C00F15B7A /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; };
113 | CECEA20621B6789C00F15B7A /* CBTabBarController.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = CBTabBarController.podspec; sourceTree = ""; };
114 | CECEA20721B6789C00F15B7A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
115 | CECEA21921B679EE00F15B7A /* CBFlashyTabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBFlashyTabBarController.h; sourceTree = ""; };
116 | /* End PBXFileReference section */
117 |
118 | /* Begin PBXFrameworksBuildPhase section */
119 | CECEA1A321B6758200F15B7A /* Frameworks */ = {
120 | isa = PBXFrameworksBuildPhase;
121 | buildActionMask = 2147483647;
122 | files = (
123 | CECEA1D821B6767100F15B7A /* CBTabBarController.framework in Frameworks */,
124 | );
125 | runOnlyForDeploymentPostprocessing = 0;
126 | };
127 | CECEA1CE21B6767100F15B7A /* Frameworks */ = {
128 | isa = PBXFrameworksBuildPhase;
129 | buildActionMask = 2147483647;
130 | files = (
131 | );
132 | runOnlyForDeploymentPostprocessing = 0;
133 | };
134 | /* End PBXFrameworksBuildPhase section */
135 |
136 | /* Begin PBXGroup section */
137 | CE53232F22424F2000D6C307 /* Flashy */ = {
138 | isa = PBXGroup;
139 | children = (
140 | CE532332224254BF00D6C307 /* Animations */,
141 | CECEA1F921B676FF00F15B7A /* CBFlashyTabBarButton.swift */,
142 | CE53233022424F2E00D6C307 /* CBFlashyTabButtonFactory.swift */,
143 | );
144 | path = Flashy;
145 | sourceTree = "";
146 | };
147 | CE532332224254BF00D6C307 /* Animations */ = {
148 | isa = PBXGroup;
149 | children = (
150 | CECEA1F521B676FF00F15B7A /* CBFlashyTabItemBasicAnimation.swift */,
151 | CECEA1F421B676FF00F15B7A /* CBTabItemSelectAnimation.swift */,
152 | CECEA1F721B676FF00F15B7A /* CBTabItemDeselectAnimation.swift */,
153 | );
154 | path = Animations;
155 | sourceTree = "";
156 | };
157 | CE5323332242587E00D6C307 /* Menu */ = {
158 | isa = PBXGroup;
159 | children = (
160 | CE53233622425A9F00D6C307 /* CBMenuTabButtonFactory.swift */,
161 | CE5323392242B6D000D6C307 /* CBMenuButton.swift */,
162 | CE53233F2242BD3B00D6C307 /* CBMenuTabItem.swift */,
163 | CE53234522431B1800D6C307 /* CBMenuPresenter.swift */,
164 | CE53234D2243353B00D6C307 /* CBMenuController.swift */,
165 | CE53234F2243376200D6C307 /* CBMenuPresentAnimationController.swift */,
166 | CE5323582244ACA700D6C307 /* CBMenuDismissAnimationController.swift */,
167 | );
168 | path = Menu;
169 | sourceTree = "";
170 | };
171 | CE532347224332E400D6C307 /* Images */ = {
172 | isa = PBXGroup;
173 | children = (
174 | CE5323522243BB8500D6C307 /* btnClose.png */,
175 | CE5323512243BB8500D6C307 /* btnClose@2x.png */,
176 | CE5323412242F1FC00D6C307 /* btnMenu.png */,
177 | CE5323422242F1FC00D6C307 /* btnMenu@2x.png */,
178 | );
179 | path = Images;
180 | sourceTree = "";
181 | };
182 | CE532348224332ED00D6C307 /* Storyboard */ = {
183 | isa = PBXGroup;
184 | children = (
185 | CE53234B2243332900D6C307 /* CBMenuController.storyboard */,
186 | );
187 | path = Storyboard;
188 | sourceTree = "";
189 | };
190 | CE5323552244AB4800D6C307 /* Helpers */ = {
191 | isa = PBXGroup;
192 | children = (
193 | CE5323562244AB5900D6C307 /* CGPoint.swift */,
194 | CE53235F22454B2400D6C307 /* Bundle.swift */,
195 | );
196 | path = Helpers;
197 | sourceTree = "";
198 | };
199 | CE53235C2244C31500D6C307 /* Fade */ = {
200 | isa = PBXGroup;
201 | children = (
202 | CE532334224258CB00D6C307 /* CBFadeTabBarButton.swift */,
203 | CE53235D2244C33A00D6C307 /* CBFadeTabButtonFactory.swift */,
204 | );
205 | path = Fade;
206 | sourceTree = "";
207 | };
208 | CECEA19D21B6758200F15B7A = {
209 | isa = PBXGroup;
210 | children = (
211 | CECEA20421B6785D00F15B7A /* Supporting Files */,
212 | CECEA1DE21B676D000F15B7A /* Example */,
213 | CECEA1D221B6767100F15B7A /* CBTabBarController */,
214 | CECEA1A721B6758200F15B7A /* Products */,
215 | );
216 | sourceTree = "";
217 | };
218 | CECEA1A721B6758200F15B7A /* Products */ = {
219 | isa = PBXGroup;
220 | children = (
221 | CECEA1A621B6758200F15B7A /* ExampleApp.app */,
222 | CECEA1D121B6767100F15B7A /* CBTabBarController.framework */,
223 | );
224 | name = Products;
225 | sourceTree = "";
226 | };
227 | CECEA1D221B6767100F15B7A /* CBTabBarController */ = {
228 | isa = PBXGroup;
229 | children = (
230 | CECEA1EF21B676FF00F15B7A /* Assets */,
231 | CECEA1F121B676FF00F15B7A /* Classes */,
232 | CECEA1D421B6767100F15B7A /* Info.plist */,
233 | CECEA21921B679EE00F15B7A /* CBFlashyTabBarController.h */,
234 | );
235 | path = CBTabBarController;
236 | sourceTree = "";
237 | };
238 | CECEA1DE21B676D000F15B7A /* Example */ = {
239 | isa = PBXGroup;
240 | children = (
241 | CECEA1DF21B676D000F15B7A /* ViewController.swift */,
242 | CE5323812245583300D6C307 /* LaunchScreen.storyboard */,
243 | CECEA1E221B676D000F15B7A /* Main.storyboard */,
244 | CECEA1E421B676D000F15B7A /* Images.xcassets */,
245 | CECEA1E521B676D000F15B7A /* AppDelegate.swift */,
246 | CECEA1E621B676D000F15B7A /* CBSampleViewController.swift */,
247 | CECEA1E721B676D000F15B7A /* Info.plist */,
248 | );
249 | path = Example;
250 | sourceTree = "";
251 | };
252 | CECEA1EF21B676FF00F15B7A /* Assets */ = {
253 | isa = PBXGroup;
254 | children = (
255 | CE532348224332ED00D6C307 /* Storyboard */,
256 | CE532347224332E400D6C307 /* Images */,
257 | );
258 | path = Assets;
259 | sourceTree = "";
260 | };
261 | CECEA1F121B676FF00F15B7A /* Classes */ = {
262 | isa = PBXGroup;
263 | children = (
264 | CE53235C2244C31500D6C307 /* Fade */,
265 | CE5323552244AB4800D6C307 /* Helpers */,
266 | CECEA1F021B676FF00F15B7A /* .gitkeep */,
267 | CE5323332242587E00D6C307 /* Menu */,
268 | CECEA1F221B676FF00F15B7A /* .gitkeep */,
269 | CE53232F22424F2000D6C307 /* Flashy */,
270 | CECEA1F321B676FF00F15B7A /* Animation */,
271 | CECEA1F821B676FF00F15B7A /* CBTabBar.swift */,
272 | CE53233B2242B71D00D6C307 /* CBBaseTabButton.swift */,
273 | CECEA1FA21B676FF00F15B7A /* CBTabBarController.swift */,
274 | CE53232D2241270C00D6C307 /* CBTabButtonFactory.swift */,
275 | CE53235A2244BF9D00D6C307 /* CBTabBarStyle.swift */,
276 | );
277 | path = Classes;
278 | sourceTree = "";
279 | };
280 | CECEA1F321B676FF00F15B7A /* Animation */ = {
281 | isa = PBXGroup;
282 | children = (
283 | CECEA1F621B676FF00F15B7A /* CBTabItemAnimation.swift */,
284 | );
285 | path = Animation;
286 | sourceTree = "";
287 | };
288 | CECEA20421B6785D00F15B7A /* Supporting Files */ = {
289 | isa = PBXGroup;
290 | children = (
291 | CECEA20621B6789C00F15B7A /* CBTabBarController.podspec */,
292 | CECEA20521B6789C00F15B7A /* LICENSE */,
293 | CECEA20721B6789C00F15B7A /* README.md */,
294 | );
295 | name = "Supporting Files";
296 | sourceTree = "";
297 | };
298 | /* End PBXGroup section */
299 |
300 | /* Begin PBXHeadersBuildPhase section */
301 | CECEA1CC21B6767100F15B7A /* Headers */ = {
302 | isa = PBXHeadersBuildPhase;
303 | buildActionMask = 2147483647;
304 | files = (
305 | CECEA21A21B679EE00F15B7A /* CBFlashyTabBarController.h in Headers */,
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | };
309 | /* End PBXHeadersBuildPhase section */
310 |
311 | /* Begin PBXNativeTarget section */
312 | CECEA1A521B6758200F15B7A /* ExampleApp */ = {
313 | isa = PBXNativeTarget;
314 | buildConfigurationList = CECEA1B821B6758400F15B7A /* Build configuration list for PBXNativeTarget "ExampleApp" */;
315 | buildPhases = (
316 | CECEA1A221B6758200F15B7A /* Sources */,
317 | CECEA1A321B6758200F15B7A /* Frameworks */,
318 | CECEA1A421B6758200F15B7A /* Resources */,
319 | CECEA1DD21B6767100F15B7A /* Embed Frameworks */,
320 | );
321 | buildRules = (
322 | );
323 | dependencies = (
324 | CECEA1D721B6767100F15B7A /* PBXTargetDependency */,
325 | );
326 | name = ExampleApp;
327 | productName = FlashyTabBar;
328 | productReference = CECEA1A621B6758200F15B7A /* ExampleApp.app */;
329 | productType = "com.apple.product-type.application";
330 | };
331 | CECEA1D021B6767100F15B7A /* CBTabBarController */ = {
332 | isa = PBXNativeTarget;
333 | buildConfigurationList = CECEA1DA21B6767100F15B7A /* Build configuration list for PBXNativeTarget "CBTabBarController" */;
334 | buildPhases = (
335 | CECEA1CC21B6767100F15B7A /* Headers */,
336 | CECEA1CD21B6767100F15B7A /* Sources */,
337 | CECEA1CE21B6767100F15B7A /* Frameworks */,
338 | CECEA1CF21B6767100F15B7A /* Resources */,
339 | );
340 | buildRules = (
341 | );
342 | dependencies = (
343 | );
344 | name = CBTabBarController;
345 | productName = CBFlashyTabBarController;
346 | productReference = CECEA1D121B6767100F15B7A /* CBTabBarController.framework */;
347 | productType = "com.apple.product-type.framework";
348 | };
349 | /* End PBXNativeTarget section */
350 |
351 | /* Begin PBXProject section */
352 | CECEA19E21B6758200F15B7A /* Project object */ = {
353 | isa = PBXProject;
354 | attributes = {
355 | LastSwiftUpdateCheck = 1010;
356 | LastUpgradeCheck = 1010;
357 | ORGANIZATIONNAME = cuberto;
358 | TargetAttributes = {
359 | CECEA1A521B6758200F15B7A = {
360 | CreatedOnToolsVersion = 10.1;
361 | };
362 | CECEA1D021B6767100F15B7A = {
363 | CreatedOnToolsVersion = 10.1;
364 | };
365 | };
366 | };
367 | buildConfigurationList = CECEA1A121B6758200F15B7A /* Build configuration list for PBXProject "CBTabBar" */;
368 | compatibilityVersion = "Xcode 9.3";
369 | developmentRegion = en;
370 | hasScannedForEncodings = 0;
371 | knownRegions = (
372 | en,
373 | Base,
374 | );
375 | mainGroup = CECEA19D21B6758200F15B7A;
376 | productRefGroup = CECEA1A721B6758200F15B7A /* Products */;
377 | projectDirPath = "";
378 | projectRoot = "";
379 | targets = (
380 | CECEA1A521B6758200F15B7A /* ExampleApp */,
381 | CECEA1D021B6767100F15B7A /* CBTabBarController */,
382 | );
383 | };
384 | /* End PBXProject section */
385 |
386 | /* Begin PBXResourcesBuildPhase section */
387 | CECEA1A421B6758200F15B7A /* Resources */ = {
388 | isa = PBXResourcesBuildPhase;
389 | buildActionMask = 2147483647;
390 | files = (
391 | CE5323832245583300D6C307 /* LaunchScreen.storyboard in Resources */,
392 | CECEA1EB21B676D000F15B7A /* Images.xcassets in Resources */,
393 | CECEA1EA21B676D000F15B7A /* Main.storyboard in Resources */,
394 | );
395 | runOnlyForDeploymentPostprocessing = 0;
396 | };
397 | CECEA1CF21B6767100F15B7A /* Resources */ = {
398 | isa = PBXResourcesBuildPhase;
399 | buildActionMask = 2147483647;
400 | files = (
401 | CE5323542243BB8500D6C307 /* btnClose.png in Resources */,
402 | CE53234C2243332900D6C307 /* CBMenuController.storyboard in Resources */,
403 | CE5323442242F1FD00D6C307 /* btnMenu@2x.png in Resources */,
404 | CE5323532243BB8500D6C307 /* btnClose@2x.png in Resources */,
405 | CE5323432242F1FD00D6C307 /* btnMenu.png in Resources */,
406 | );
407 | runOnlyForDeploymentPostprocessing = 0;
408 | };
409 | /* End PBXResourcesBuildPhase section */
410 |
411 | /* Begin PBXSourcesBuildPhase section */
412 | CECEA1A221B6758200F15B7A /* Sources */ = {
413 | isa = PBXSourcesBuildPhase;
414 | buildActionMask = 2147483647;
415 | files = (
416 | CECEA1ED21B676D000F15B7A /* CBSampleViewController.swift in Sources */,
417 | CECEA1EC21B676D000F15B7A /* AppDelegate.swift in Sources */,
418 | CECEA1E821B676D000F15B7A /* ViewController.swift in Sources */,
419 | );
420 | runOnlyForDeploymentPostprocessing = 0;
421 | };
422 | CECEA1CD21B6767100F15B7A /* Sources */ = {
423 | isa = PBXSourcesBuildPhase;
424 | buildActionMask = 2147483647;
425 | files = (
426 | CE53232E2241270C00D6C307 /* CBTabButtonFactory.swift in Sources */,
427 | CECEA1FD21B676FF00F15B7A /* CBTabItemSelectAnimation.swift in Sources */,
428 | CE53234622431B1800D6C307 /* CBMenuPresenter.swift in Sources */,
429 | CE5323402242BD3B00D6C307 /* CBMenuTabItem.swift in Sources */,
430 | CE5323502243376200D6C307 /* CBMenuPresentAnimationController.swift in Sources */,
431 | CECEA1FE21B676FF00F15B7A /* CBFlashyTabItemBasicAnimation.swift in Sources */,
432 | CE53233722425A9F00D6C307 /* CBMenuTabButtonFactory.swift in Sources */,
433 | CE5323572244AB5900D6C307 /* CGPoint.swift in Sources */,
434 | CECEA20321B676FF00F15B7A /* CBTabBarController.swift in Sources */,
435 | CE53235E2244C33A00D6C307 /* CBFadeTabButtonFactory.swift in Sources */,
436 | CECEA20221B676FF00F15B7A /* CBFlashyTabBarButton.swift in Sources */,
437 | CE53235B2244BF9E00D6C307 /* CBTabBarStyle.swift in Sources */,
438 | CE53234E2243353B00D6C307 /* CBMenuController.swift in Sources */,
439 | CECEA20021B676FF00F15B7A /* CBTabItemDeselectAnimation.swift in Sources */,
440 | CE53233A2242B6D000D6C307 /* CBMenuButton.swift in Sources */,
441 | CE532335224258CB00D6C307 /* CBFadeTabBarButton.swift in Sources */,
442 | CECEA1FF21B676FF00F15B7A /* CBTabItemAnimation.swift in Sources */,
443 | CE53233C2242B71D00D6C307 /* CBBaseTabButton.swift in Sources */,
444 | CECEA20121B676FF00F15B7A /* CBTabBar.swift in Sources */,
445 | CE53236022454B2400D6C307 /* Bundle.swift in Sources */,
446 | CE53233122424F2E00D6C307 /* CBFlashyTabButtonFactory.swift in Sources */,
447 | CE5323592244ACA700D6C307 /* CBMenuDismissAnimationController.swift in Sources */,
448 | );
449 | runOnlyForDeploymentPostprocessing = 0;
450 | };
451 | /* End PBXSourcesBuildPhase section */
452 |
453 | /* Begin PBXTargetDependency section */
454 | CECEA1D721B6767100F15B7A /* PBXTargetDependency */ = {
455 | isa = PBXTargetDependency;
456 | target = CECEA1D021B6767100F15B7A /* CBTabBarController */;
457 | targetProxy = CECEA1D621B6767100F15B7A /* PBXContainerItemProxy */;
458 | };
459 | /* End PBXTargetDependency section */
460 |
461 | /* Begin PBXVariantGroup section */
462 | CE5323812245583300D6C307 /* LaunchScreen.storyboard */ = {
463 | isa = PBXVariantGroup;
464 | children = (
465 | CE5323822245583300D6C307 /* Base */,
466 | );
467 | name = LaunchScreen.storyboard;
468 | sourceTree = "";
469 | };
470 | CECEA1E221B676D000F15B7A /* Main.storyboard */ = {
471 | isa = PBXVariantGroup;
472 | children = (
473 | CECEA1E321B676D000F15B7A /* Base */,
474 | );
475 | name = Main.storyboard;
476 | sourceTree = "";
477 | };
478 | /* End PBXVariantGroup section */
479 |
480 | /* Begin XCBuildConfiguration section */
481 | CECEA1B621B6758400F15B7A /* Debug */ = {
482 | isa = XCBuildConfiguration;
483 | buildSettings = {
484 | ALWAYS_SEARCH_USER_PATHS = NO;
485 | CLANG_ANALYZER_NONNULL = YES;
486 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
487 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
488 | CLANG_CXX_LIBRARY = "libc++";
489 | CLANG_ENABLE_MODULES = YES;
490 | CLANG_ENABLE_OBJC_ARC = YES;
491 | CLANG_ENABLE_OBJC_WEAK = YES;
492 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
493 | CLANG_WARN_BOOL_CONVERSION = YES;
494 | CLANG_WARN_COMMA = YES;
495 | CLANG_WARN_CONSTANT_CONVERSION = YES;
496 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
497 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
498 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
499 | CLANG_WARN_EMPTY_BODY = YES;
500 | CLANG_WARN_ENUM_CONVERSION = YES;
501 | CLANG_WARN_INFINITE_RECURSION = YES;
502 | CLANG_WARN_INT_CONVERSION = YES;
503 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
504 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
505 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
506 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
507 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
508 | CLANG_WARN_STRICT_PROTOTYPES = YES;
509 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
510 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
511 | CLANG_WARN_UNREACHABLE_CODE = YES;
512 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
513 | CODE_SIGN_IDENTITY = "iPhone Developer";
514 | COPY_PHASE_STRIP = NO;
515 | DEBUG_INFORMATION_FORMAT = dwarf;
516 | ENABLE_STRICT_OBJC_MSGSEND = YES;
517 | ENABLE_TESTABILITY = YES;
518 | GCC_C_LANGUAGE_STANDARD = gnu11;
519 | GCC_DYNAMIC_NO_PIC = NO;
520 | GCC_NO_COMMON_BLOCKS = YES;
521 | GCC_OPTIMIZATION_LEVEL = 0;
522 | GCC_PREPROCESSOR_DEFINITIONS = (
523 | "DEBUG=1",
524 | "$(inherited)",
525 | );
526 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
527 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
528 | GCC_WARN_UNDECLARED_SELECTOR = YES;
529 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
530 | GCC_WARN_UNUSED_FUNCTION = YES;
531 | GCC_WARN_UNUSED_VARIABLE = YES;
532 | IPHONEOS_DEPLOYMENT_TARGET = 12.1;
533 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
534 | MTL_FAST_MATH = YES;
535 | ONLY_ACTIVE_ARCH = YES;
536 | SDKROOT = iphoneos;
537 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
538 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
539 | };
540 | name = Debug;
541 | };
542 | CECEA1B721B6758400F15B7A /* Release */ = {
543 | isa = XCBuildConfiguration;
544 | buildSettings = {
545 | ALWAYS_SEARCH_USER_PATHS = NO;
546 | CLANG_ANALYZER_NONNULL = YES;
547 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
548 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
549 | CLANG_CXX_LIBRARY = "libc++";
550 | CLANG_ENABLE_MODULES = YES;
551 | CLANG_ENABLE_OBJC_ARC = YES;
552 | CLANG_ENABLE_OBJC_WEAK = YES;
553 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
554 | CLANG_WARN_BOOL_CONVERSION = YES;
555 | CLANG_WARN_COMMA = YES;
556 | CLANG_WARN_CONSTANT_CONVERSION = YES;
557 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
558 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
559 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
560 | CLANG_WARN_EMPTY_BODY = YES;
561 | CLANG_WARN_ENUM_CONVERSION = YES;
562 | CLANG_WARN_INFINITE_RECURSION = YES;
563 | CLANG_WARN_INT_CONVERSION = YES;
564 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
565 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
566 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
567 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
568 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
569 | CLANG_WARN_STRICT_PROTOTYPES = YES;
570 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
571 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
572 | CLANG_WARN_UNREACHABLE_CODE = YES;
573 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
574 | CODE_SIGN_IDENTITY = "iPhone Developer";
575 | COPY_PHASE_STRIP = NO;
576 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
577 | ENABLE_NS_ASSERTIONS = NO;
578 | ENABLE_STRICT_OBJC_MSGSEND = YES;
579 | GCC_C_LANGUAGE_STANDARD = gnu11;
580 | GCC_NO_COMMON_BLOCKS = YES;
581 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
582 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
583 | GCC_WARN_UNDECLARED_SELECTOR = YES;
584 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
585 | GCC_WARN_UNUSED_FUNCTION = YES;
586 | GCC_WARN_UNUSED_VARIABLE = YES;
587 | IPHONEOS_DEPLOYMENT_TARGET = 12.1;
588 | MTL_ENABLE_DEBUG_INFO = NO;
589 | MTL_FAST_MATH = YES;
590 | SDKROOT = iphoneos;
591 | SWIFT_COMPILATION_MODE = wholemodule;
592 | SWIFT_OPTIMIZATION_LEVEL = "-O";
593 | VALIDATE_PRODUCT = YES;
594 | };
595 | name = Release;
596 | };
597 | CECEA1B921B6758400F15B7A /* Debug */ = {
598 | isa = XCBuildConfiguration;
599 | buildSettings = {
600 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
601 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
602 | CODE_SIGN_STYLE = Automatic;
603 | DEVELOPMENT_TEAM = SK7A63GXF6;
604 | INFOPLIST_FILE = Example/Info.plist;
605 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
606 | LD_RUNPATH_SEARCH_PATHS = (
607 | "$(inherited)",
608 | "@executable_path/Frameworks",
609 | );
610 | PRODUCT_BUNDLE_IDENTIFIER = com.cuberto.components.FlashyTabBar;
611 | PRODUCT_NAME = "$(TARGET_NAME)";
612 | SWIFT_VERSION = 4.2;
613 | TARGETED_DEVICE_FAMILY = "1,2";
614 | };
615 | name = Debug;
616 | };
617 | CECEA1BA21B6758400F15B7A /* Release */ = {
618 | isa = XCBuildConfiguration;
619 | buildSettings = {
620 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
621 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
622 | CODE_SIGN_STYLE = Automatic;
623 | DEVELOPMENT_TEAM = SK7A63GXF6;
624 | INFOPLIST_FILE = Example/Info.plist;
625 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
626 | LD_RUNPATH_SEARCH_PATHS = (
627 | "$(inherited)",
628 | "@executable_path/Frameworks",
629 | );
630 | PRODUCT_BUNDLE_IDENTIFIER = com.cuberto.components.FlashyTabBar;
631 | PRODUCT_NAME = "$(TARGET_NAME)";
632 | SWIFT_VERSION = 4.2;
633 | TARGETED_DEVICE_FAMILY = "1,2";
634 | };
635 | name = Release;
636 | };
637 | CECEA1DB21B6767100F15B7A /* Debug */ = {
638 | isa = XCBuildConfiguration;
639 | buildSettings = {
640 | CODE_SIGN_IDENTITY = "";
641 | CODE_SIGN_STYLE = Automatic;
642 | CURRENT_PROJECT_VERSION = 1;
643 | DEFINES_MODULE = YES;
644 | DEVELOPMENT_TEAM = SK7A63GXF6;
645 | DYLIB_COMPATIBILITY_VERSION = 1;
646 | DYLIB_CURRENT_VERSION = 1;
647 | DYLIB_INSTALL_NAME_BASE = "@rpath";
648 | INFOPLIST_FILE = CBTabBarController/Info.plist;
649 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
650 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
651 | LD_RUNPATH_SEARCH_PATHS = (
652 | "$(inherited)",
653 | "@executable_path/Frameworks",
654 | "@loader_path/Frameworks",
655 | );
656 | PRODUCT_BUNDLE_IDENTIFIER = com.cuberto.components.CBTabBarController;
657 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
658 | SKIP_INSTALL = YES;
659 | SWIFT_VERSION = 4.2;
660 | TARGETED_DEVICE_FAMILY = "1,2";
661 | VERSIONING_SYSTEM = "apple-generic";
662 | VERSION_INFO_PREFIX = "";
663 | };
664 | name = Debug;
665 | };
666 | CECEA1DC21B6767100F15B7A /* Release */ = {
667 | isa = XCBuildConfiguration;
668 | buildSettings = {
669 | CODE_SIGN_IDENTITY = "";
670 | CODE_SIGN_STYLE = Automatic;
671 | CURRENT_PROJECT_VERSION = 1;
672 | DEFINES_MODULE = YES;
673 | DEVELOPMENT_TEAM = SK7A63GXF6;
674 | DYLIB_COMPATIBILITY_VERSION = 1;
675 | DYLIB_CURRENT_VERSION = 1;
676 | DYLIB_INSTALL_NAME_BASE = "@rpath";
677 | INFOPLIST_FILE = CBTabBarController/Info.plist;
678 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
679 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
680 | LD_RUNPATH_SEARCH_PATHS = (
681 | "$(inherited)",
682 | "@executable_path/Frameworks",
683 | "@loader_path/Frameworks",
684 | );
685 | PRODUCT_BUNDLE_IDENTIFIER = com.cuberto.components.CBTabBarController;
686 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
687 | SKIP_INSTALL = YES;
688 | SWIFT_VERSION = 4.2;
689 | TARGETED_DEVICE_FAMILY = "1,2";
690 | VERSIONING_SYSTEM = "apple-generic";
691 | VERSION_INFO_PREFIX = "";
692 | };
693 | name = Release;
694 | };
695 | /* End XCBuildConfiguration section */
696 |
697 | /* Begin XCConfigurationList section */
698 | CECEA1A121B6758200F15B7A /* Build configuration list for PBXProject "CBTabBar" */ = {
699 | isa = XCConfigurationList;
700 | buildConfigurations = (
701 | CECEA1B621B6758400F15B7A /* Debug */,
702 | CECEA1B721B6758400F15B7A /* Release */,
703 | );
704 | defaultConfigurationIsVisible = 0;
705 | defaultConfigurationName = Release;
706 | };
707 | CECEA1B821B6758400F15B7A /* Build configuration list for PBXNativeTarget "ExampleApp" */ = {
708 | isa = XCConfigurationList;
709 | buildConfigurations = (
710 | CECEA1B921B6758400F15B7A /* Debug */,
711 | CECEA1BA21B6758400F15B7A /* Release */,
712 | );
713 | defaultConfigurationIsVisible = 0;
714 | defaultConfigurationName = Release;
715 | };
716 | CECEA1DA21B6767100F15B7A /* Build configuration list for PBXNativeTarget "CBTabBarController" */ = {
717 | isa = XCConfigurationList;
718 | buildConfigurations = (
719 | CECEA1DB21B6767100F15B7A /* Debug */,
720 | CECEA1DC21B6767100F15B7A /* Release */,
721 | );
722 | defaultConfigurationIsVisible = 0;
723 | defaultConfigurationName = Release;
724 | };
725 | /* End XCConfigurationList section */
726 | };
727 | rootObject = CECEA19E21B6758200F15B7A /* Project object */;
728 | }
729 |
--------------------------------------------------------------------------------