├── Screenshots ├── Card.png ├── Light.png ├── Special.png └── SemiCard.png ├── LightCardTabBar ├── Resources │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard ├── ViewController.swift ├── Controllers │ ├── CardTabBarController.swift │ ├── LightTabBarController.swift │ ├── SemiCardTabBarController.swift │ ├── SpecialTabBarController.swift │ └── TabBarController.swift ├── Tab Bar │ ├── BaseCardTabBar.swift │ ├── SemiCardTabBar.swift │ ├── CardTabBar.swift │ ├── LightTabBar.swift │ └── SpecialCardTabBar.swift ├── AppDelegate.swift └── Extensions │ └── UIView+Additions.swift ├── LightCardTabBar.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── hussc.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── LightCardTabBarTests ├── Info.plist └── LightCardTabBarTests.swift ├── LightCardTabBarUITests ├── Info.plist └── LightCardTabBarUITests.swift └── README.md /Screenshots/Card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hussc/lightCardTabBar/HEAD/Screenshots/Card.png -------------------------------------------------------------------------------- /Screenshots/Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hussc/lightCardTabBar/HEAD/Screenshots/Light.png -------------------------------------------------------------------------------- /Screenshots/Special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hussc/lightCardTabBar/HEAD/Screenshots/Special.png -------------------------------------------------------------------------------- /Screenshots/SemiCard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hussc/lightCardTabBar/HEAD/Screenshots/SemiCard.png -------------------------------------------------------------------------------- /LightCardTabBar/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LightCardTabBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LightCardTabBar/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LightCardTabBar/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /LightCardTabBar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LightCardTabBar/Controllers/CardTabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardTabBarController.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class CardTabBarController: TabBarController { 11 | 12 | override func makeTabBar() -> BaseCardTabBar { 13 | CardTabBar() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LightCardTabBar/Controllers/LightTabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightTabBarController.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class LightTabBarController: TabBarController { 11 | 12 | override func makeTabBar() -> BaseCardTabBar { 13 | LightTabBar() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LightCardTabBar/Controllers/SemiCardTabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SemiCardTabBarController.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SemiCardTabBarController: TabBarController { 11 | 12 | override func makeTabBar() -> BaseCardTabBar { 13 | SemiCardTabBar() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LightCardTabBar/Controllers/SpecialTabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpecialTabBarController.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SpecialTabBarController: TabBarController { 11 | 12 | 13 | override func makeTabBar() -> BaseCardTabBar { 14 | SpecialCardTabBar() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LightCardTabBar.xcodeproj/xcuserdata/hussc.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LightCardTabBar.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LightCardTabBar/Tab Bar/BaseCardTabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCardTabBar.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol CardTabBarDelegate: AnyObject { 11 | func cardTabBar(_ sender: BaseCardTabBar, didSelectItemAt index: Int) 12 | 13 | func didUpdateHeight() 14 | } 15 | 16 | class BaseCardTabBar: UIView { 17 | 18 | var preferredTabBarHeight: CGFloat { 19 | 70 20 | } 21 | 22 | var preferredBottomBackground: UIColor { 23 | .clear 24 | } 25 | 26 | weak var delegate: CardTabBarDelegate? 27 | 28 | func select(at index: Int, animated: Bool, notifyDelegate: Bool) { 29 | 30 | } 31 | 32 | func set(items: [UITabBarItem]) { 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LightCardTabBarTests/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LightCardTabBarUITests/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LightCardTabBarTests/LightCardTabBarTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightCardTabBarTests.swift 3 | // LightCardTabBarTests 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import XCTest 9 | @testable import LightCardTabBar 10 | 11 | class LightCardTabBarTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /LightCardTabBarUITests/LightCardTabBarUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightCardTabBarUITests.swift 3 | // LightCardTabBarUITests 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class LightCardTabBarUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LightCardTabBar/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | // setupStyleSwizzling() 18 | return true 19 | } 20 | 21 | 22 | // func setupStyleSwizzling(){ 23 | // let originalMethod = class_getInstanceMethod(UIView.self, #selector(UIView.didMoveToSuperview)) 24 | // let swizzledMethod = class_getInstanceMethod(UIView.self, #selector(UIView.swizzledDidMoveToSuperview)) 25 | // method_exchangeImplementations(originalMethod!, swizzledMethod!) 26 | // } 27 | 28 | } 29 | 30 | 31 | 32 | //extension UIView { 33 | // @objc func swizzledDidMoveToSuperview(){ 34 | // self.updateStyle() 35 | // } 36 | //} 37 | // 38 | ///** 39 | // A Stylable represents any entity whishes to update it's attributes based on given style 40 | // */ 41 | //@objc 42 | //protocol Styleable: NSObjectProtocol { 43 | // 44 | // @objc func updateStyle() 45 | //} 46 | // 47 | //extension UIView: Styleable { 48 | // 49 | // func updateStyle() { 50 | // 51 | // } 52 | //} 53 | // 54 | //extension Styleable where Self: UIView { 55 | // func tellChildrenUpdateStyle(){ 56 | // self.subviews.forEach { 57 | // $0.updateStyle() 58 | // $0.tellChildrenUpdateStyle() 59 | // } 60 | // } 61 | //} 62 | -------------------------------------------------------------------------------- /LightCardTabBar/Resources/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /LightCardTabBar/Resources/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 | -------------------------------------------------------------------------------- /LightCardTabBar/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LightCardTabBar 2 | 3 | An expiremental project exploring different types of custom-tabbar styles, screenshots as below. 4 | 5 | ## Screenshots 6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 | ## Implementation 15 | 16 | The implementation relies on two main components: 17 | - TabBarController -> which hides the default tabbar and initializes the custom tabbar, and any nesscary components 18 | - BaseCardTabBar -> A base class for any client who wishes to implement custom tab bar, including any actions needed. 19 | 20 | 21 | ## Examples 22 | 23 | The Example Project includes **4 styles** of custom-tabbar, crafted and inspired by well made designs. 24 | 25 | Each type of tabbar has the two main components, it's own **tabbarcontroller** and **tabbar**, all have the same inteface to interact with. 26 | 27 | 28 | ## Your own tabbar! 29 | 30 | The Example project demonstrates different types of tabbar represents a wide range of applications you can use, I hope the examples provide a starting point if you want to implement your own. 31 | 32 | And as always, you can pull request a new style! 33 | 34 | ## License 35 | 36 | Copyright 2021 Hussein AlRyalat 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 43 | -------------------------------------------------------------------------------- /LightCardTabBar/Controllers/TabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarController.swift 3 | // tweetly 4 | // 5 | // Created by Hussein Work on 28/11/2020. 6 | // 7 | 8 | import UIKit 9 | 10 | class TabBarController: UITabBarController { 11 | 12 | var tabBarHeight: CGFloat { 13 | customTabBar.preferredTabBarHeight 14 | } 15 | 16 | lazy var customTabBar: BaseCardTabBar = makeTabBar() 17 | lazy var anotherSmallView = UIView() 18 | 19 | 20 | fileprivate var anotherSmallViewBottomConstraint: NSLayoutConstraint? 21 | fileprivate var tabBarHeightConstraint: NSLayoutConstraint? 22 | 23 | override var selectedIndex: Int { 24 | didSet { 25 | customTabBar.select(at: selectedIndex, animated: false, notifyDelegate: false) 26 | } 27 | } 28 | 29 | override var selectedViewController: UIViewController? { 30 | didSet { 31 | customTabBar.select(at: selectedIndex, animated: false, notifyDelegate: false) 32 | } 33 | } 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | 38 | self.tabBar.isHidden = true 39 | 40 | setupTabBar() 41 | updateTabBarHeightIfNeeded() 42 | } 43 | 44 | override func viewWillAppear(_ animated: Bool) { 45 | super.viewWillAppear(animated) 46 | 47 | customTabBar.set(items: tabBar.items ?? []) 48 | customTabBar.select(at: selectedIndex, animated: false, notifyDelegate: true) 49 | } 50 | 51 | 52 | fileprivate func setupTabBar(){ 53 | self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: tabBarHeight, right: 0) 54 | 55 | customTabBar.delegate = self 56 | customTabBar.translatesAutoresizingMaskIntoConstraints = false 57 | self.view.addSubview(customTabBar) 58 | 59 | 60 | anotherSmallView.backgroundColor = customTabBar.preferredBottomBackground 61 | anotherSmallView.translatesAutoresizingMaskIntoConstraints = false 62 | self.view.addSubview(anotherSmallView) 63 | 64 | anotherSmallView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true 65 | anotherSmallView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 66 | anotherSmallView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 67 | 68 | customTabBar.bottomAnchor.constraint(equalTo: anotherSmallView.topAnchor).isActive = true 69 | customTabBar.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true 70 | customTabBar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true 71 | 72 | self.view.bringSubviewToFront(customTabBar) 73 | self.view.bringSubviewToFront(anotherSmallView) 74 | } 75 | 76 | fileprivate func updateTabBarHeightIfNeeded(){ 77 | self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: tabBarHeight, right: 0) 78 | 79 | 80 | tabBarHeightConstraint = customTabBar.heightAnchor.constraint(equalToConstant: tabBarHeight) 81 | tabBarHeightConstraint?.isActive = true 82 | 83 | anotherSmallViewBottomConstraint = anotherSmallView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: tabBarHeight) 84 | 85 | 86 | anotherSmallViewBottomConstraint?.priority = .defaultHigh 87 | anotherSmallViewBottomConstraint?.isActive = true 88 | } 89 | 90 | func setTabBarHidden(_ isHidden: Bool, animated: Bool){ 91 | let block = { 92 | self.customTabBar.alpha = isHidden ? 0 : 1 93 | self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: isHidden ? 0 : self.tabBarHeight, right: 0) 94 | } 95 | 96 | if animated { 97 | UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: block, completion: nil) 98 | } else { 99 | block() 100 | } 101 | } 102 | 103 | 104 | func makeTabBar() -> BaseCardTabBar { 105 | SpecialCardTabBar() 106 | } 107 | } 108 | 109 | extension TabBarController: CardTabBarDelegate { 110 | func cardTabBar(_ sender: BaseCardTabBar, didSelectItemAt index: Int) { 111 | self.selectedIndex = index 112 | } 113 | 114 | func didUpdateHeight() { 115 | self.updateTabBarHeightIfNeeded() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /LightCardTabBar/Extensions/UIView+Additions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIStackView+Additions.swift 3 | // talabyeh 4 | // 5 | // Created by Hussein Work on 11/11/20. 6 | // Copyright © 2020 Dominate. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension CGPoint { 12 | func distance(to point: CGPoint) -> CGFloat { 13 | return hypot(self.x - point.x, self.y - point.y) 14 | } 15 | } 16 | 17 | 18 | @resultBuilder public struct SubviewsBuilder { 19 | public static func buildBlock(_ content: UIView...) -> [UIView] { 20 | return content 21 | } 22 | } 23 | 24 | /** 25 | Creating a chainable stackview to make life easiere 26 | */ 27 | 28 | extension UIStackView { 29 | @discardableResult 30 | func alignment(_ alignment: Alignment) -> UIStackView { 31 | self.alignment = alignment 32 | return self 33 | } 34 | 35 | @discardableResult 36 | func distribution(_ distribution: Distribution) -> UIStackView { 37 | self.distribution = distribution 38 | return self 39 | } 40 | 41 | @discardableResult 42 | func spacing(_ spacing: CGFloat) -> UIStackView { 43 | self.spacing = spacing 44 | return self 45 | } 46 | 47 | @discardableResult 48 | func axis(_ axis: NSLayoutConstraint.Axis) -> UIStackView { 49 | self.axis = axis 50 | return self 51 | } 52 | 53 | @discardableResult 54 | func addingArrangedSubviews(_ arrangedSubviews: [UIView]) -> UIStackView { 55 | arrangedSubviews.forEach { 56 | self.addArrangedSubview($0) 57 | } 58 | 59 | return self 60 | } 61 | 62 | @discardableResult 63 | func preparedForAutolayout() -> UIStackView { 64 | translatesAutoresizingMaskIntoConstraints = false 65 | return self 66 | } 67 | 68 | @discardableResult 69 | func addingArrangedSubviews(@SubviewsBuilder content: () -> [UIView]) -> UIStackView { 70 | for sv in content() { 71 | addArrangedSubview(sv) 72 | sv.translatesAutoresizingMaskIntoConstraints = false 73 | } 74 | return self 75 | } 76 | } 77 | 78 | extension UIView { 79 | func subviewsPreparedAL(@SubviewsBuilder content: () -> [UIView]){ 80 | for view in content() { 81 | addSubview(view) 82 | view.translatesAutoresizingMaskIntoConstraints = false 83 | } 84 | } 85 | } 86 | 87 | extension UIView { 88 | func pinToSafeArea(top: CGFloat? = 0, left: CGFloat? = 0, bottom: CGFloat? = 0, right: CGFloat? = 0){ 89 | guard let superview = self.superview else { return } 90 | 91 | prepareForAutoLayout() 92 | 93 | var guide: UILayoutGuide 94 | if #available(iOS 11.0, *) { 95 | guide = superview.safeAreaLayoutGuide 96 | } else { 97 | guide = superview.layoutMarginsGuide 98 | } 99 | 100 | if let top = top { 101 | self.topAnchor.constraint(equalTo: guide.topAnchor, constant: top).isActive = true 102 | } 103 | 104 | if let bottom = bottom { 105 | self.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: bottom).isActive = true 106 | } 107 | 108 | if let left = left { 109 | self.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: left).isActive = true 110 | } 111 | 112 | if let right = right { 113 | self.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: right).isActive = true 114 | } 115 | } 116 | 117 | func pinToSuperView(top: CGFloat? = 0, left: CGFloat? = 0, bottom: CGFloat? = 0, right: CGFloat? = 0){ 118 | guard let superview = self.superview else { return } 119 | 120 | prepareForAutoLayout() 121 | 122 | if let top = top { 123 | self.topAnchor.constraint(equalTo: superview.topAnchor, constant: top).isActive = true 124 | } 125 | 126 | if let bottom = bottom { 127 | self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: bottom).isActive = true 128 | } 129 | 130 | if let left = left { 131 | self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: left).isActive = true 132 | } 133 | 134 | if let right = right { 135 | self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: right).isActive = true 136 | } 137 | } 138 | 139 | func centerInSuperView(){ 140 | guard let superview = self.superview else { return } 141 | 142 | prepareForAutoLayout() 143 | 144 | self.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true 145 | self.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true 146 | } 147 | 148 | func constraint(width: CGFloat){ 149 | prepareForAutoLayout() 150 | self.widthAnchor.constraint(equalToConstant: width).isActive = true 151 | } 152 | 153 | func constraint(height: CGFloat){ 154 | prepareForAutoLayout() 155 | self.heightAnchor.constraint(equalToConstant: height).isActive = true 156 | } 157 | 158 | func makeWidthEqualHeight(){ 159 | prepareForAutoLayout() 160 | self.widthAnchor.constraint(equalTo: self.heightAnchor).isActive = true 161 | } 162 | 163 | func prepareForAutoLayout(){ 164 | translatesAutoresizingMaskIntoConstraints = false 165 | } 166 | } 167 | 168 | -------------------------------------------------------------------------------- /LightCardTabBar/Tab Bar/SemiCardTabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardTabBar.swift 3 | // tweetly 4 | // 5 | // Created by Hussein AlRyalat on 13/05/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | class SemiCardTabBar: BaseCardTabBar { 12 | 13 | struct TabBarItem: Equatable { 14 | var image: UIImage 15 | } 16 | 17 | override var preferredBottomBackground: UIColor { 18 | .white 19 | } 20 | 21 | private lazy var stackView: UIStackView = { 22 | let stackView = UIStackView(arrangedSubviews: []) 23 | stackView.translatesAutoresizingMaskIntoConstraints = false 24 | stackView.axis = .horizontal 25 | stackView.distribution = .fillEqually 26 | stackView.alignment = .center 27 | 28 | self.addSubview(stackView) 29 | 30 | stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 31 | stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true 32 | stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 33 | stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 34 | 35 | 36 | return stackView 37 | }() 38 | 39 | private(set) var items: [TabBarItem] = [] 40 | fileprivate(set) var selectedIndex: Int = 0 41 | 42 | override init(frame: CGRect) { 43 | super.init(frame: frame) 44 | setup() 45 | } 46 | 47 | required init?(coder aDecoder: NSCoder) { 48 | super.init(coder: aDecoder) 49 | setup() 50 | } 51 | 52 | deinit { 53 | stackView.arrangedSubviews.forEach { 54 | if let button = $0 as? UIControl { 55 | button.removeTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside) 56 | } 57 | } 58 | } 59 | 60 | private func setup(){ 61 | 62 | updateStyle() 63 | } 64 | 65 | func updateStyle() { 66 | self.backgroundColor = .white 67 | self.translatesAutoresizingMaskIntoConstraints = false 68 | self.layer.cornerRadius = 12 69 | self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] 70 | self.layer.shadowColor = UIColor.lightGray.cgColor 71 | self.layer.shadowOffset = CGSize(width: 0, height: -2) 72 | self.layer.shadowRadius = 4 73 | self.layer.shadowOpacity = 0.3 74 | 75 | let selected = self.selectedIndex 76 | self.select(at: selected, animated: false, notifyDelegate: false) 77 | } 78 | 79 | func add(item: TabBarItem){ 80 | self.items.append(item) 81 | self.addButton(with: item.image) 82 | } 83 | 84 | func hide(at index: Int){ 85 | guard stackView.arrangedSubviews.count > index, index >= 0 else { 86 | return 87 | } 88 | 89 | stackView.arrangedSubviews[index].isHidden = true 90 | } 91 | 92 | func unhide(at index: Int){ 93 | guard stackView.arrangedSubviews.count > index, index >= 0 else { 94 | return 95 | } 96 | 97 | stackView.arrangedSubviews[index].isHidden = false 98 | } 99 | 100 | func remove(item: TabBarItem){ 101 | if let index = self.items.firstIndex(of: item) { 102 | self.items.remove(at: index) 103 | let view = self.stackView.arrangedSubviews[index] 104 | self.stackView.removeArrangedSubview(view) 105 | } 106 | } 107 | 108 | private func addButton(with image: UIImage){ 109 | let button = UIButton() 110 | 111 | button.translatesAutoresizingMaskIntoConstraints = false 112 | button.setImage(image, for: .normal) 113 | button.tintColor = tintColor 114 | button.tag = self.items.count 115 | 116 | button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside) 117 | self.stackView.addArrangedSubview(button) 118 | } 119 | 120 | override func set(items: [UITabBarItem]) { 121 | items.compactMap { $0.image }.forEach { 122 | self.add(item: .init(image: $0)) 123 | } 124 | } 125 | 126 | override func select(at index: Int, animated: Bool, notifyDelegate: Bool = true){ 127 | selectedIndex = index 128 | for (bIndex, view) in stackView.arrangedSubviews.enumerated() { 129 | if let button = view as? UIButton { 130 | button.tintColor = bIndex == index ? .black : UIColor.black.withAlphaComponent(0.4) 131 | } 132 | } 133 | 134 | if notifyDelegate { 135 | self.delegate?.cardTabBar(self, didSelectItemAt: index) 136 | } 137 | } 138 | 139 | @objc func buttonTapped(sender: UIButton){ 140 | if let index = stackView.arrangedSubviews.firstIndex(of: sender){ 141 | select(at: index, animated: true) 142 | } 143 | } 144 | 145 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 146 | guard let position = touches.first?.location(in: self) else { 147 | super.touchesEnded(touches, with: event) 148 | return 149 | } 150 | 151 | let buttons = self.stackView.arrangedSubviews.compactMap { $0 as? UIButton }.filter { !$0.isHidden } 152 | let distances = buttons.map { $0.center.distance(to: position) } 153 | 154 | let buttonsDistances = zip(buttons, distances) 155 | 156 | if let closestButton = buttonsDistances.min(by: { $0.1 < $1.1 }) { 157 | buttonTapped(sender: closestButton.0) 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /LightCardTabBar/Tab Bar/CardTabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardTabBar.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class CardTabBar: BaseCardTabBar { 11 | 12 | override var preferredTabBarHeight: CGFloat { 13 | 75 14 | } 15 | 16 | override var preferredBottomBackground: UIColor { 17 | .clear 18 | } 19 | 20 | lazy var containerView: UIView = UIView() 21 | 22 | lazy var stackView: UIStackView = { 23 | let stackView = UIStackView(arrangedSubviews: []) 24 | stackView.axis = .horizontal 25 | stackView.distribution = .fillEqually 26 | stackView.alignment = .center 27 | 28 | return stackView 29 | }() 30 | 31 | 32 | lazy var indicatorView: PTIndicatorView = { 33 | let view = PTIndicatorView() 34 | view.translatesAutoresizingMaskIntoConstraints = false 35 | view.constraint(width: 4) 36 | view.backgroundColor = .black 37 | view.makeWidthEqualHeight() 38 | 39 | return view 40 | }() 41 | 42 | 43 | private var indicatorViewXConstraint: NSLayoutConstraint! 44 | 45 | override init(frame: CGRect) { 46 | super.init(frame: frame) 47 | setup() 48 | } 49 | 50 | required public init?(coder aDecoder: NSCoder) { 51 | super.init(coder: aDecoder) 52 | setup() 53 | } 54 | 55 | deinit { 56 | stackView.arrangedSubviews.forEach { 57 | if let button = $0 as? UIControl { 58 | button.removeTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside) 59 | } 60 | } 61 | } 62 | 63 | private func setup(){ 64 | translatesAutoresizingMaskIntoConstraints = false 65 | 66 | subviewsPreparedAL { 67 | containerView 68 | } 69 | 70 | containerView.subviewsPreparedAL { 71 | stackView 72 | indicatorView 73 | } 74 | 75 | containerView.pinToSuperView(top: 0, left: 20, bottom: -15, right: -20) 76 | stackView.pinToSuperView(top: 0, left: 20, bottom: nil, right: -20) 77 | stackView.centerInSuperView() 78 | 79 | indicatorView.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: -12).isActive = true 80 | 81 | updateStyle() 82 | } 83 | 84 | 85 | func updateStyle(){ 86 | containerView.backgroundColor = .white 87 | containerView.layer.shadowColor = UIColor.black.cgColor 88 | containerView.layer.shadowOffset = CGSize(width: 3, height: 3) 89 | containerView.layer.shadowRadius = 6 90 | containerView.layer.shadowOpacity = 0.15 91 | } 92 | 93 | 94 | override func set(items: [UITabBarItem]) { 95 | for button in (stackView.arrangedSubviews.compactMap { $0 as? PTBarButton }) { 96 | stackView.removeArrangedSubview(button) 97 | button.removeFromSuperview() 98 | button.removeTarget(self, action: nil, for: .touchUpInside) 99 | } 100 | 101 | for item in items { 102 | if let image = item.image { 103 | addButton(with: image) 104 | } else { 105 | addButton(with: UIImage()) 106 | } 107 | } 108 | 109 | layoutIfNeeded() 110 | } 111 | 112 | override func select(at index: Int, animated: Bool, notifyDelegate: Bool) { 113 | /* move the indicator view */ 114 | if indicatorViewXConstraint != nil { 115 | indicatorViewXConstraint.isActive = false 116 | indicatorViewXConstraint = nil 117 | } 118 | 119 | for (bIndex, button) in buttons().enumerated() { 120 | button.selectedColor = .black 121 | button.isSelected = bIndex == index 122 | 123 | if bIndex == index { 124 | indicatorViewXConstraint = indicatorView.centerXAnchor.constraint(equalTo: button.centerXAnchor) 125 | indicatorViewXConstraint.isActive = true 126 | } 127 | } 128 | 129 | UIView.animate(withDuration: 0.25) { 130 | self.layoutIfNeeded() 131 | } 132 | 133 | 134 | if notifyDelegate { 135 | self.delegate?.cardTabBar(self, didSelectItemAt: index) 136 | } 137 | } 138 | 139 | private func addButton(with image: UIImage){ 140 | let button = PTBarButton(image: image) 141 | button.translatesAutoresizingMaskIntoConstraints = false 142 | button.selectedColor = .black 143 | 144 | button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside) 145 | self.stackView.addArrangedSubview(button) 146 | } 147 | 148 | private func buttons() -> [PTBarButton] { 149 | return stackView.arrangedSubviews.compactMap { $0 as? PTBarButton } 150 | } 151 | 152 | 153 | @objc func buttonTapped(sender: PTBarButton){ 154 | if let index = stackView.arrangedSubviews.firstIndex(of: sender){ 155 | select(at: index, animated: true, notifyDelegate: true) 156 | } 157 | } 158 | 159 | override open func touchesEnded(_ touches: Set, with event: UIEvent?) { 160 | guard let position = touches.first?.location(in: self) else { 161 | super.touchesEnded(touches, with: event) 162 | return 163 | } 164 | 165 | let buttons = self.stackView.arrangedSubviews.compactMap { $0 as? PTBarButton }.filter { !$0.isHidden } 166 | let distances = buttons.map { $0.center.distance(to: position) } 167 | 168 | let buttonsDistances = zip(buttons, distances) 169 | 170 | if let closestButton = buttonsDistances.min(by: { $0.1 < $1.1 }) { 171 | buttonTapped(sender: closestButton.0) 172 | } 173 | } 174 | 175 | override open func layoutSubviews() { 176 | super.layoutSubviews() 177 | containerView.layer.cornerRadius = containerView.bounds.height / 2 178 | } 179 | } 180 | 181 | extension CardTabBar { 182 | open class PTIndicatorView: UIView { 183 | override open func layoutSubviews() { 184 | super.layoutSubviews() 185 | self.backgroundColor = .black 186 | self.layer.cornerRadius = self.bounds.height / 2 187 | } 188 | } 189 | 190 | 191 | public class PTBarButton: UIButton { 192 | 193 | var selectedColor: UIColor = .black { 194 | didSet { 195 | reloadApperance() 196 | } 197 | } 198 | 199 | var unselectedColor: UIColor = .lightGray { 200 | didSet { 201 | reloadApperance() 202 | } 203 | } 204 | 205 | init(forItem item: UITabBarItem) { 206 | super.init(frame: .zero) 207 | setImage(item.image, for: .normal) 208 | } 209 | 210 | init(image: UIImage){ 211 | super.init(frame: .zero) 212 | setImage(image, for: .normal) 213 | } 214 | 215 | required init?(coder aDecoder: NSCoder) { 216 | super.init(coder: aDecoder) 217 | } 218 | 219 | 220 | override public var isSelected: Bool { 221 | didSet { 222 | reloadApperance() 223 | } 224 | } 225 | 226 | func reloadApperance(){ 227 | self.tintColor = isSelected ? selectedColor : unselectedColor 228 | } 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /LightCardTabBar/Tab Bar/LightTabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightTabBar.swift 3 | // LightCardTabBar 4 | // 5 | // Created by Hussein AlRyalat on 05/06/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class LightTabBar: BaseCardTabBar { 11 | 12 | override var preferredBottomBackground: UIColor { 13 | .clear 14 | } 15 | 16 | override var preferredTabBarHeight: CGFloat { 17 | 75 18 | } 19 | 20 | let containerView = UIView() 21 | let containerStackView = UIStackView() 22 | let indicatorView = IndicatorView() 23 | 24 | fileprivate var buttons: [BaseButton] = [] 25 | var selectedIndex: Int = 0 26 | 27 | fileprivate var indicatorCenterX: NSLayoutConstraint! 28 | fileprivate var indicatorWidth: NSLayoutConstraint! 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | setup() 33 | } 34 | 35 | required init?(coder: NSCoder) { 36 | super.init(coder: coder) 37 | setup() 38 | } 39 | 40 | func setup() { 41 | subviewsPreparedAL { 42 | containerView 43 | } 44 | 45 | containerView.subviewsPreparedAL { 46 | containerStackView 47 | indicatorView 48 | } 49 | 50 | containerStackView.distribution = .fillEqually 51 | containerStackView.spacing = 15 52 | containerStackView.axis = .horizontal 53 | containerStackView.alignment = .fill 54 | 55 | indicatorView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true 56 | indicatorView.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true 57 | 58 | containerView.pinToSuperView(top: 7.5, left: 20, bottom: -7.5, right: -20) 59 | containerStackView.pinToSuperView(top: 10, left: 10, bottom: -10, right: -10) 60 | 61 | updateStyle() 62 | } 63 | 64 | func updateStyle() { 65 | backgroundColor = .clear 66 | 67 | containerView.backgroundColor = .black 68 | 69 | containerView.translatesAutoresizingMaskIntoConstraints = false 70 | containerView.layer.cornerRadius = 12 71 | containerView.layer.shadowColor = UIColor.lightGray.cgColor 72 | containerView.layer.shadowOffset = CGSize(width: 0, height: -2) 73 | containerView.layer.shadowRadius = 4 74 | containerView.layer.shadowOpacity = 0.3 75 | } 76 | 77 | override func select(at index: Int, animated: Bool, notifyDelegate: Bool) { 78 | guard !buttons.isEmpty else { return } 79 | 80 | self.selectedIndex = index 81 | let selectedButton = buttons[index] 82 | 83 | 84 | indicatorCenterX?.isActive = false 85 | indicatorWidth?.isActive = false 86 | 87 | indicatorCenterX = indicatorView.centerXAnchor.constraint(equalTo: selectedButton.centerXAnchor) 88 | indicatorCenterX.isActive = true 89 | 90 | indicatorWidth = indicatorView.widthAnchor.constraint(equalTo: selectedButton.widthAnchor) 91 | indicatorWidth.isActive = true 92 | 93 | let block = { 94 | self.buttons.forEach { 95 | $0._isSelected = false 96 | } 97 | 98 | selectedButton._isSelected = true 99 | 100 | self.containerView.layoutIfNeeded() 101 | } 102 | 103 | if animated { 104 | UIView.animate(withDuration: 0.3, animations: block) 105 | } else { 106 | block() 107 | } 108 | 109 | if notifyDelegate { 110 | self.delegate?.cardTabBar(self, didSelectItemAt: index) 111 | } 112 | } 113 | 114 | override func set(items: [UITabBarItem]) { 115 | self.buttons = [] 116 | self.containerStackView.arrangedSubviews.forEach { 117 | self.containerStackView.removeArrangedSubview($0) 118 | $0.removeFromSuperview() 119 | } 120 | 121 | let buttons: [IconOnlyButton] = items.enumerated().map { 122 | let button = IconOnlyButton(image: $0.element.image, selectedImage: $0.element.selectedImage) 123 | button.tag = $0.offset 124 | return button 125 | } 126 | 127 | 128 | buttons.forEach { 129 | $0.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside) 130 | } 131 | 132 | self.buttons = buttons 133 | self.containerStackView.addingArrangedSubviews(buttons) 134 | } 135 | 136 | @objc func buttonTapped(sender: BaseButton){ 137 | self.select(at: sender.tag, animated: true, notifyDelegate: true) 138 | } 139 | } 140 | 141 | 142 | extension LightTabBar { 143 | class IndicatorView: UIView { 144 | 145 | let topView = UIView() 146 | let gradientView = GradientView() 147 | let gradientMask = CAShapeLayer() 148 | 149 | let offset: CGFloat = 4 150 | 151 | override init(frame: CGRect) { 152 | super.init(frame: frame) 153 | setup() 154 | } 155 | 156 | required init?(coder: NSCoder) { 157 | super.init(coder: coder) 158 | setup() 159 | } 160 | 161 | func setup() { 162 | 163 | subviewsPreparedAL { 164 | gradientView 165 | topView 166 | } 167 | 168 | gradientView.pinToSuperView() 169 | 170 | topView.pinToSuperView(top: 0, left: offset, bottom: nil, right: -offset) 171 | topView.constraint(height: 3) 172 | 173 | 174 | updateStyle() 175 | } 176 | 177 | func updateStyle() { 178 | gradientView.colors = [.white.withAlphaComponent(0.2), .white.withAlphaComponent(0)] 179 | gradientView.locations = [0, 0.8] 180 | gradientView.isOpaque = false 181 | 182 | topView.backgroundColor = .white 183 | topView.layer.cornerRadius = 1 184 | } 185 | 186 | override func layoutSubviews() { 187 | super.layoutSubviews() 188 | 189 | self.gradientMask.path = pathForMask(in: bounds, offset: offset * 2).cgPath 190 | self.gradientView.layer.mask = gradientMask 191 | } 192 | 193 | func pathForMask(in rect: CGRect, offset: CGFloat) -> UIBezierPath { 194 | let path = UIBezierPath() 195 | 196 | path.move(to: CGPoint(x: offset, y: 0)) 197 | path.addLine(to: CGPoint(x: rect.width - offset, y: 0)) 198 | path.addLine(to: CGPoint(x: rect.width, y: rect.height)) 199 | path.addLine(to: CGPoint(x: 0, y: rect.height)) 200 | path.close() 201 | 202 | return path 203 | } 204 | } 205 | 206 | class BaseButton: UIButton { 207 | var _isSelected: Bool = false { 208 | didSet { 209 | updateStyle() 210 | } 211 | } 212 | 213 | func updateStyle() { 214 | 215 | } 216 | } 217 | 218 | class IconOnlyButton: BaseButton { 219 | 220 | let image: UIImage? 221 | let selectedImage: UIImage? 222 | 223 | init(image: UIImage?, selectedImage: UIImage?){ 224 | self.image = image 225 | self.selectedImage = selectedImage 226 | super.init(frame: .zero) 227 | 228 | 229 | translatesAutoresizingMaskIntoConstraints = false 230 | } 231 | 232 | required init?(coder: NSCoder) { 233 | fatalError("init(coder:) has not been implemented") 234 | } 235 | 236 | override func updateStyle(){ 237 | setImage(_isSelected ? selectedImage : image, for: .normal) 238 | tintColor = _isSelected ? .white : UIColor.white.withAlphaComponent(0.25) 239 | } 240 | 241 | override var intrinsicContentSize: CGSize { 242 | .init(width: 45, height: 45) 243 | } 244 | } 245 | 246 | } 247 | 248 | class GradientView: UIView { 249 | 250 | var colors: [UIColor]? { 251 | didSet { 252 | updateGradient() 253 | } 254 | } 255 | 256 | var locations: [CGFloat]? { 257 | didSet { 258 | updateGradient() 259 | } 260 | } 261 | 262 | override func draw(_ rect: CGRect) { 263 | let context = UIGraphicsGetCurrentContext() 264 | let size = bounds.size 265 | 266 | // Gradient 267 | if let gradient = gradient { 268 | let options: CGGradientDrawingOptions = [.drawsAfterEndLocation] 269 | 270 | let startPoint = CGPoint.zero 271 | let endPoint = CGPoint(x: 0, y: size.height) 272 | context?.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: options) 273 | } 274 | } 275 | 276 | 277 | override func didMoveToWindow() { 278 | super.didMoveToWindow() 279 | contentMode = .redraw 280 | } 281 | 282 | // MARK: - Private 283 | private var gradient: CGGradient? 284 | 285 | private func updateGradient() { 286 | gradient = nil 287 | setNeedsDisplay() 288 | 289 | if let colors = self.colors { 290 | let colorSpace = CGColorSpaceCreateDeviceRGB() 291 | let colorSpaceModel = colorSpace.model 292 | 293 | let gradientColors = colors.map { (color: UIColor) -> AnyObject in 294 | let cgColor = color.cgColor 295 | let cgColorSpace = cgColor.colorSpace ?? colorSpace 296 | 297 | if cgColorSpace.model == colorSpaceModel { 298 | return cgColor as AnyObject 299 | } 300 | 301 | var red: CGFloat = 0 302 | var blue: CGFloat = 0 303 | var green: CGFloat = 0 304 | var alpha: CGFloat = 0 305 | color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) 306 | return UIColor(red: red, green: green, blue: blue, alpha: alpha).cgColor as AnyObject 307 | } as NSArray 308 | 309 | gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: locations) 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /LightCardTabBar/Resources/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 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 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 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /LightCardTabBar/Tab Bar/SpecialCardTabBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpecialCardTabBar.swift 3 | // tweetly 4 | // 5 | // Created by Hussein AlRyalat on 20/05/2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | class SpecialCardTabBar: BaseCardTabBar { 12 | 13 | override var preferredBottomBackground: UIColor { 14 | .white 15 | } 16 | 17 | let textCotainerStackView = UIStackView() 18 | let rightButtonsContainerStackView = UIStackView() 19 | let indicatorView = IndicatorView() 20 | 21 | fileprivate(set) var selectedIndex: Int = 0 22 | 23 | fileprivate var buttons: [BaseButton] = [] 24 | fileprivate var firstTrigger: Bool = false 25 | fileprivate var indicatorCenterXConstraint: NSLayoutConstraint! 26 | 27 | override init(frame: CGRect) { 28 | super.init(frame: frame) 29 | setup() 30 | } 31 | 32 | required init?(coder: NSCoder) { 33 | super.init(coder: coder) 34 | setup() 35 | } 36 | 37 | func setup() { 38 | subviewsPreparedAL { 39 | textCotainerStackView 40 | rightButtonsContainerStackView 41 | indicatorView 42 | } 43 | 44 | textCotainerStackView.distribution = .fill 45 | textCotainerStackView.alignment = .center 46 | textCotainerStackView.axis = .horizontal 47 | textCotainerStackView.spacing = 15 48 | 49 | rightButtonsContainerStackView.distribution = .fill 50 | rightButtonsContainerStackView.alignment = .center 51 | rightButtonsContainerStackView.axis = .horizontal 52 | rightButtonsContainerStackView.spacing = 15 53 | 54 | textCotainerStackView.pinToSuperView(top: 0, left: 20, bottom: nil, right: nil) 55 | textCotainerStackView.bottomAnchor.constraint(equalTo: indicatorView.topAnchor, constant: 10).isActive = true 56 | 57 | rightButtonsContainerStackView.pinToSuperView(top: 8, left: nil, bottom: nil, right: -20) 58 | rightButtonsContainerStackView.centerYAnchor.constraint(equalTo: textCotainerStackView.centerYAnchor).isActive = true 59 | rightButtonsContainerStackView.leadingAnchor.constraint(greaterThanOrEqualTo: textCotainerStackView.trailingAnchor, constant: 15).isActive = true 60 | 61 | 62 | updateStyle() 63 | } 64 | 65 | func updateStyle() { 66 | self.backgroundColor = .white 67 | self.translatesAutoresizingMaskIntoConstraints = false 68 | self.layer.cornerRadius = 12 69 | self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] 70 | self.layer.shadowColor = UIColor.lightGray.cgColor 71 | self.layer.shadowOffset = CGSize(width: 0, height: -2) 72 | self.layer.shadowRadius = 4 73 | self.layer.shadowOpacity = 0.3 74 | 75 | let selected = self.selectedIndex 76 | self.select(at: selected, notifyDelegate: false) 77 | } 78 | 79 | func select(at index: Int, notifyDelegate: Bool){ 80 | self.select(at: index, animated: false, notifyDelegate: notifyDelegate) 81 | } 82 | 83 | override func select(at index: Int, animated: Bool, notifyDelegate: Bool) { 84 | guard textCotainerStackView.arrangedSubviews.count > 0, 85 | rightButtonsContainerStackView.arrangedSubviews.count > 0 else { 86 | return 87 | } 88 | 89 | self.selectedIndex = index 90 | 91 | buttons.forEach { 92 | $0._isSelected = false 93 | } 94 | 95 | switch index { 96 | case 0: 97 | indicatorView.isHidden = false 98 | let button = textCotainerStackView.arrangedSubviews[0] as! BaseButton 99 | 100 | indicatorCenterXConstraint?.isActive = false 101 | indicatorCenterXConstraint = indicatorView.centerXAnchor.constraint(equalTo: button.centerXAnchor) 102 | indicatorCenterXConstraint.isActive = true 103 | 104 | button._isSelected = true 105 | case 1: 106 | let button = textCotainerStackView.arrangedSubviews[1] as! BaseButton 107 | 108 | indicatorView.isHidden = false 109 | 110 | indicatorCenterXConstraint?.isActive = false 111 | indicatorCenterXConstraint = indicatorView.centerXAnchor.constraint(equalTo: button.centerXAnchor) 112 | indicatorCenterXConstraint.isActive = true 113 | 114 | button._isSelected = true 115 | case 2: 116 | let button = rightButtonsContainerStackView.arrangedSubviews[0] as! BaseButton 117 | indicatorView.isHidden = false 118 | 119 | indicatorCenterXConstraint?.isActive = false 120 | indicatorCenterXConstraint = indicatorView.centerXAnchor.constraint(equalTo: button.centerXAnchor) 121 | indicatorCenterXConstraint.isActive = true 122 | 123 | button._isSelected = true 124 | case 3: 125 | let button = rightButtonsContainerStackView.arrangedSubviews[1] as! BaseButton 126 | indicatorView.isHidden = true 127 | 128 | indicatorCenterXConstraint?.isActive = false 129 | indicatorCenterXConstraint = indicatorView.centerXAnchor.constraint(equalTo: button.centerXAnchor) 130 | indicatorCenterXConstraint.isActive = true 131 | 132 | button._isSelected = true 133 | default: 134 | break 135 | } 136 | 137 | if animated { 138 | UIView.animate(withDuration: 0.3) { 139 | self.layoutIfNeeded() 140 | } 141 | } 142 | 143 | 144 | if notifyDelegate { 145 | delegate?.cardTabBar(self, didSelectItemAt: selectedIndex) 146 | } 147 | } 148 | 149 | override func set(items: [UITabBarItem]) { 150 | guard items.count > 4 else { 151 | fatalError("A Special CardTabBar can't be initialized with less than 4 items") 152 | } 153 | 154 | var mutableItems = items 155 | 156 | let firstItem = mutableItems.removeFirst() 157 | let firstTextButton = TextOnlyButton(title: firstItem.title) 158 | firstTextButton.tag = 0 159 | 160 | buttons.append(firstTextButton) 161 | self.textCotainerStackView.addingArrangedSubviews { 162 | firstTextButton 163 | } 164 | 165 | let anotherFirstItem = mutableItems.removeFirst() 166 | let secondTextButton = TextOnlyButton(title: anotherFirstItem.title) 167 | secondTextButton.tag = 1 168 | 169 | buttons.append(secondTextButton) 170 | self.textCotainerStackView.addingArrangedSubviews { 171 | secondTextButton 172 | } 173 | 174 | let anotherLastItem = mutableItems.remove(at: mutableItems.count - 2) 175 | let withImageButton = IconOnlyButton(image: anotherLastItem.image, selectedImage: anotherLastItem.selectedImage) 176 | withImageButton.tag = 2 177 | 178 | 179 | buttons.append(withImageButton) 180 | self.rightButtonsContainerStackView.addingArrangedSubviews { 181 | withImageButton 182 | } 183 | 184 | let lastItem = mutableItems.removeLast() 185 | let specialButton = SpecialButton(image: lastItem.image, selectedImage: lastItem.selectedImage) 186 | specialButton.tag = 3 187 | 188 | 189 | buttons.append(specialButton) 190 | self.rightButtonsContainerStackView.addingArrangedSubviews { 191 | specialButton 192 | } 193 | 194 | 195 | buttons.forEach { 196 | $0.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside) 197 | } 198 | } 199 | 200 | @objc func buttonTapped(sender: BaseButton){ 201 | self.select(at: sender.tag, animated: true, notifyDelegate: true) 202 | } 203 | } 204 | 205 | extension SpecialCardTabBar { 206 | 207 | class IndicatorView: UIView { 208 | 209 | override func didMoveToSuperview() { 210 | super.didMoveToSuperview() 211 | updateStyle() 212 | } 213 | 214 | func updateStyle() { 215 | layer.cornerRadius = 2 216 | backgroundColor = .black 217 | } 218 | 219 | override var intrinsicContentSize: CGSize { 220 | .init(width: 4, height: 4) 221 | } 222 | } 223 | 224 | class BaseButton: UIButton { 225 | var _isSelected: Bool = false { 226 | didSet { 227 | updateStyle() 228 | } 229 | } 230 | 231 | override func didMoveToSuperview() { 232 | super.didMoveToSuperview() 233 | updateStyle() 234 | } 235 | 236 | 237 | func updateStyle(){ 238 | 239 | } 240 | } 241 | 242 | class TextOnlyButton: BaseButton { 243 | 244 | let title: String? 245 | 246 | init(title: String?){ 247 | self.title = title 248 | super.init(frame: .zero) 249 | } 250 | 251 | required init?(coder: NSCoder) { 252 | fatalError("init(coder:) has not been implemented") 253 | } 254 | 255 | override func updateStyle() { 256 | setTitle(title, for: .normal) 257 | setTitleColor(_isSelected ? .black : .lightGray, for: .normal) 258 | titleLabel?.font = .systemFont(ofSize: 14, weight: .bold) 259 | } 260 | } 261 | 262 | class IconOnlyButton: BaseButton { 263 | 264 | let image: UIImage? 265 | let selectedImage: UIImage? 266 | 267 | init(image: UIImage?, selectedImage: UIImage?){ 268 | self.image = image 269 | self.selectedImage = selectedImage 270 | super.init(frame: .zero) 271 | } 272 | 273 | required init?(coder: NSCoder) { 274 | fatalError("init(coder:) has not been implemented") 275 | } 276 | 277 | override func updateStyle(){ 278 | setImage(_isSelected ? selectedImage : image, for: .normal) 279 | tintColor = _isSelected ? .black : .lightGray 280 | } 281 | } 282 | 283 | class SpecialButton: BaseButton { 284 | 285 | let image: UIImage? 286 | let selectedImage: UIImage? 287 | 288 | init(image: UIImage?, selectedImage: UIImage?){ 289 | self.image = image 290 | self.selectedImage = selectedImage 291 | super.init(frame: .zero) 292 | } 293 | 294 | required init?(coder: NSCoder) { 295 | fatalError("init(coder:) has not been implemented") 296 | } 297 | 298 | override func updateStyle(){ 299 | layer.cornerRadius = 6 300 | 301 | setImage(_isSelected ? selectedImage : image, for: .normal) 302 | tintColor = _isSelected ? .white : .black 303 | backgroundColor = _isSelected ? .black : UIColor(white: 0.93, alpha: 1) 304 | } 305 | 306 | override var intrinsicContentSize: CGSize { 307 | .init(width: 50, height: 50) 308 | } 309 | } 310 | } 311 | 312 | 313 | 314 | 315 | -------------------------------------------------------------------------------- /LightCardTabBar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E403A86F266B5BAA00EFFA93 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A86E266B5BAA00EFFA93 /* AppDelegate.swift */; }; 11 | E403A873266B5BAA00EFFA93 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A872266B5BAA00EFFA93 /* ViewController.swift */; }; 12 | E403A876266B5BAA00EFFA93 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E403A874266B5BAA00EFFA93 /* Main.storyboard */; }; 13 | E403A878266B5BAB00EFFA93 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E403A877266B5BAB00EFFA93 /* Assets.xcassets */; }; 14 | E403A87B266B5BAB00EFFA93 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E403A879266B5BAB00EFFA93 /* LaunchScreen.storyboard */; }; 15 | E403A886266B5BAB00EFFA93 /* LightCardTabBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A885266B5BAB00EFFA93 /* LightCardTabBarTests.swift */; }; 16 | E403A891266B5BAB00EFFA93 /* LightCardTabBarUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A890266B5BAB00EFFA93 /* LightCardTabBarUITests.swift */; }; 17 | E403A8A2266B5BC400EFFA93 /* SemiCardTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A89F266B5BC400EFFA93 /* SemiCardTabBar.swift */; }; 18 | E403A8A3266B5BC400EFFA93 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A8A0266B5BC400EFFA93 /* TabBarController.swift */; }; 19 | E403A8A4266B5BC400EFFA93 /* SpecialCardTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403A8A1266B5BC400EFFA93 /* SpecialCardTabBar.swift */; }; 20 | E4449646266B5CB600372DAF /* UIView+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4449644266B5CB600372DAF /* UIView+Additions.swift */; }; 21 | E444964A266B5E7F00372DAF /* LightTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4449649266B5E7F00372DAF /* LightTabBar.swift */; }; 22 | E444964C266BA51D00372DAF /* BaseCardTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E444964B266BA51D00372DAF /* BaseCardTabBar.swift */; }; 23 | E4449650266BADBD00372DAF /* CardTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E444964F266BADBD00372DAF /* CardTabBar.swift */; }; 24 | E4449653266BBE3000372DAF /* SpecialTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4449652266BBE3000372DAF /* SpecialTabBarController.swift */; }; 25 | E4449655266BBE3A00372DAF /* SemiCardTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4449654266BBE3A00372DAF /* SemiCardTabBarController.swift */; }; 26 | E4449657266BBE4200372DAF /* CardTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4449656266BBE4200372DAF /* CardTabBarController.swift */; }; 27 | E4449659266BBE4900372DAF /* LightTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4449658266BBE4900372DAF /* LightTabBarController.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | E403A882266B5BAB00EFFA93 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = E403A863266B5BAA00EFFA93 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = E403A86A266B5BAA00EFFA93; 36 | remoteInfo = LightCardTabBar; 37 | }; 38 | E403A88D266B5BAB00EFFA93 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = E403A863266B5BAA00EFFA93 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = E403A86A266B5BAA00EFFA93; 43 | remoteInfo = LightCardTabBar; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | E403A86B266B5BAA00EFFA93 /* LightCardTabBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LightCardTabBar.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | E403A86E266B5BAA00EFFA93 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50 | E403A872266B5BAA00EFFA93 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 51 | E403A875266B5BAA00EFFA93 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 52 | E403A877266B5BAB00EFFA93 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 53 | E403A87A266B5BAB00EFFA93 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 54 | E403A87C266B5BAB00EFFA93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | E403A881266B5BAB00EFFA93 /* LightCardTabBarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LightCardTabBarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | E403A885266B5BAB00EFFA93 /* LightCardTabBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightCardTabBarTests.swift; sourceTree = ""; }; 57 | E403A887266B5BAB00EFFA93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | E403A88C266B5BAB00EFFA93 /* LightCardTabBarUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LightCardTabBarUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | E403A890266B5BAB00EFFA93 /* LightCardTabBarUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightCardTabBarUITests.swift; sourceTree = ""; }; 60 | E403A892266B5BAB00EFFA93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | E403A89F266B5BC400EFFA93 /* SemiCardTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemiCardTabBar.swift; sourceTree = ""; }; 62 | E403A8A0266B5BC400EFFA93 /* TabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 63 | E403A8A1266B5BC400EFFA93 /* SpecialCardTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpecialCardTabBar.swift; sourceTree = ""; }; 64 | E4449644266B5CB600372DAF /* UIView+Additions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Additions.swift"; sourceTree = ""; }; 65 | E4449649266B5E7F00372DAF /* LightTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightTabBar.swift; sourceTree = ""; }; 66 | E444964B266BA51D00372DAF /* BaseCardTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCardTabBar.swift; sourceTree = ""; }; 67 | E444964F266BADBD00372DAF /* CardTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTabBar.swift; sourceTree = ""; }; 68 | E4449652266BBE3000372DAF /* SpecialTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialTabBarController.swift; sourceTree = ""; }; 69 | E4449654266BBE3A00372DAF /* SemiCardTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemiCardTabBarController.swift; sourceTree = ""; }; 70 | E4449656266BBE4200372DAF /* CardTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTabBarController.swift; sourceTree = ""; }; 71 | E4449658266BBE4900372DAF /* LightTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightTabBarController.swift; sourceTree = ""; }; 72 | E4A5BE0B2681B6F800865480 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | E403A868266B5BAA00EFFA93 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | E403A87E266B5BAB00EFFA93 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | E403A889266B5BAB00EFFA93 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | E403A862266B5BAA00EFFA93 = { 101 | isa = PBXGroup; 102 | children = ( 103 | E4A5BE0B2681B6F800865480 /* README.md */, 104 | E403A86D266B5BAA00EFFA93 /* LightCardTabBar */, 105 | E403A884266B5BAB00EFFA93 /* LightCardTabBarTests */, 106 | E403A88F266B5BAB00EFFA93 /* LightCardTabBarUITests */, 107 | E403A86C266B5BAA00EFFA93 /* Products */, 108 | ); 109 | sourceTree = ""; 110 | }; 111 | E403A86C266B5BAA00EFFA93 /* Products */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | E403A86B266B5BAA00EFFA93 /* LightCardTabBar.app */, 115 | E403A881266B5BAB00EFFA93 /* LightCardTabBarTests.xctest */, 116 | E403A88C266B5BAB00EFFA93 /* LightCardTabBarUITests.xctest */, 117 | ); 118 | name = Products; 119 | sourceTree = ""; 120 | }; 121 | E403A86D266B5BAA00EFFA93 /* LightCardTabBar */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | E403A86E266B5BAA00EFFA93 /* AppDelegate.swift */, 125 | E403A872266B5BAA00EFFA93 /* ViewController.swift */, 126 | E444965A266BBE7C00372DAF /* Extensions */, 127 | E4449651266BBE2300372DAF /* Controllers */, 128 | E403A89E266B5BC400EFFA93 /* Tab Bar */, 129 | E444965B266BBF4700372DAF /* Resources */, 130 | ); 131 | path = LightCardTabBar; 132 | sourceTree = ""; 133 | }; 134 | E403A884266B5BAB00EFFA93 /* LightCardTabBarTests */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | E403A885266B5BAB00EFFA93 /* LightCardTabBarTests.swift */, 138 | E403A887266B5BAB00EFFA93 /* Info.plist */, 139 | ); 140 | path = LightCardTabBarTests; 141 | sourceTree = ""; 142 | }; 143 | E403A88F266B5BAB00EFFA93 /* LightCardTabBarUITests */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | E403A890266B5BAB00EFFA93 /* LightCardTabBarUITests.swift */, 147 | E403A892266B5BAB00EFFA93 /* Info.plist */, 148 | ); 149 | path = LightCardTabBarUITests; 150 | sourceTree = ""; 151 | }; 152 | E403A89E266B5BC400EFFA93 /* Tab Bar */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | E444964B266BA51D00372DAF /* BaseCardTabBar.swift */, 156 | E403A8A1266B5BC400EFFA93 /* SpecialCardTabBar.swift */, 157 | E403A89F266B5BC400EFFA93 /* SemiCardTabBar.swift */, 158 | E444964F266BADBD00372DAF /* CardTabBar.swift */, 159 | E4449649266B5E7F00372DAF /* LightTabBar.swift */, 160 | ); 161 | path = "Tab Bar"; 162 | sourceTree = ""; 163 | }; 164 | E4449651266BBE2300372DAF /* Controllers */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | E403A8A0266B5BC400EFFA93 /* TabBarController.swift */, 168 | E4449652266BBE3000372DAF /* SpecialTabBarController.swift */, 169 | E4449654266BBE3A00372DAF /* SemiCardTabBarController.swift */, 170 | E4449656266BBE4200372DAF /* CardTabBarController.swift */, 171 | E4449658266BBE4900372DAF /* LightTabBarController.swift */, 172 | ); 173 | path = Controllers; 174 | sourceTree = ""; 175 | }; 176 | E444965A266BBE7C00372DAF /* Extensions */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | E4449644266B5CB600372DAF /* UIView+Additions.swift */, 180 | ); 181 | path = Extensions; 182 | sourceTree = ""; 183 | }; 184 | E444965B266BBF4700372DAF /* Resources */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | E403A874266B5BAA00EFFA93 /* Main.storyboard */, 188 | E403A877266B5BAB00EFFA93 /* Assets.xcassets */, 189 | E403A879266B5BAB00EFFA93 /* LaunchScreen.storyboard */, 190 | E403A87C266B5BAB00EFFA93 /* Info.plist */, 191 | ); 192 | path = Resources; 193 | sourceTree = ""; 194 | }; 195 | /* End PBXGroup section */ 196 | 197 | /* Begin PBXNativeTarget section */ 198 | E403A86A266B5BAA00EFFA93 /* LightCardTabBar */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = E403A895266B5BAB00EFFA93 /* Build configuration list for PBXNativeTarget "LightCardTabBar" */; 201 | buildPhases = ( 202 | E403A867266B5BAA00EFFA93 /* Sources */, 203 | E403A868266B5BAA00EFFA93 /* Frameworks */, 204 | E403A869266B5BAA00EFFA93 /* Resources */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | ); 210 | name = LightCardTabBar; 211 | packageProductDependencies = ( 212 | ); 213 | productName = LightCardTabBar; 214 | productReference = E403A86B266B5BAA00EFFA93 /* LightCardTabBar.app */; 215 | productType = "com.apple.product-type.application"; 216 | }; 217 | E403A880266B5BAB00EFFA93 /* LightCardTabBarTests */ = { 218 | isa = PBXNativeTarget; 219 | buildConfigurationList = E403A898266B5BAB00EFFA93 /* Build configuration list for PBXNativeTarget "LightCardTabBarTests" */; 220 | buildPhases = ( 221 | E403A87D266B5BAB00EFFA93 /* Sources */, 222 | E403A87E266B5BAB00EFFA93 /* Frameworks */, 223 | E403A87F266B5BAB00EFFA93 /* Resources */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | E403A883266B5BAB00EFFA93 /* PBXTargetDependency */, 229 | ); 230 | name = LightCardTabBarTests; 231 | productName = LightCardTabBarTests; 232 | productReference = E403A881266B5BAB00EFFA93 /* LightCardTabBarTests.xctest */; 233 | productType = "com.apple.product-type.bundle.unit-test"; 234 | }; 235 | E403A88B266B5BAB00EFFA93 /* LightCardTabBarUITests */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = E403A89B266B5BAB00EFFA93 /* Build configuration list for PBXNativeTarget "LightCardTabBarUITests" */; 238 | buildPhases = ( 239 | E403A888266B5BAB00EFFA93 /* Sources */, 240 | E403A889266B5BAB00EFFA93 /* Frameworks */, 241 | E403A88A266B5BAB00EFFA93 /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | E403A88E266B5BAB00EFFA93 /* PBXTargetDependency */, 247 | ); 248 | name = LightCardTabBarUITests; 249 | productName = LightCardTabBarUITests; 250 | productReference = E403A88C266B5BAB00EFFA93 /* LightCardTabBarUITests.xctest */; 251 | productType = "com.apple.product-type.bundle.ui-testing"; 252 | }; 253 | /* End PBXNativeTarget section */ 254 | 255 | /* Begin PBXProject section */ 256 | E403A863266B5BAA00EFFA93 /* Project object */ = { 257 | isa = PBXProject; 258 | attributes = { 259 | LastSwiftUpdateCheck = 1250; 260 | LastUpgradeCheck = 1250; 261 | TargetAttributes = { 262 | E403A86A266B5BAA00EFFA93 = { 263 | CreatedOnToolsVersion = 12.5; 264 | }; 265 | E403A880266B5BAB00EFFA93 = { 266 | CreatedOnToolsVersion = 12.5; 267 | TestTargetID = E403A86A266B5BAA00EFFA93; 268 | }; 269 | E403A88B266B5BAB00EFFA93 = { 270 | CreatedOnToolsVersion = 12.5; 271 | TestTargetID = E403A86A266B5BAA00EFFA93; 272 | }; 273 | }; 274 | }; 275 | buildConfigurationList = E403A866266B5BAA00EFFA93 /* Build configuration list for PBXProject "LightCardTabBar" */; 276 | compatibilityVersion = "Xcode 9.3"; 277 | developmentRegion = en; 278 | hasScannedForEncodings = 0; 279 | knownRegions = ( 280 | en, 281 | Base, 282 | ); 283 | mainGroup = E403A862266B5BAA00EFFA93; 284 | packageReferences = ( 285 | ); 286 | productRefGroup = E403A86C266B5BAA00EFFA93 /* Products */; 287 | projectDirPath = ""; 288 | projectRoot = ""; 289 | targets = ( 290 | E403A86A266B5BAA00EFFA93 /* LightCardTabBar */, 291 | E403A880266B5BAB00EFFA93 /* LightCardTabBarTests */, 292 | E403A88B266B5BAB00EFFA93 /* LightCardTabBarUITests */, 293 | ); 294 | }; 295 | /* End PBXProject section */ 296 | 297 | /* Begin PBXResourcesBuildPhase section */ 298 | E403A869266B5BAA00EFFA93 /* Resources */ = { 299 | isa = PBXResourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | E403A87B266B5BAB00EFFA93 /* LaunchScreen.storyboard in Resources */, 303 | E403A878266B5BAB00EFFA93 /* Assets.xcassets in Resources */, 304 | E403A876266B5BAA00EFFA93 /* Main.storyboard in Resources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | E403A87F266B5BAB00EFFA93 /* Resources */ = { 309 | isa = PBXResourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | E403A88A266B5BAB00EFFA93 /* Resources */ = { 316 | isa = PBXResourcesBuildPhase; 317 | buildActionMask = 2147483647; 318 | files = ( 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXResourcesBuildPhase section */ 323 | 324 | /* Begin PBXSourcesBuildPhase section */ 325 | E403A867266B5BAA00EFFA93 /* Sources */ = { 326 | isa = PBXSourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | E444964C266BA51D00372DAF /* BaseCardTabBar.swift in Sources */, 330 | E403A873266B5BAA00EFFA93 /* ViewController.swift in Sources */, 331 | E403A8A2266B5BC400EFFA93 /* SemiCardTabBar.swift in Sources */, 332 | E4449655266BBE3A00372DAF /* SemiCardTabBarController.swift in Sources */, 333 | E4449653266BBE3000372DAF /* SpecialTabBarController.swift in Sources */, 334 | E4449646266B5CB600372DAF /* UIView+Additions.swift in Sources */, 335 | E4449659266BBE4900372DAF /* LightTabBarController.swift in Sources */, 336 | E444964A266B5E7F00372DAF /* LightTabBar.swift in Sources */, 337 | E403A86F266B5BAA00EFFA93 /* AppDelegate.swift in Sources */, 338 | E403A8A3266B5BC400EFFA93 /* TabBarController.swift in Sources */, 339 | E403A8A4266B5BC400EFFA93 /* SpecialCardTabBar.swift in Sources */, 340 | E4449650266BADBD00372DAF /* CardTabBar.swift in Sources */, 341 | E4449657266BBE4200372DAF /* CardTabBarController.swift in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | E403A87D266B5BAB00EFFA93 /* Sources */ = { 346 | isa = PBXSourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | E403A886266B5BAB00EFFA93 /* LightCardTabBarTests.swift in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | E403A888266B5BAB00EFFA93 /* Sources */ = { 354 | isa = PBXSourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | E403A891266B5BAB00EFFA93 /* LightCardTabBarUITests.swift in Sources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | /* End PBXSourcesBuildPhase section */ 362 | 363 | /* Begin PBXTargetDependency section */ 364 | E403A883266B5BAB00EFFA93 /* PBXTargetDependency */ = { 365 | isa = PBXTargetDependency; 366 | target = E403A86A266B5BAA00EFFA93 /* LightCardTabBar */; 367 | targetProxy = E403A882266B5BAB00EFFA93 /* PBXContainerItemProxy */; 368 | }; 369 | E403A88E266B5BAB00EFFA93 /* PBXTargetDependency */ = { 370 | isa = PBXTargetDependency; 371 | target = E403A86A266B5BAA00EFFA93 /* LightCardTabBar */; 372 | targetProxy = E403A88D266B5BAB00EFFA93 /* PBXContainerItemProxy */; 373 | }; 374 | /* End PBXTargetDependency section */ 375 | 376 | /* Begin PBXVariantGroup section */ 377 | E403A874266B5BAA00EFFA93 /* Main.storyboard */ = { 378 | isa = PBXVariantGroup; 379 | children = ( 380 | E403A875266B5BAA00EFFA93 /* Base */, 381 | ); 382 | name = Main.storyboard; 383 | sourceTree = ""; 384 | }; 385 | E403A879266B5BAB00EFFA93 /* LaunchScreen.storyboard */ = { 386 | isa = PBXVariantGroup; 387 | children = ( 388 | E403A87A266B5BAB00EFFA93 /* Base */, 389 | ); 390 | name = LaunchScreen.storyboard; 391 | sourceTree = ""; 392 | }; 393 | /* End PBXVariantGroup section */ 394 | 395 | /* Begin XCBuildConfiguration section */ 396 | E403A893266B5BAB00EFFA93 /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_ANALYZER_NONNULL = YES; 401 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 403 | CLANG_CXX_LIBRARY = "libc++"; 404 | CLANG_ENABLE_MODULES = YES; 405 | CLANG_ENABLE_OBJC_ARC = YES; 406 | CLANG_ENABLE_OBJC_WEAK = YES; 407 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 408 | CLANG_WARN_BOOL_CONVERSION = YES; 409 | CLANG_WARN_COMMA = YES; 410 | CLANG_WARN_CONSTANT_CONVERSION = YES; 411 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 412 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 413 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 414 | CLANG_WARN_EMPTY_BODY = YES; 415 | CLANG_WARN_ENUM_CONVERSION = YES; 416 | CLANG_WARN_INFINITE_RECURSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 420 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 421 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 422 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 423 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 424 | CLANG_WARN_STRICT_PROTOTYPES = YES; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 427 | CLANG_WARN_UNREACHABLE_CODE = YES; 428 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = dwarf; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | ENABLE_TESTABILITY = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu11; 434 | GCC_DYNAMIC_NO_PIC = NO; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_OPTIMIZATION_LEVEL = 0; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 448 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 449 | MTL_FAST_MATH = YES; 450 | ONLY_ACTIVE_ARCH = YES; 451 | SDKROOT = iphoneos; 452 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 453 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 454 | }; 455 | name = Debug; 456 | }; 457 | E403A894266B5BAB00EFFA93 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ALWAYS_SEARCH_USER_PATHS = NO; 461 | CLANG_ANALYZER_NONNULL = YES; 462 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 463 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 464 | CLANG_CXX_LIBRARY = "libc++"; 465 | CLANG_ENABLE_MODULES = YES; 466 | CLANG_ENABLE_OBJC_ARC = YES; 467 | CLANG_ENABLE_OBJC_WEAK = YES; 468 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 469 | CLANG_WARN_BOOL_CONVERSION = YES; 470 | CLANG_WARN_COMMA = YES; 471 | CLANG_WARN_CONSTANT_CONVERSION = YES; 472 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 473 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 474 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 475 | CLANG_WARN_EMPTY_BODY = YES; 476 | CLANG_WARN_ENUM_CONVERSION = YES; 477 | CLANG_WARN_INFINITE_RECURSION = YES; 478 | CLANG_WARN_INT_CONVERSION = YES; 479 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 480 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 481 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 482 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 483 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 484 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 485 | CLANG_WARN_STRICT_PROTOTYPES = YES; 486 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 487 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 488 | CLANG_WARN_UNREACHABLE_CODE = YES; 489 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 490 | COPY_PHASE_STRIP = NO; 491 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 492 | ENABLE_NS_ASSERTIONS = NO; 493 | ENABLE_STRICT_OBJC_MSGSEND = YES; 494 | GCC_C_LANGUAGE_STANDARD = gnu11; 495 | GCC_NO_COMMON_BLOCKS = YES; 496 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 497 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 498 | GCC_WARN_UNDECLARED_SELECTOR = YES; 499 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 500 | GCC_WARN_UNUSED_FUNCTION = YES; 501 | GCC_WARN_UNUSED_VARIABLE = YES; 502 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 503 | MTL_ENABLE_DEBUG_INFO = NO; 504 | MTL_FAST_MATH = YES; 505 | SDKROOT = iphoneos; 506 | SWIFT_COMPILATION_MODE = wholemodule; 507 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 508 | VALIDATE_PRODUCT = YES; 509 | }; 510 | name = Release; 511 | }; 512 | E403A896266B5BAB00EFFA93 /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 517 | CODE_SIGN_STYLE = Automatic; 518 | DEVELOPMENT_TEAM = 6VU6999229; 519 | INFOPLIST_FILE = LightCardTabBar/Resources/Info.plist; 520 | LD_RUNPATH_SEARCH_PATHS = ( 521 | "$(inherited)", 522 | "@executable_path/Frameworks", 523 | ); 524 | PRODUCT_BUNDLE_IDENTIFIER = me.sketch.LightCardTabBar; 525 | PRODUCT_NAME = "$(TARGET_NAME)"; 526 | SWIFT_VERSION = 5.0; 527 | TARGETED_DEVICE_FAMILY = "1,2"; 528 | }; 529 | name = Debug; 530 | }; 531 | E403A897266B5BAB00EFFA93 /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | buildSettings = { 534 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 535 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 536 | CODE_SIGN_STYLE = Automatic; 537 | DEVELOPMENT_TEAM = 6VU6999229; 538 | INFOPLIST_FILE = LightCardTabBar/Resources/Info.plist; 539 | LD_RUNPATH_SEARCH_PATHS = ( 540 | "$(inherited)", 541 | "@executable_path/Frameworks", 542 | ); 543 | PRODUCT_BUNDLE_IDENTIFIER = me.sketch.LightCardTabBar; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | SWIFT_VERSION = 5.0; 546 | TARGETED_DEVICE_FAMILY = "1,2"; 547 | }; 548 | name = Release; 549 | }; 550 | E403A899266B5BAB00EFFA93 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 554 | BUNDLE_LOADER = "$(TEST_HOST)"; 555 | CODE_SIGN_STYLE = Automatic; 556 | DEVELOPMENT_TEAM = 6VU6999229; 557 | INFOPLIST_FILE = LightCardTabBarTests/Info.plist; 558 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 559 | LD_RUNPATH_SEARCH_PATHS = ( 560 | "$(inherited)", 561 | "@executable_path/Frameworks", 562 | "@loader_path/Frameworks", 563 | ); 564 | PRODUCT_BUNDLE_IDENTIFIER = me.sketch.LightCardTabBarTests; 565 | PRODUCT_NAME = "$(TARGET_NAME)"; 566 | SWIFT_VERSION = 5.0; 567 | TARGETED_DEVICE_FAMILY = "1,2"; 568 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LightCardTabBar.app/LightCardTabBar"; 569 | }; 570 | name = Debug; 571 | }; 572 | E403A89A266B5BAB00EFFA93 /* Release */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 576 | BUNDLE_LOADER = "$(TEST_HOST)"; 577 | CODE_SIGN_STYLE = Automatic; 578 | DEVELOPMENT_TEAM = 6VU6999229; 579 | INFOPLIST_FILE = LightCardTabBarTests/Info.plist; 580 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 581 | LD_RUNPATH_SEARCH_PATHS = ( 582 | "$(inherited)", 583 | "@executable_path/Frameworks", 584 | "@loader_path/Frameworks", 585 | ); 586 | PRODUCT_BUNDLE_IDENTIFIER = me.sketch.LightCardTabBarTests; 587 | PRODUCT_NAME = "$(TARGET_NAME)"; 588 | SWIFT_VERSION = 5.0; 589 | TARGETED_DEVICE_FAMILY = "1,2"; 590 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LightCardTabBar.app/LightCardTabBar"; 591 | }; 592 | name = Release; 593 | }; 594 | E403A89C266B5BAB00EFFA93 /* Debug */ = { 595 | isa = XCBuildConfiguration; 596 | buildSettings = { 597 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 598 | CODE_SIGN_STYLE = Automatic; 599 | DEVELOPMENT_TEAM = 6VU6999229; 600 | INFOPLIST_FILE = LightCardTabBarUITests/Info.plist; 601 | LD_RUNPATH_SEARCH_PATHS = ( 602 | "$(inherited)", 603 | "@executable_path/Frameworks", 604 | "@loader_path/Frameworks", 605 | ); 606 | PRODUCT_BUNDLE_IDENTIFIER = me.sketch.LightCardTabBarUITests; 607 | PRODUCT_NAME = "$(TARGET_NAME)"; 608 | SWIFT_VERSION = 5.0; 609 | TARGETED_DEVICE_FAMILY = "1,2"; 610 | TEST_TARGET_NAME = LightCardTabBar; 611 | }; 612 | name = Debug; 613 | }; 614 | E403A89D266B5BAB00EFFA93 /* Release */ = { 615 | isa = XCBuildConfiguration; 616 | buildSettings = { 617 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 618 | CODE_SIGN_STYLE = Automatic; 619 | DEVELOPMENT_TEAM = 6VU6999229; 620 | INFOPLIST_FILE = LightCardTabBarUITests/Info.plist; 621 | LD_RUNPATH_SEARCH_PATHS = ( 622 | "$(inherited)", 623 | "@executable_path/Frameworks", 624 | "@loader_path/Frameworks", 625 | ); 626 | PRODUCT_BUNDLE_IDENTIFIER = me.sketch.LightCardTabBarUITests; 627 | PRODUCT_NAME = "$(TARGET_NAME)"; 628 | SWIFT_VERSION = 5.0; 629 | TARGETED_DEVICE_FAMILY = "1,2"; 630 | TEST_TARGET_NAME = LightCardTabBar; 631 | }; 632 | name = Release; 633 | }; 634 | /* End XCBuildConfiguration section */ 635 | 636 | /* Begin XCConfigurationList section */ 637 | E403A866266B5BAA00EFFA93 /* Build configuration list for PBXProject "LightCardTabBar" */ = { 638 | isa = XCConfigurationList; 639 | buildConfigurations = ( 640 | E403A893266B5BAB00EFFA93 /* Debug */, 641 | E403A894266B5BAB00EFFA93 /* Release */, 642 | ); 643 | defaultConfigurationIsVisible = 0; 644 | defaultConfigurationName = Release; 645 | }; 646 | E403A895266B5BAB00EFFA93 /* Build configuration list for PBXNativeTarget "LightCardTabBar" */ = { 647 | isa = XCConfigurationList; 648 | buildConfigurations = ( 649 | E403A896266B5BAB00EFFA93 /* Debug */, 650 | E403A897266B5BAB00EFFA93 /* Release */, 651 | ); 652 | defaultConfigurationIsVisible = 0; 653 | defaultConfigurationName = Release; 654 | }; 655 | E403A898266B5BAB00EFFA93 /* Build configuration list for PBXNativeTarget "LightCardTabBarTests" */ = { 656 | isa = XCConfigurationList; 657 | buildConfigurations = ( 658 | E403A899266B5BAB00EFFA93 /* Debug */, 659 | E403A89A266B5BAB00EFFA93 /* Release */, 660 | ); 661 | defaultConfigurationIsVisible = 0; 662 | defaultConfigurationName = Release; 663 | }; 664 | E403A89B266B5BAB00EFFA93 /* Build configuration list for PBXNativeTarget "LightCardTabBarUITests" */ = { 665 | isa = XCConfigurationList; 666 | buildConfigurations = ( 667 | E403A89C266B5BAB00EFFA93 /* Debug */, 668 | E403A89D266B5BAB00EFFA93 /* Release */, 669 | ); 670 | defaultConfigurationIsVisible = 0; 671 | defaultConfigurationName = Release; 672 | }; 673 | /* End XCConfigurationList section */ 674 | }; 675 | rootObject = E403A863266B5BAA00EFFA93 /* Project object */; 676 | } 677 | --------------------------------------------------------------------------------