├── .github ├── FUNDING.yml └── workflows │ └── swift.yml ├── .gitignore ├── .swiftlint.yml ├── LICENSE.txt ├── Package.swift ├── README.md └── Sources └── Tabify ├── Enumerations └── Visibility.swift ├── Extensions ├── TabifyExtension.swift └── ViewExtension.swift ├── Models └── TabifyItem.swift ├── Modifiers └── ItemViewModifier.swift ├── Styles ├── BarStyle │ ├── BarStyle.swift │ ├── DefaultBarStyle.swift │ └── GenericBarStyle.swift └── ItemStyle │ ├── DefaultItemStyle.swift │ ├── GenericItemStyle.swift │ └── ItemStyle.swift ├── Tabify.swift └── Util ├── CustomPreferenceKey.swift └── SelectionManager.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bgeisb 4 | custom: "https://www.buymeacoffee.com/bgeisb" 5 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Swift project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift 3 | 4 | name: Swift 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | SwiftLint: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - name: GitHub Action for SwiftLint 22 | uses: norio-nomura/action-swiftlint@3.2.1 23 | 24 | Build: 25 | 26 | runs-on: macOS-12 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | 31 | - name: Build iOS 32 | run: xcodebuild -scheme Tabify -destination 'generic/platform=iOS' 33 | 34 | - name: Build macOS 35 | run: xcodebuild -scheme Tabify -destination 'generic/platform=macOS' 36 | 37 | - name: Build mac Catalyst 38 | run: xcodebuild -scheme Tabify -destination 'generic/platform=macOS,variant=Mac Catalyst' 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .swiftpm 3 | xcuserdata/ 4 | Package.resolved 5 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: # rule identifiers to exclude from running 2 | - variable_name 3 | - nesting 4 | - function_parameter_count 5 | - trailing_whitespace 6 | opt_in_rules: # some rules are only opt-in 7 | - control_statement 8 | - empty_count 9 | - trailing_newline 10 | - colon 11 | - comma 12 | excluded: # paths to ignore during linting. Takes precedence over `included`. 13 | - Pods 14 | 15 | force_cast: warning # implicitly. Give warning only for force casting 16 | 17 | force_try: 18 | severity: warning # explicitly. Give warning only for force try 19 | 20 | type_body_length: 21 | - 300 # warning 22 | - 400 # error 23 | 24 | file_length: 25 | warning: 500 26 | error: 800 27 | 28 | large_tuple: # warn user when using 3 values in tuple, give error if there are 4 29 | - 3 30 | - 4 31 | 32 | # naming rules can set warnings/errors for min_length and max_length 33 | # additionally they can set excluded names 34 | type_name: 35 | min_length: 4 # only warning 36 | max_length: # warning and error 37 | warning: 30 38 | error: 35 39 | excluded: iPhone # excluded via string 40 | reporter: "xcode" 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Benedikt Geisberger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.7 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Tabify", 8 | platforms: [ 9 | .iOS(.v13), 10 | .macOS(.v11) 11 | ], 12 | products: [ 13 | // Products define the executables and libraries a package produces, and make them visible to other packages. 14 | .library( 15 | name: "Tabify", 16 | targets: ["Tabify"]), 17 | ], 18 | targets: [ 19 | .target( 20 | name: "Tabify", 21 | dependencies: [] 22 | ), 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | [![Contributors][contributors-shield]][contributors-url] 12 | [![Forks][forks-shield]][forks-url] 13 | [![Stargazers][stars-shield]][stars-url] 14 | [![Issues][issues-shield]][issues-url] 15 | [![MIT License][license-shield]][license-url] 16 | [![LinkedIn][linkedin-shield]][linkedin-url] 17 | 18 | 19 | 20 |  21 | 22 | 23 | 24 | ## Table of contents 25 | 26 | * [Description](#description) 27 | * [Requirements](#requirements) 28 | * [Installation](#installation) 29 | * [Swift Package Manager](#swift-package-manager) 30 | * [CocoaPods](#cocoapods) 31 | * [Usage](#usage) 32 | * [1. Create TabifyItems Enum](#1-create-tabifyitems-enum) 33 | * [2. Add Tabify View](#2-add-tabify-view) 34 | * [Contributing](#contributing) 35 | * [License](#license) 36 | 37 | 38 | 39 | ## Description 40 | `Tabify` is a SwiftUI view that elegantly utilizes interactive user interface elements to dynamically switch between multiple child views. 41 | It is highly customizable and, due to its similarity to the native `TabView` SwiftUI component, very user-friendly and easy to use. 42 | 43 | 44 | 45 | ## Requirements 46 | 47 | - SwiftUI 48 | - iOS 13.0 or above 49 | 50 |
51 | 52 | 53 | 54 | ## Installation 55 | 56 | ### Swift Package Manager 57 | 58 | The preferred way of installing `Tabify` is via the [Swift Package Manager](https://swift.org/package-manager/). 59 | 60 | 1. In Xcode, open your project and navigate to **File** → **Add Packages** 61 | 2. Paste the repository URL (`https://github.com/bgeisb/Tabify.git`) and click **Next**. 62 | 3. For **Rules**, select **Up to Next Major Version**. 63 | 4. Click **Add Package**. 64 | 65 | ### CocoaPods 66 | 67 | ⏳ Coming soon... 68 | 69 | 70 | 71 | 72 | ## Usage 73 | 74 | ### 1. Create TabifyItems Enum 75 | 76 | > Make sure your enum implements the `TabifyItem` protocol. 77 | 78 | ```swift 79 | enum TabifyItems: Int, TabifyItem { 80 | case home = 0 81 | case search 82 | case profile 83 | 84 | var icon: String { 85 | switch self { 86 | case .home: return "house" 87 | case .search: return "magnifyingglass" 88 | case .profile: return "person" 89 | } 90 | } 91 | 92 | var title: String { 93 | switch self { 94 | case .home: return "Home" 95 | case .search: return "Search" 96 | case .profile: return "Profile" 97 | } 98 | } 99 | } 100 | ``` 101 | 102 | ### 2. Add Tabify View 103 | 104 | ```swift 105 | Tabify(selectedItem: $tabBarSelection) { 106 | Text("Home") 107 | .tabItem(for: TabifyItems.first) 108 | 109 | Text("Search") 110 | .tabItem(for: TabifyItems.second) 111 | 112 | Text("Profile") 113 | .tabItem(for: TabifyItems.third) 114 | } 115 | /* This following modifiers are optional. You can use them to inject your custom bar and item styling. */ 116 | .barStyle(style: CustomTabifyBarStyle()) 117 | .itemStyle(style: CustomTabifyItemStyle()) 118 | ``` 119 | 120 | 121 | 122 | 123 | ## Contributing 124 | 125 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 126 | 127 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply [open an issue](https://github.com/bgeisb/Tabify/issues/new) with the tag "enhancement". 128 | Don't forget to give the project a star! Thanks again! 129 | 130 | 1. Fork the Project 131 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 132 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 133 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 134 | 5. Open a Pull Request 135 | 136 | 137 | 138 | 139 | 140 | ## License 141 | 142 | Distributed under the MIT License. See `LICENSE.txt` for more information. 143 | 144 | 145 | 146 | 147 | 148 | 149 | [contributors-shield]: https://img.shields.io/github/contributors/bgeisb/Tabify.svg?style=for-the-badge 150 | [contributors-url]: https://github.com/bgeisb/Tabify/graphs/contributors 151 | [forks-shield]: https://img.shields.io/github/forks/bgeisb/Tabify.svg?style=for-the-badge 152 | [forks-url]: https://github.com/bgeisb/Tabify/network/members 153 | [stars-shield]: https://img.shields.io/github/stars/bgeisb/Tabify.svg?style=for-the-badge 154 | [stars-url]: https://github.com/bgeisb/Tabify/stargazers 155 | [issues-shield]: https://img.shields.io/github/issues/bgeisb/Tabify.svg?style=for-the-badge 156 | [issues-url]: https://github.com/bgeisb/Tabify/issues 157 | [license-shield]: https://img.shields.io/github/license/bgeisb/Tabify.svg?style=for-the-badge 158 | [license-url]: https://github.com/bgeisb/Tabify/blob/master/LICENSE.txt 159 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 160 | [linkedin-url]: https://linkedin.com/in/bgeisberger 161 | [product-screenshot]: images/screenshot.png 162 | -------------------------------------------------------------------------------- /Sources/Tabify/Enumerations/Visibility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Visibility.swift 3 | // 4 | // 5 | // Created by Benedikt Geisberger on 29.12.22. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Visibility: CaseIterable { 11 | case visible 12 | case invisible 13 | 14 | public mutating func toggle() { 15 | switch self { 16 | case .visible: 17 | self = .invisible 18 | case .invisible: 19 | self = .visible 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Tabify/Extensions/TabifyExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tabify.swift 3 | // 4 | // 5 | // Created by Benedikt Geisberger on 29.12.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Tabify { 11 | public func itemStyle