├── .gitignore ├── .travis.yml ├── Framework ├── Info.plist └── TiltedTabView.h ├── LICENSE ├── README.md ├── Resources ├── Screenshot.png └── Screenshot_iPad.png ├── Sample ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── snapshot.imageset │ │ ├── Contents.json │ │ └── snapshot.png │ └── snapshot_ipad.imageset │ │ ├── Contents.json │ │ └── snapshot_ipad.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist └── ViewController.swift ├── Sources ├── Layouts │ ├── TiltedTabCollectionViewLayout.swift │ ├── TiltedTabGridCollectionViewLayout.swift │ └── TiltedTabTiltedCollectionViewLayout.swift ├── TiltedTabViewController.swift └── Views │ └── TiltedTabViewCell.swift ├── TiltedTabView.podspec ├── TiltedTabView.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── TiltedTabView.xcscheme │ └── libTiltedTabView.xcscheme └── build.plist /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.xcworkspacedata 3 | *.xcuserdatad 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9.2 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | - PROJECT=TiltedTabView.xcodeproj 8 | - IOS_FRAMEWORK_SCHEME="TiltedTabView" 9 | - IOS_SIM_SDK=iphonesimulator11.2 10 | - IOS_DEV_SDK=iphoneos11.2 11 | matrix: 12 | - SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SIM_SDK" 13 | - SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_DEV_SDK" 14 | script: 15 | - set -o pipefail 16 | - xcodebuild -version 17 | - xcodebuild -showsdks 18 | - xcodebuild -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -configuration Release ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES build; 19 | 20 | branches: 21 | only: 22 | - master 23 | -------------------------------------------------------------------------------- /Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Framework/TiltedTabView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TiltedTabView.h 3 | // TiltedTabView 4 | // 5 | // Copyright © 2017 Ian McDowell. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for TiltedTabView. 11 | FOUNDATION_EXPORT double TiltedTabViewVersionNumber; 12 | 13 | //! Project version string for TiltedTabView. 14 | FOUNDATION_EXPORT const unsigned char TiltedTabViewVersionString[]; 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ian McDowell 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tilted Tab View 2 | 3 |

4 | TiltedTabView • 5 | TabView • 6 | InputAssistant • 7 | Git 8 |

9 | 10 | -------- 11 | 12 | This library aims to replicate the tab switcher in Safari on iOS. It handles both compact width (tilted) and regular width (grid) layouts. 13 | 14 | [![Build Status](http://img.shields.io/travis/IMcD23/TiltedTabView.svg)](https://travis-ci.org/IMcD23/TiltedTabView) 15 | [![Version](https://img.shields.io/github/release/IMcD23/TiltedTabView.svg)](https://github.com/IMcD23/TiltedTabView/releases/latest) 16 | ![Package Managers](https://img.shields.io/badge/supports-Carthage-orange.svg) 17 | [![Contact](https://img.shields.io/badge/contact-%40ian__mcdowell-3a8fc1.svg)](https://twitter.com/ian_mcdowell) 18 | 19 | 20 | 21 | # Requirements 22 | 23 | * Xcode 9 or later 24 | * iOS 10.0 or later 25 | 26 | # Usage 27 | 28 | The main class in this library is the TiltedTabViewController. It is a subclass of UICollectionViewController, that contains a custom collection view and layout. 29 | 30 | To get started, create a view controller that subclasses TiltedTabViewController 31 | 32 | The TiltedTabViewController has data source and delegate properties, similar to those of a UICollectionView. 33 | 34 | Set an object that conforms to the TiltedTabViewControllerDataSource and TiltedTabViewControllerDelegate protocols as the dataSource and delegate properties, respectively. 35 | 36 | Provide implementations for all required methods of each protocol, and you're off to the races. 37 | 38 | # Installation 39 | 40 | ## Carthage 41 | To install TiltedTabView using [Carthage](https://github.com/Carthage/Carthage), add the following line to your Cartfile: 42 | 43 | ``` 44 | github "IMcD23/TiltedTabView" "master" 45 | ``` 46 | 47 | ## Submodule 48 | To install TiltedTabView as a submodule into your git repository, run the following command: 49 | 50 | ``` 51 | git submodule add -b master https://github.com/IMcD23/TiltedTabView.git Path/To/TiltedTabView 52 | git submodule update --init --recursive 53 | ``` 54 | 55 | Then, add the `.xcodeproj` in the root of the repository into your Xcode project, and add it as a build dependency. 56 | 57 | ## ibuild 58 | A Swift static library of this project is also available for the ibuild build system. Learn more about ibuild [here](https://github.com/IMcD23/ibuild) 59 | 60 | # Author 61 | Created by [Ian McDowell](https://ianmcdowell.net) 62 | 63 | # License 64 | All code in this project is available under the license specified in the LICENSE file. However, since this project also bundles code from other projects, you are subject to those projects' licenses as well. 65 | -------------------------------------------------------------------------------- /Resources/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-mcdowell/TiltedTabView/30c11397991c5b7a9ab54eb5d372cbcb27e38126/Resources/Screenshot.png -------------------------------------------------------------------------------- /Resources/Screenshot_iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-mcdowell/TiltedTabView/30c11397991c5b7a9ab54eb5d372cbcb27e38126/Resources/Screenshot_iPad.png -------------------------------------------------------------------------------- /Sample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 10 | 11 | self.window = UIWindow(frame: UIScreen.main.bounds) 12 | self.window?.tintColor = .white 13 | self.window?.rootViewController = UINavigationController(rootViewController: ViewController()) 14 | self.window?.makeKeyAndVisible() 15 | 16 | return true 17 | } 18 | 19 | func applicationWillResignActive(_ application: UIApplication) { 20 | } 21 | 22 | func applicationDidEnterBackground(_ application: UIApplication) { 23 | } 24 | 25 | func applicationWillEnterForeground(_ application: UIApplication) { 26 | } 27 | 28 | func applicationDidBecomeActive(_ application: UIApplication) { 29 | } 30 | 31 | func applicationWillTerminate(_ application: UIApplication) { 32 | } 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/snapshot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "snapshot.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/snapshot.imageset/snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-mcdowell/TiltedTabView/30c11397991c5b7a9ab54eb5d372cbcb27e38126/Sample/Assets.xcassets/snapshot.imageset/snapshot.png -------------------------------------------------------------------------------- /Sample/Assets.xcassets/snapshot_ipad.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "snapshot_ipad.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sample/Assets.xcassets/snapshot_ipad.imageset/snapshot_ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-mcdowell/TiltedTabView/30c11397991c5b7a9ab54eb5d372cbcb27e38126/Sample/Assets.xcassets/snapshot_ipad.imageset/snapshot_ipad.png -------------------------------------------------------------------------------- /Sample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sample/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UIStatusBarStyle 30 | UIStatusBarStyleLightContent 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Sample/ViewController.swift: -------------------------------------------------------------------------------- 1 | import TiltedTabView 2 | 3 | class ViewController: TiltedTabViewController { 4 | 5 | var tabs: [String] 6 | 7 | override init() { 8 | self.tabs = ["Tilted", "Tab", "View"] 9 | super.init() 10 | 11 | self.delegate = self 12 | self.dataSource = self 13 | } 14 | 15 | required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | self.navigationController?.isNavigationBarHidden = true 21 | self.navigationController?.isToolbarHidden = false 22 | self.navigationController?.toolbar.barStyle = .blackTranslucent 23 | self.toolbarItems = [ 24 | UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), 25 | UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addNewTab)), 26 | UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 27 | ] 28 | } 29 | 30 | @objc private func addNewTab() { 31 | self.addTab(atIndex: tabs.count) 32 | } 33 | 34 | override var preferredStatusBarStyle: UIStatusBarStyle { 35 | return .lightContent 36 | } 37 | 38 | } 39 | 40 | extension ViewController: TiltedTabViewControllerDataSource { 41 | func numberOfTabsInTiltedTabViewController() -> Int { 42 | return tabs.count 43 | } 44 | 45 | func snapshotForTab(atIndex index: Int) -> UIImage? { 46 | if UIDevice.current.userInterfaceIdiom == .pad { 47 | return #imageLiteral(resourceName: "snapshot_ipad") 48 | } 49 | return #imageLiteral(resourceName: "snapshot") 50 | } 51 | 52 | func titleForTab(atIndex index: Int) -> String? { 53 | return tabs[index] 54 | } 55 | 56 | func indexForActiveTab() -> Int? { 57 | return nil 58 | } 59 | 60 | func tabAdded(atIndex index: Int) { 61 | tabs.append("Tab") 62 | } 63 | 64 | func tabRemoved(atIndex index: Int) { 65 | tabs.removeLast() 66 | } 67 | 68 | func tabMoved(fromIndex: Int, toIndex: Int) { 69 | let tab = tabs.remove(at: fromIndex) 70 | tabs.insert(tab, at: toIndex) 71 | } 72 | 73 | } 74 | 75 | extension ViewController: TiltedTabViewControllerDelegate { 76 | func tabSelected(atIndex index: Int) { 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Sources/Layouts/TiltedTabCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TiltedTabCollectionViewLayout.swift 3 | // TiltedTabView 4 | // 5 | // Copyright © 2017 Ian McDowell. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class TiltedTabCollectionViewLayout: UICollectionViewLayout { 11 | 12 | weak var delegate: UICollectionViewDelegate? 13 | weak var dataSource: UICollectionViewDataSource? 14 | 15 | /// Set by the view controller when user adds a tab. 16 | /// When a cell appears, if its index path was equal to this, the animation will differ. 17 | var addingIndexPath: IndexPath? 18 | 19 | /// Set by the view controller when user removes a tab. 20 | /// When a cell disappears, if its index path was equal to this, the animation will differ. 21 | var removingIndexPath: IndexPath? 22 | 23 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 24 | return true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Layouts/TiltedTabGridCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TiltedTabGridCollectionViewLayout.swift 3 | // TiltedTabView 4 | // 5 | // Copyright © 2017 Ian McDowell. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class TiltedTabGridCollectionViewLayout: TiltedTabCollectionViewLayout { 11 | 12 | private let maxItemsPerRow: Int = 3 13 | private let minSpacingBetweenItems: CGFloat = 35 14 | 15 | private var layoutAttributes: [IndexPath: UICollectionViewLayoutAttributes] = [:] 16 | private var contentHeight: CGFloat = 0 17 | 18 | override func prepare() { 19 | super.prepare() 20 | 21 | layoutAttributes = [:] 22 | 23 | guard let collectionView = collectionView else { 24 | return 25 | } 26 | 27 | var currentHeight: CGFloat = 0 28 | 29 | // Subtract spacing on left/right 30 | let horizontalSpacing = minSpacingBetweenItems 31 | let verticalSpacing = minSpacingBetweenItems 32 | let rowUsableWidth = collectionView.bounds.width - horizontalSpacing 33 | let maxItemWidth = collectionView.bounds.width * 0.8 34 | 35 | for section in 0.. UICollectionViewLayoutAttributes? { 82 | return layoutAttributes[indexPath] 83 | } 84 | 85 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 86 | return layoutAttributes.values.filter { rect.intersects($0.frame) } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Sources/Layouts/TiltedTabTiltedCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TiltedTabTiltedCollectionViewLayout.swift 3 | // TiltedTabView 4 | // 5 | // Copyright © 2017 Ian McDowell. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class TiltedTabTiltedCollectionViewLayout: TiltedTabCollectionViewLayout { 11 | 12 | private var layoutAttributes: [IndexPath: UICollectionViewLayoutAttributes] = [:] 13 | private var contentHeight: CGFloat = 0 14 | 15 | private let leftRightPadding: CGFloat = 20 16 | private let minimumAngle: CGFloat = -30 17 | private let maximumAngle: CGFloat = -80 18 | private let standardDepth: CGFloat = 200 19 | private let distanceBetweenItems: CGFloat = 123 20 | 21 | internal var currentMotionOffset: UIOffset = UIOffsetMake(0, 0) { 22 | didSet { self.invalidateLayout() } 23 | } 24 | 25 | override func prepare() { 26 | super.prepare() 27 | 28 | layoutAttributes = [:] 29 | contentHeight = 0 30 | 31 | guard let collectionView = collectionView else { 32 | return 33 | } 34 | 35 | let itemWidth = collectionView.bounds.width - (2 * leftRightPadding) 36 | let scaleFactor: CGFloat = itemWidth / collectionView.bounds.width 37 | let itemHeight = collectionView.bounds.height * scaleFactor 38 | 39 | for section in 0.. UICollectionViewLayoutAttributes? { 88 | return layoutAttributes[indexPath] 89 | } 90 | 91 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 92 | return layoutAttributes.values.filter { rect.intersects($0.frame) } 93 | } 94 | 95 | override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 96 | guard let attributes = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath) else { return nil } 97 | if self.addingIndexPath == itemIndexPath { 98 | attributes.transform3D = CATransform3DScale(CATransform3DTranslate(attributes.transform3D, 0, attributes.bounds.height, 0), 0.8, 0.8, 0.8) 99 | } 100 | return attributes 101 | } 102 | 103 | override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 104 | guard let attributes = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath) else { return nil } 105 | if self.removingIndexPath == itemIndexPath { 106 | attributes.transform3D = CATransform3DTranslate(attributes.transform3D, -attributes.bounds.width, 0, 0) 107 | } 108 | return attributes 109 | } 110 | 111 | override func layoutAttributesForInteractivelyMovingItem(at indexPath: IndexPath, withTargetPosition position: CGPoint) -> UICollectionViewLayoutAttributes { 112 | let attributes = super.layoutAttributesForInteractivelyMovingItem(at: indexPath, withTargetPosition: position) 113 | return attributes 114 | } 115 | 116 | } 117 | 118 | internal extension TiltedTabTiltedCollectionViewLayout { 119 | 120 | func applyMotionEffects(toCollectionView collectionView: UICollectionView) { 121 | for effect in collectionView.motionEffects { 122 | if effect is NotifyingMotionEffect { 123 | collectionView.removeMotionEffect(effect) 124 | } 125 | } 126 | 127 | collectionView.addMotionEffect(NotifyingMotionEffect(layout: self)) 128 | } 129 | } 130 | 131 | @objc private class NotifyingMotionEffect: UIMotionEffect { 132 | 133 | private weak var layout: TiltedTabTiltedCollectionViewLayout? 134 | init(layout: TiltedTabTiltedCollectionViewLayout) { 135 | self.layout = layout 136 | super.init() 137 | } 138 | 139 | required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } 140 | 141 | override func keyPathsAndRelativeValues(forViewerOffset viewerOffset: UIOffset) -> [String : Any]? { 142 | self.layout?.currentMotionOffset = viewerOffset 143 | return nil 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Sources/TiltedTabViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TiltedTabViewController.swift 3 | // TiltedTabView 4 | // 5 | // Copyright © 2017 Ian McDowell. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Conform to this data source protocol to provide the tilted tab view data. 11 | public protocol TiltedTabViewControllerDataSource: class { 12 | 13 | /// How many tabs are present in the tab view 14 | func numberOfTabsInTiltedTabViewController() -> Int 15 | 16 | /// Provide an image to display in the tab. Use UIGraphicsImageRenderer to render a view's hierarchy to retrieve a snapshot before presenting the tab view, cache it, and return it here. 17 | func snapshotForTab(atIndex index: Int) -> UIImage? 18 | 19 | /// The title to be displayed on the tab 20 | func titleForTab(atIndex index: Int) -> String? 21 | 22 | /// Used for presentation/dismissal. The tab view will animate the tab at this index in and out. 23 | func indexForActiveTab() -> Int? 24 | 25 | /// Called when a tab was manually added. Add a new tab to data storage. 26 | func tabAdded(atIndex index: Int) 27 | 28 | /// Called when a tab was closed by the user. Remove it from data storage. 29 | func tabRemoved(atIndex index: Int) 30 | 31 | /// Called when a tab was moved by the user. Move it in the data storage. 32 | func tabMoved(fromIndex: Int, toIndex: Int) 33 | } 34 | 35 | /// Conform to this delegate protocol to know when the tilted tab view does things. 36 | public protocol TiltedTabViewControllerDelegate: class { 37 | 38 | /// The user tapped on the tab at the given index. 39 | func tabSelected(atIndex index: Int) 40 | 41 | } 42 | 43 | /// A UICollectionViewController containing a tilted tab view. 44 | /// It should not be necessary to subclass this, as most functionality is exposed through its data source and delegate. 45 | open class TiltedTabViewController: UICollectionViewController { 46 | 47 | public weak var dataSource: TiltedTabViewControllerDataSource? 48 | public weak var delegate: TiltedTabViewControllerDelegate? 49 | 50 | /// Create a new tilted tab view controller. 51 | public init() { 52 | let layout = TiltedTabTiltedCollectionViewLayout() 53 | super.init(collectionViewLayout: layout) 54 | layout.delegate = self 55 | layout.dataSource = self 56 | 57 | self.transitioningDelegate = self 58 | } 59 | 60 | required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } 61 | 62 | /// Trigger a reload to the tab content. Data source methods will be called again to re-populate data. 63 | public func reload() { 64 | self.collectionView?.reloadData() 65 | } 66 | 67 | /// Add a new tab at the given index. Be sure to also add a model for this tab to the data source's model. 68 | /// The tabAdded data source method will be called by this method. 69 | public func addTab(atIndex index: Int) { 70 | dataSource?.tabAdded(atIndex: index) 71 | let indexPath = IndexPath(item: index, section: 0) 72 | let layout = self.collectionView?.collectionViewLayout as? TiltedTabCollectionViewLayout 73 | layout?.addingIndexPath = indexPath 74 | self.collectionView?.insertItems(at: [indexPath]) 75 | self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true) 76 | layout?.addingIndexPath = nil 77 | } 78 | 79 | /// Remove the tab at the given index. Be sure to also remove the model for this tab from the data source's model. 80 | /// The tabRemoved data source method will be called by this method. 81 | public func removeTab(atIndex index: Int) { 82 | dataSource?.tabRemoved(atIndex: index) 83 | let indexPath = IndexPath(item: index, section: 0) 84 | let layout = self.collectionView?.collectionViewLayout as? TiltedTabCollectionViewLayout 85 | layout?.removingIndexPath = indexPath 86 | self.collectionView?.deleteItems(at: [indexPath]) 87 | layout?.removingIndexPath = nil 88 | } 89 | 90 | // MARK: UIViewController 91 | 92 | open override func viewDidLoad() { 93 | super.viewDidLoad() 94 | 95 | guard let collectionView = self.collectionView else { 96 | assertionFailure("Collection view not found in UICollectionViewController") 97 | return 98 | } 99 | collectionView.backgroundColor = UIColor(white: 0.2, alpha: 1) 100 | 101 | collectionView.alwaysBounceVertical = true 102 | collectionView.dataSource = self 103 | collectionView.delegate = self 104 | collectionView.register(TiltedTabViewCell.self, forCellWithReuseIdentifier: "Tab") 105 | 106 | (collectionView.collectionViewLayout as? TiltedTabTiltedCollectionViewLayout)?.applyMotionEffects(toCollectionView: collectionView) 107 | } 108 | 109 | open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 110 | super.traitCollectionDidChange(previousTraitCollection) 111 | 112 | let layout: TiltedTabCollectionViewLayout 113 | switch self.traitCollection.horizontalSizeClass { 114 | case .compact: 115 | let tiltedLayout = TiltedTabTiltedCollectionViewLayout() 116 | tiltedLayout.applyMotionEffects(toCollectionView: self.collectionView!) 117 | layout = tiltedLayout 118 | case .regular: 119 | layout = TiltedTabGridCollectionViewLayout() 120 | default: 121 | return 122 | } 123 | 124 | layout.delegate = self 125 | layout.dataSource = self 126 | self.collectionView?.collectionViewLayout = layout 127 | } 128 | 129 | open override var preferredStatusBarStyle: UIStatusBarStyle { 130 | return .lightContent 131 | } 132 | } 133 | 134 | // MARK: UICollectionViewDataSource 135 | extension TiltedTabViewController { 136 | 137 | open override func numberOfSections(in collectionView: UICollectionView) -> Int { 138 | return 1 139 | } 140 | 141 | open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 142 | return dataSource?.numberOfTabsInTiltedTabViewController() ?? 0 143 | } 144 | 145 | open override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 146 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Tab", for: indexPath) as! TiltedTabViewCell 147 | 148 | cell.delegate = self 149 | cell.title = dataSource?.titleForTab(atIndex: indexPath.item) 150 | cell.snapshot = dataSource?.snapshotForTab(atIndex: indexPath.item) 151 | 152 | // Only show gradient if in tilted tab layout 153 | cell.gradientLayer.isHidden = !(self.collectionView?.collectionViewLayout is TiltedTabTiltedCollectionViewLayout) 154 | 155 | return cell 156 | } 157 | 158 | open override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 159 | dataSource?.tabMoved(fromIndex: sourceIndexPath.item, toIndex: destinationIndexPath.item) 160 | } 161 | 162 | open override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 163 | collectionView.deselectItem(at: indexPath, animated: true) 164 | 165 | delegate?.tabSelected(atIndex: indexPath.item) 166 | } 167 | } 168 | 169 | extension TiltedTabViewController: TiltedTabViewCellDelegate { 170 | func tiltedTabViewCellCloseButtonTapped(_ cell: TiltedTabViewCell) { 171 | guard let indexPath = collectionView?.indexPath(for: cell) else { 172 | return 173 | } 174 | self.removeTab(atIndex: indexPath.item) 175 | } 176 | } 177 | 178 | extension TiltedTabViewController: UIViewControllerTransitioningDelegate { 179 | 180 | private class PresentingAnimationController: NSObject, UIViewControllerAnimatedTransitioning { 181 | 182 | let tabViewController: TiltedTabViewController? 183 | let activatedTabIndex: Int? 184 | 185 | init(tabViewController: TiltedTabViewController?, activatedTabIndex: Int?) { 186 | self.tabViewController = tabViewController; self.activatedTabIndex = activatedTabIndex 187 | } 188 | 189 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 190 | return 0.5 191 | } 192 | 193 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 194 | guard let toViewController = transitionContext.viewController(forKey: .to) else { 195 | return 196 | } 197 | 198 | let toView = transitionContext.view(forKey: .to) 199 | let fromView = transitionContext.view(forKey: .from) 200 | let fromSnapshot = fromView?.snapshotView(afterScreenUpdates: true) 201 | 202 | let containerView = transitionContext.containerView 203 | 204 | if let toView = toView, let fromView = fromView, let fromSnapshot = fromSnapshot { 205 | containerView.insertSubview(fromSnapshot, aboveSubview: fromView) 206 | containerView.insertSubview(toView, belowSubview: fromSnapshot) 207 | toView.frame = transitionContext.finalFrame(for: toViewController) 208 | } 209 | 210 | let finalPosition: CGRect 211 | let finalTransform: CATransform3D 212 | if 213 | let tabViewController = tabViewController, 214 | let activatedTabIndex = activatedTabIndex, 215 | let layout = tabViewController.collectionViewLayout as? TiltedTabCollectionViewLayout 216 | { 217 | layout.prepare() 218 | let attributes = layout.layoutAttributesForItem(at: IndexPath(item: activatedTabIndex, section: 0))! 219 | 220 | finalPosition = attributes.frame 221 | finalTransform = attributes.transform3D 222 | } else { 223 | // Fall back to a scale transform 224 | finalTransform = CATransform3DMakeScale(0.5, 0.5, 1) 225 | finalPosition = fromView?.frame ?? .zero 226 | } 227 | 228 | toView?.alpha = 0 229 | UIView.animate(withDuration: 230 | self.transitionDuration(using: transitionContext), 231 | delay: 0, 232 | usingSpringWithDamping: 300, 233 | initialSpringVelocity: 5, 234 | options: .allowUserInteraction, 235 | animations: { 236 | toView?.alpha = 1 237 | 238 | fromSnapshot?.layer.transform = finalTransform 239 | fromSnapshot?.frame = finalPosition 240 | fromSnapshot?.alpha = 0 241 | }, 242 | completion: { finished in 243 | 244 | let wasCancelled = transitionContext.transitionWasCancelled 245 | transitionContext.completeTransition(!wasCancelled) 246 | 247 | fromSnapshot?.removeFromSuperview() 248 | } 249 | ) 250 | } 251 | } 252 | 253 | private class DismissingAnimationController: NSObject, UIViewControllerAnimatedTransitioning { 254 | 255 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 256 | return 0.5 257 | } 258 | 259 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 260 | guard let toViewController = transitionContext.viewController(forKey: .to) else { 261 | return 262 | } 263 | 264 | let toView = transitionContext.view(forKey: .to) 265 | let fromView = transitionContext.view(forKey: .from) 266 | 267 | let containerView = transitionContext.containerView 268 | 269 | if let toView = toView { 270 | containerView.addSubview(toView) 271 | toView.frame = transitionContext.finalFrame(for: toViewController) 272 | } 273 | 274 | toView?.alpha = 0 275 | toView?.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) 276 | UIView.animate(withDuration: 277 | self.transitionDuration(using: transitionContext), 278 | delay: 0, 279 | usingSpringWithDamping: 300, 280 | initialSpringVelocity: 5, 281 | options: .allowUserInteraction, 282 | animations: { 283 | fromView?.alpha = 0 284 | toView?.alpha = 1 285 | toView?.transform = .identity 286 | }, 287 | completion: { finished in 288 | 289 | let wasCancelled = transitionContext.transitionWasCancelled 290 | transitionContext.completeTransition(!wasCancelled) 291 | } 292 | ) 293 | } 294 | } 295 | 296 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 297 | return PresentingAnimationController( 298 | tabViewController: presented as? TiltedTabViewController ?? (presented as? UINavigationController)?.childViewControllers.first as? TiltedTabViewController, 299 | activatedTabIndex: (source as? TiltedTabViewControllerDataSource)?.indexForActiveTab() 300 | ) 301 | } 302 | 303 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 304 | return DismissingAnimationController() 305 | } 306 | 307 | public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 308 | return nil 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /Sources/Views/TiltedTabViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TiltedTabViewCell.swift 3 | // TiltedTabView 4 | // 5 | // Copyright © 2017 Ian McDowell. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol TiltedTabViewCellDelegate: class { 11 | func tiltedTabViewCellCloseButtonTapped(_ cell: TiltedTabViewCell) 12 | } 13 | 14 | class TiltedTabViewCell: UICollectionViewCell { 15 | 16 | private var cornerRadius: CGFloat = 8 17 | 18 | weak var delegate: TiltedTabViewCellDelegate? 19 | 20 | var title: String? { 21 | didSet { 22 | headerView.titleLabel.text = title 23 | } 24 | } 25 | var snapshot: UIImage? { 26 | didSet { 27 | snapshotContainer.image = snapshot 28 | } 29 | } 30 | 31 | private let headerView: TiltedTabViewCell.HeaderView 32 | private let snapshotContainer: UIImageView 33 | let gradientLayer: CAGradientLayer 34 | 35 | override init(frame: CGRect) { 36 | headerView = TiltedTabViewCell.HeaderView() 37 | snapshotContainer = UIImageView() 38 | gradientLayer = CAGradientLayer() 39 | super.init(frame: frame) 40 | 41 | self.layer.shouldRasterize = true 42 | self.layer.rasterizationScale = UIScreen.main.scale 43 | 44 | self.contentView.layer.cornerRadius = cornerRadius 45 | self.contentView.layer.masksToBounds = true 46 | self.backgroundView = UIView() 47 | self.backgroundView?.backgroundColor = .white 48 | self.backgroundView?.layer.cornerRadius = cornerRadius 49 | self.backgroundView?.layer.masksToBounds = true 50 | 51 | let contentView = self.contentView 52 | headerView.translatesAutoresizingMaskIntoConstraints = false 53 | snapshotContainer.translatesAutoresizingMaskIntoConstraints = false 54 | contentView.addSubview(headerView) 55 | contentView.addSubview(snapshotContainer) 56 | 57 | headerView.backgroundColor = .white 58 | 59 | gradientLayer.colors = [UIColor.clear.cgColor, UIColor.init(white: 0, alpha: 0.4).cgColor, UIColor.init(white: 0, alpha: 0.6).cgColor] 60 | gradientLayer.locations = [0, 0.4, 1] 61 | self.snapshotContainer.layer.addSublayer(gradientLayer) 62 | self.contentView.backgroundColor = .black 63 | 64 | headerView.closeButton.addTarget(self, action: #selector(closeTapped), for: .touchUpInside) 65 | 66 | NSLayoutConstraint.activate([ 67 | headerView.topAnchor.constraint(equalTo: contentView.topAnchor), 68 | headerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), 69 | headerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), 70 | headerView.heightAnchor.constraint(equalToConstant: 25) 71 | ]) 72 | 73 | NSLayoutConstraint.activate([ 74 | snapshotContainer.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), 75 | snapshotContainer.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), 76 | snapshotContainer.topAnchor.constraint(equalTo: headerView.bottomAnchor), 77 | snapshotContainer.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) 78 | ]) 79 | } 80 | 81 | required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } 82 | 83 | override var isHighlighted: Bool { 84 | didSet { 85 | let transform = isHighlighted ? CGAffineTransform(scaleX: 1.05, y: 1.05) : .identity 86 | UIView.animate( 87 | withDuration: 0.3, 88 | delay: 0, 89 | usingSpringWithDamping: 500, 90 | initialSpringVelocity: 3, 91 | options: .allowUserInteraction, 92 | animations: { 93 | self.contentView.transform = transform 94 | self.backgroundView?.transform = transform 95 | }, 96 | completion: nil 97 | ) 98 | } 99 | } 100 | 101 | override func layoutSubviews() { 102 | super.layoutSubviews() 103 | self.gradientLayer.frame = self.snapshotContainer.bounds 104 | } 105 | 106 | override func prepareForReuse() { 107 | super.prepareForReuse() 108 | 109 | title = nil 110 | snapshot = nil 111 | } 112 | 113 | @objc private func closeTapped() { 114 | self.delegate?.tiltedTabViewCellCloseButtonTapped(self) 115 | } 116 | 117 | class HeaderView: UIView { 118 | let closeButton: UIButton 119 | let titleLabel: UILabel 120 | 121 | init() { 122 | closeButton = UIButton(type: .system) 123 | titleLabel = UILabel() 124 | super.init(frame: .zero) 125 | 126 | closeButton.setImage(HeaderView.closeImage, for: .normal) 127 | closeButton.tintColor = .black 128 | 129 | titleLabel.text = "" 130 | titleLabel.textAlignment = .center 131 | titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .semibold) 132 | 133 | closeButton.translatesAutoresizingMaskIntoConstraints = false 134 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 135 | addSubview(closeButton) 136 | addSubview(titleLabel) 137 | 138 | NSLayoutConstraint.activate([ 139 | closeButton.widthAnchor.constraint(equalTo: closeButton.heightAnchor), 140 | closeButton.leadingAnchor.constraint(equalTo: leadingAnchor), 141 | closeButton.topAnchor.constraint(equalTo: topAnchor), 142 | closeButton.bottomAnchor.constraint(equalTo: bottomAnchor) 143 | ]) 144 | 145 | NSLayoutConstraint.activate([ 146 | titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20), 147 | titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20), 148 | titleLabel.topAnchor.constraint(equalTo: topAnchor), 149 | titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor) 150 | ]) 151 | } 152 | 153 | required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } 154 | 155 | private static var closeImage: UIImage = { 156 | return UIGraphicsImageRenderer(size: CGSize(width: 12, height: 12)).image(actions: { context in 157 | let downwards = UIBezierPath() 158 | downwards.move(to: CGPoint(x: 1, y: 1)) 159 | downwards.addLine(to: CGPoint(x: 11, y: 11)) 160 | UIColor.black.setStroke() 161 | downwards.lineWidth = 2 162 | downwards.stroke() 163 | 164 | let upwards = UIBezierPath() 165 | upwards.move(to: CGPoint(x: 1, y: 11)) 166 | upwards.addLine(to: CGPoint(x: 11, y: 1)) 167 | UIColor.black.setStroke() 168 | upwards.lineWidth = 2 169 | upwards.stroke() 170 | 171 | context.cgContext.addPath(downwards.cgPath) 172 | context.cgContext.addPath(upwards.cgPath) 173 | }) 174 | }() 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /TiltedTabView.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint InputAssistant.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "TiltedTabView" 19 | s.version = "1.0.0" 20 | s.summary = "This library aims to replicate the tab switcher in Safari on iOS. It handles both compact width (tilted) and regular width (grid) layouts." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | TiltedTabView 1.0.0 - This library aims to replicate the tab switcher in Safari on iOS. It handles both compact width (tilted) and regular width (grid) layouts. 29 | DESC 30 | 31 | s.homepage = "https://github.com/IMcD23/TiltedTabView" 32 | s.screenshots = "https://github.com/IMcD23/TiltedTabView/raw/master/Resources/Screenshot.png","https://github.com/IMcD23/TiltedTabView/raw/master/Resources/Screenshot_iPad.png" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | s.license = { :type => "MIT", :file => "LICENSE" } 43 | 44 | 45 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 46 | # 47 | # Specify the authors of the library, with email addresses. Email addresses 48 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 49 | # accepts just a name if you'd rather not provide an email address. 50 | # 51 | # Specify a social_media_url where others can refer to, for example a twitter 52 | # profile URL. 53 | # 54 | 55 | s.author = { "Ian McDowell" => "me@ianmcdowell.net" } 56 | # Or just: s.author = "Ian McDowell" 57 | # s.authors = { "Ian McDowell" => "me@ianmcdowell.net" } 58 | s.social_media_url = "http://twitter.com/ian_mcdowell" 59 | 60 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 61 | # 62 | # If this Pod runs only on iOS or OS X, then specify the platform and 63 | # the deployment target. You can optionally include the target after the platform. 64 | # 65 | 66 | s.platform = :ios, "10.0" 67 | # s.platform = :ios, "5.0" 68 | 69 | # When using multiple platforms 70 | # s.ios.deployment_target = "5.0" 71 | # s.osx.deployment_target = "10.7" 72 | # s.watchos.deployment_target = "2.0" 73 | # s.tvos.deployment_target = "9.0" 74 | 75 | 76 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 77 | # 78 | # Specify the location from where the source should be retrieved. 79 | # Supports git, hg, bzr, svn and HTTP. 80 | # 81 | 82 | s.source = { :git => "https://github.com/IMcD23/TiltedTabView.git", :tag => "#{s.version}" } 83 | 84 | 85 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 86 | # 87 | # CocoaPods is smart about how it includes source code. For source files 88 | # giving a folder will include any swift, h, m, mm, c & cpp files. 89 | # For header files it will include any header in the folder. 90 | # Not including the public_header_files will make all headers public. 91 | # 92 | 93 | s.source_files = "Sources/**/*.swift" 94 | 95 | 96 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 97 | # 98 | # A list of resources included with the Pod. These are copied into the 99 | # target bundle with a build phase script. Anything else will be cleaned. 100 | # You can preserve files from being cleaned, please don't preserve 101 | # non-essential files like tests, examples and documentation. 102 | # 103 | 104 | # s.resource = "icon.png" 105 | # s.resources = "Resources/*.png" 106 | 107 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 108 | 109 | 110 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 111 | # 112 | # Link your library with frameworks, or libraries. Libraries do not include 113 | # the lib prefix of their name. 114 | # 115 | 116 | # s.framework = "SomeFramework" 117 | # s.frameworks = "SomeFramework", "AnotherFramework" 118 | 119 | # s.library = "iconv" 120 | # s.libraries = "iconv", "xml2" 121 | 122 | 123 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 124 | # 125 | # If your library depends on compiler flags you can set them in the xcconfig hash 126 | # where they will only apply to your library. If you depend on other Podspecs 127 | # you can include multiple dependencies to ensure it works. 128 | 129 | s.requires_arc = true 130 | s.swift_version = "4.0" 131 | 132 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 133 | # s.dependency "JSONKit", "~> 1.4" 134 | 135 | end 136 | -------------------------------------------------------------------------------- /TiltedTabView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3C13AC28201AF74B002F36B2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C13AC27201AF74B002F36B2 /* ViewController.swift */; }; 11 | 3C13AC2A201AF74B002F36B2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C13AC29201AF74B002F36B2 /* AppDelegate.swift */; }; 12 | 3C13AC2F201AF74B002F36B2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3C13AC2E201AF74B002F36B2 /* Assets.xcassets */; }; 13 | 3C13AC32201AF74B002F36B2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3C13AC30201AF74B002F36B2 /* LaunchScreen.storyboard */; }; 14 | 3C13AC3A201AF7C9002F36B2 /* TiltedTabView.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 3CB476881FF775C400C6B265 /* TiltedTabView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 3CB476AC1FF7769900C6B265 /* TiltedTabCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A61FF7769900C6B265 /* TiltedTabCollectionViewLayout.swift */; }; 16 | 3CB476AD1FF7769900C6B265 /* TiltedTabTiltedCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A71FF7769900C6B265 /* TiltedTabTiltedCollectionViewLayout.swift */; }; 17 | 3CB476AE1FF7769900C6B265 /* TiltedTabGridCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A81FF7769900C6B265 /* TiltedTabGridCollectionViewLayout.swift */; }; 18 | 3CB476AF1FF7769900C6B265 /* TiltedTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A91FF7769900C6B265 /* TiltedTabViewController.swift */; }; 19 | 3CB476B01FF7769900C6B265 /* TiltedTabViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476AB1FF7769900C6B265 /* TiltedTabViewCell.swift */; }; 20 | 3CB476B31FF776EB00C6B265 /* TiltedTabView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CB476B11FF776E200C6B265 /* TiltedTabView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | 3CB476B91FF77B7500C6B265 /* TiltedTabCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A61FF7769900C6B265 /* TiltedTabCollectionViewLayout.swift */; }; 22 | 3CB476BA1FF77B7500C6B265 /* TiltedTabTiltedCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A71FF7769900C6B265 /* TiltedTabTiltedCollectionViewLayout.swift */; }; 23 | 3CB476BB1FF77B7500C6B265 /* TiltedTabGridCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A81FF7769900C6B265 /* TiltedTabGridCollectionViewLayout.swift */; }; 24 | 3CB476BC1FF77B7500C6B265 /* TiltedTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476A91FF7769900C6B265 /* TiltedTabViewController.swift */; }; 25 | 3CB476BD1FF77B7700C6B265 /* TiltedTabViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB476AB1FF7769900C6B265 /* TiltedTabViewCell.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 3C13AC37201AF78D002F36B2 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 3CB4767F1FF775C400C6B265 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 3CB476871FF775C400C6B265; 34 | remoteInfo = TiltedTabView; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXCopyFilesBuildPhase section */ 39 | 3C13AC39201AF7BF002F36B2 /* Copy Frameworks */ = { 40 | isa = PBXCopyFilesBuildPhase; 41 | buildActionMask = 2147483647; 42 | dstPath = ""; 43 | dstSubfolderSpec = 10; 44 | files = ( 45 | 3C13AC3A201AF7C9002F36B2 /* TiltedTabView.framework in Copy Frameworks */, 46 | ); 47 | name = "Copy Frameworks"; 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | 3CB476951FF7761400C6B265 /* CopyFiles */ = { 51 | isa = PBXCopyFilesBuildPhase; 52 | buildActionMask = 2147483647; 53 | dstPath = "include/$(PRODUCT_NAME)"; 54 | dstSubfolderSpec = 16; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXCopyFilesBuildPhase section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 3C13AC25201AF74B002F36B2 /* TiltedTabView Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TiltedTabView Sample.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 3C13AC27201AF74B002F36B2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 64 | 3C13AC29201AF74B002F36B2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | 3C13AC2E201AF74B002F36B2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 66 | 3C13AC31201AF74B002F36B2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 67 | 3C13AC33201AF74B002F36B2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 3CB476881FF775C400C6B265 /* TiltedTabView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TiltedTabView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 3CB4768C1FF775C400C6B265 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 3CB476971FF7761400C6B265 /* libTiltedTabView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTiltedTabView.a; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 3CB476A61FF7769900C6B265 /* TiltedTabCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TiltedTabCollectionViewLayout.swift; sourceTree = ""; }; 72 | 3CB476A71FF7769900C6B265 /* TiltedTabTiltedCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TiltedTabTiltedCollectionViewLayout.swift; sourceTree = ""; }; 73 | 3CB476A81FF7769900C6B265 /* TiltedTabGridCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TiltedTabGridCollectionViewLayout.swift; sourceTree = ""; }; 74 | 3CB476A91FF7769900C6B265 /* TiltedTabViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TiltedTabViewController.swift; sourceTree = ""; }; 75 | 3CB476AB1FF7769900C6B265 /* TiltedTabViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TiltedTabViewCell.swift; sourceTree = ""; }; 76 | 3CB476B11FF776E200C6B265 /* TiltedTabView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiltedTabView.h; sourceTree = ""; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFrameworksBuildPhase section */ 80 | 3C13AC22201AF74B002F36B2 /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | 3CB476841FF775C400C6B265 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | 3CB476941FF7761400C6B265 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | /* End PBXFrameworksBuildPhase section */ 102 | 103 | /* Begin PBXGroup section */ 104 | 3C13AC26201AF74B002F36B2 /* Sample */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 3C13AC27201AF74B002F36B2 /* ViewController.swift */, 108 | 3C13AC29201AF74B002F36B2 /* AppDelegate.swift */, 109 | 3C13AC2E201AF74B002F36B2 /* Assets.xcassets */, 110 | 3C13AC30201AF74B002F36B2 /* LaunchScreen.storyboard */, 111 | 3C13AC33201AF74B002F36B2 /* Info.plist */, 112 | ); 113 | path = Sample; 114 | sourceTree = ""; 115 | }; 116 | 3CB4767E1FF775C400C6B265 = { 117 | isa = PBXGroup; 118 | children = ( 119 | 3CB476A41FF7767400C6B265 /* Sources */, 120 | 3CB4768A1FF775C400C6B265 /* Framework */, 121 | 3C13AC26201AF74B002F36B2 /* Sample */, 122 | 3CB476891FF775C400C6B265 /* Products */, 123 | 3CB476A21FF7765D00C6B265 /* Frameworks */, 124 | ); 125 | sourceTree = ""; 126 | usesTabs = 0; 127 | }; 128 | 3CB476891FF775C400C6B265 /* Products */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 3CB476881FF775C400C6B265 /* TiltedTabView.framework */, 132 | 3CB476971FF7761400C6B265 /* libTiltedTabView.a */, 133 | 3C13AC25201AF74B002F36B2 /* TiltedTabView Sample.app */, 134 | ); 135 | name = Products; 136 | sourceTree = ""; 137 | }; 138 | 3CB4768A1FF775C400C6B265 /* Framework */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 3CB476B11FF776E200C6B265 /* TiltedTabView.h */, 142 | 3CB4768C1FF775C400C6B265 /* Info.plist */, 143 | ); 144 | path = Framework; 145 | sourceTree = ""; 146 | }; 147 | 3CB476A21FF7765D00C6B265 /* Frameworks */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | ); 151 | name = Frameworks; 152 | sourceTree = ""; 153 | }; 154 | 3CB476A41FF7767400C6B265 /* Sources */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 3CB476A51FF7769900C6B265 /* Layouts */, 158 | 3CB476A91FF7769900C6B265 /* TiltedTabViewController.swift */, 159 | 3CB476AA1FF7769900C6B265 /* Views */, 160 | ); 161 | path = Sources; 162 | sourceTree = ""; 163 | }; 164 | 3CB476A51FF7769900C6B265 /* Layouts */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 3CB476A61FF7769900C6B265 /* TiltedTabCollectionViewLayout.swift */, 168 | 3CB476A71FF7769900C6B265 /* TiltedTabTiltedCollectionViewLayout.swift */, 169 | 3CB476A81FF7769900C6B265 /* TiltedTabGridCollectionViewLayout.swift */, 170 | ); 171 | path = Layouts; 172 | sourceTree = ""; 173 | }; 174 | 3CB476AA1FF7769900C6B265 /* Views */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 3CB476AB1FF7769900C6B265 /* TiltedTabViewCell.swift */, 178 | ); 179 | path = Views; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXHeadersBuildPhase section */ 185 | 3CB476B21FF776E700C6B265 /* Headers */ = { 186 | isa = PBXHeadersBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 3CB476B31FF776EB00C6B265 /* TiltedTabView.h in Headers */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXHeadersBuildPhase section */ 194 | 195 | /* Begin PBXNativeTarget section */ 196 | 3C13AC24201AF74B002F36B2 /* TiltedTabView Sample */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 3C13AC36201AF74B002F36B2 /* Build configuration list for PBXNativeTarget "TiltedTabView Sample" */; 199 | buildPhases = ( 200 | 3C13AC21201AF74B002F36B2 /* Sources */, 201 | 3C13AC22201AF74B002F36B2 /* Frameworks */, 202 | 3C13AC23201AF74B002F36B2 /* Resources */, 203 | 3C13AC39201AF7BF002F36B2 /* Copy Frameworks */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | 3C13AC38201AF78D002F36B2 /* PBXTargetDependency */, 209 | ); 210 | name = "TiltedTabView Sample"; 211 | productName = "TiltedTabView Sample"; 212 | productReference = 3C13AC25201AF74B002F36B2 /* TiltedTabView Sample.app */; 213 | productType = "com.apple.product-type.application"; 214 | }; 215 | 3CB476871FF775C400C6B265 /* TiltedTabView */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = 3CB476901FF775C400C6B265 /* Build configuration list for PBXNativeTarget "TiltedTabView" */; 218 | buildPhases = ( 219 | 3CB476831FF775C400C6B265 /* Sources */, 220 | 3CB476841FF775C400C6B265 /* Frameworks */, 221 | 3CB476B21FF776E700C6B265 /* Headers */, 222 | ); 223 | buildRules = ( 224 | ); 225 | dependencies = ( 226 | ); 227 | name = TiltedTabView; 228 | productName = TiltedTabView; 229 | productReference = 3CB476881FF775C400C6B265 /* TiltedTabView.framework */; 230 | productType = "com.apple.product-type.framework"; 231 | }; 232 | 3CB476961FF7761400C6B265 /* libTiltedTabView */ = { 233 | isa = PBXNativeTarget; 234 | buildConfigurationList = 3CB4769D1FF7761400C6B265 /* Build configuration list for PBXNativeTarget "libTiltedTabView" */; 235 | buildPhases = ( 236 | 3CB476931FF7761400C6B265 /* Sources */, 237 | 3CB476941FF7761400C6B265 /* Frameworks */, 238 | 3CB476951FF7761400C6B265 /* CopyFiles */, 239 | ); 240 | buildRules = ( 241 | ); 242 | dependencies = ( 243 | ); 244 | name = libTiltedTabView; 245 | productName = libTiltedTabView; 246 | productReference = 3CB476971FF7761400C6B265 /* libTiltedTabView.a */; 247 | productType = "com.apple.product-type.library.static"; 248 | }; 249 | /* End PBXNativeTarget section */ 250 | 251 | /* Begin PBXProject section */ 252 | 3CB4767F1FF775C400C6B265 /* Project object */ = { 253 | isa = PBXProject; 254 | attributes = { 255 | LastSwiftUpdateCheck = 0920; 256 | LastUpgradeCheck = 0920; 257 | ORGANIZATIONNAME = "Ian McDowell"; 258 | TargetAttributes = { 259 | 3C13AC24201AF74B002F36B2 = { 260 | CreatedOnToolsVersion = 9.2; 261 | }; 262 | 3CB476871FF775C400C6B265 = { 263 | CreatedOnToolsVersion = 9.2; 264 | ProvisioningStyle = Automatic; 265 | }; 266 | 3CB476961FF7761400C6B265 = { 267 | CreatedOnToolsVersion = 9.2; 268 | LastSwiftMigration = 0920; 269 | }; 270 | }; 271 | }; 272 | buildConfigurationList = 3CB476821FF775C400C6B265 /* Build configuration list for PBXProject "TiltedTabView" */; 273 | compatibilityVersion = "Xcode 8.0"; 274 | developmentRegion = en; 275 | hasScannedForEncodings = 0; 276 | knownRegions = ( 277 | en, 278 | Base, 279 | ); 280 | mainGroup = 3CB4767E1FF775C400C6B265; 281 | productRefGroup = 3CB476891FF775C400C6B265 /* Products */; 282 | projectDirPath = ""; 283 | projectRoot = ""; 284 | targets = ( 285 | 3CB476871FF775C400C6B265 /* TiltedTabView */, 286 | 3CB476961FF7761400C6B265 /* libTiltedTabView */, 287 | 3C13AC24201AF74B002F36B2 /* TiltedTabView Sample */, 288 | ); 289 | }; 290 | /* End PBXProject section */ 291 | 292 | /* Begin PBXResourcesBuildPhase section */ 293 | 3C13AC23201AF74B002F36B2 /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | 3C13AC32201AF74B002F36B2 /* LaunchScreen.storyboard in Resources */, 298 | 3C13AC2F201AF74B002F36B2 /* Assets.xcassets in Resources */, 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | }; 302 | /* End PBXResourcesBuildPhase section */ 303 | 304 | /* Begin PBXSourcesBuildPhase section */ 305 | 3C13AC21201AF74B002F36B2 /* Sources */ = { 306 | isa = PBXSourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | 3C13AC2A201AF74B002F36B2 /* AppDelegate.swift in Sources */, 310 | 3C13AC28201AF74B002F36B2 /* ViewController.swift in Sources */, 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | 3CB476831FF775C400C6B265 /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 3CB476BC1FF77B7500C6B265 /* TiltedTabViewController.swift in Sources */, 319 | 3CB476BA1FF77B7500C6B265 /* TiltedTabTiltedCollectionViewLayout.swift in Sources */, 320 | 3CB476BD1FF77B7700C6B265 /* TiltedTabViewCell.swift in Sources */, 321 | 3CB476BB1FF77B7500C6B265 /* TiltedTabGridCollectionViewLayout.swift in Sources */, 322 | 3CB476B91FF77B7500C6B265 /* TiltedTabCollectionViewLayout.swift in Sources */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | 3CB476931FF7761400C6B265 /* Sources */ = { 327 | isa = PBXSourcesBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | 3CB476AF1FF7769900C6B265 /* TiltedTabViewController.swift in Sources */, 331 | 3CB476AD1FF7769900C6B265 /* TiltedTabTiltedCollectionViewLayout.swift in Sources */, 332 | 3CB476B01FF7769900C6B265 /* TiltedTabViewCell.swift in Sources */, 333 | 3CB476AE1FF7769900C6B265 /* TiltedTabGridCollectionViewLayout.swift in Sources */, 334 | 3CB476AC1FF7769900C6B265 /* TiltedTabCollectionViewLayout.swift in Sources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | /* End PBXSourcesBuildPhase section */ 339 | 340 | /* Begin PBXTargetDependency section */ 341 | 3C13AC38201AF78D002F36B2 /* PBXTargetDependency */ = { 342 | isa = PBXTargetDependency; 343 | target = 3CB476871FF775C400C6B265 /* TiltedTabView */; 344 | targetProxy = 3C13AC37201AF78D002F36B2 /* PBXContainerItemProxy */; 345 | }; 346 | /* End PBXTargetDependency section */ 347 | 348 | /* Begin PBXVariantGroup section */ 349 | 3C13AC30201AF74B002F36B2 /* LaunchScreen.storyboard */ = { 350 | isa = PBXVariantGroup; 351 | children = ( 352 | 3C13AC31201AF74B002F36B2 /* Base */, 353 | ); 354 | name = LaunchScreen.storyboard; 355 | sourceTree = ""; 356 | }; 357 | /* End PBXVariantGroup section */ 358 | 359 | /* Begin XCBuildConfiguration section */ 360 | 3C13AC34201AF74B002F36B2 /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | INFOPLIST_FILE = "$(SRCROOT)/Sample/Info.plist"; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 366 | PRODUCT_BUNDLE_IDENTIFIER = "net.ianmcdowell.TiltedTabView-Sample"; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | SWIFT_VERSION = 4.0; 369 | }; 370 | name = Debug; 371 | }; 372 | 3C13AC35201AF74B002F36B2 /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | INFOPLIST_FILE = "$(SRCROOT)/Sample/Info.plist"; 377 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 378 | PRODUCT_BUNDLE_IDENTIFIER = "net.ianmcdowell.TiltedTabView-Sample"; 379 | PRODUCT_NAME = "$(TARGET_NAME)"; 380 | SWIFT_VERSION = 4.0; 381 | }; 382 | name = Release; 383 | }; 384 | 3CB4768E1FF775C400C6B265 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ALWAYS_SEARCH_USER_PATHS = NO; 388 | CLANG_ANALYZER_NONNULL = YES; 389 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 390 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 391 | CLANG_CXX_LIBRARY = "libc++"; 392 | CLANG_ENABLE_MODULES = YES; 393 | CLANG_ENABLE_OBJC_ARC = YES; 394 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 395 | CLANG_WARN_BOOL_CONVERSION = YES; 396 | CLANG_WARN_COMMA = YES; 397 | CLANG_WARN_CONSTANT_CONVERSION = YES; 398 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 399 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 400 | CLANG_WARN_EMPTY_BODY = YES; 401 | CLANG_WARN_ENUM_CONVERSION = YES; 402 | CLANG_WARN_INFINITE_RECURSION = YES; 403 | CLANG_WARN_INT_CONVERSION = YES; 404 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 405 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 407 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 408 | CLANG_WARN_STRICT_PROTOTYPES = YES; 409 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 410 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 411 | CLANG_WARN_UNREACHABLE_CODE = YES; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | CODE_SIGN_IDENTITY = ""; 414 | COPY_PHASE_STRIP = NO; 415 | CURRENT_PROJECT_VERSION = 1; 416 | DEBUG_INFORMATION_FORMAT = dwarf; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | ENABLE_TESTABILITY = YES; 419 | GCC_C_LANGUAGE_STANDARD = gnu11; 420 | GCC_DYNAMIC_NO_PIC = NO; 421 | GCC_NO_COMMON_BLOCKS = YES; 422 | GCC_OPTIMIZATION_LEVEL = 0; 423 | GCC_PREPROCESSOR_DEFINITIONS = ( 424 | "DEBUG=1", 425 | "$(inherited)", 426 | ); 427 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 428 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 429 | GCC_WARN_UNDECLARED_SELECTOR = YES; 430 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 431 | GCC_WARN_UNUSED_FUNCTION = YES; 432 | GCC_WARN_UNUSED_VARIABLE = YES; 433 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 434 | MTL_ENABLE_DEBUG_INFO = YES; 435 | ONLY_ACTIVE_ARCH = YES; 436 | SDKROOT = iphoneos; 437 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 438 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 439 | SWIFT_VERSION = 4.0; 440 | TARGETED_DEVICE_FAMILY = "1,2"; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | VERSION_INFO_PREFIX = ""; 443 | }; 444 | name = Debug; 445 | }; 446 | 3CB4768F1FF775C400C6B265 /* Release */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ALWAYS_SEARCH_USER_PATHS = NO; 450 | CLANG_ANALYZER_NONNULL = YES; 451 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 452 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 453 | CLANG_CXX_LIBRARY = "libc++"; 454 | CLANG_ENABLE_MODULES = YES; 455 | CLANG_ENABLE_OBJC_ARC = YES; 456 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 457 | CLANG_WARN_BOOL_CONVERSION = YES; 458 | CLANG_WARN_COMMA = YES; 459 | CLANG_WARN_CONSTANT_CONVERSION = YES; 460 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 461 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 462 | CLANG_WARN_EMPTY_BODY = YES; 463 | CLANG_WARN_ENUM_CONVERSION = YES; 464 | CLANG_WARN_INFINITE_RECURSION = YES; 465 | CLANG_WARN_INT_CONVERSION = YES; 466 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 467 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 469 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 470 | CLANG_WARN_STRICT_PROTOTYPES = YES; 471 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 472 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 473 | CLANG_WARN_UNREACHABLE_CODE = YES; 474 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 475 | CODE_SIGN_IDENTITY = ""; 476 | COPY_PHASE_STRIP = NO; 477 | CURRENT_PROJECT_VERSION = 1; 478 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 479 | ENABLE_NS_ASSERTIONS = NO; 480 | ENABLE_STRICT_OBJC_MSGSEND = YES; 481 | GCC_C_LANGUAGE_STANDARD = gnu11; 482 | GCC_NO_COMMON_BLOCKS = YES; 483 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 484 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 485 | GCC_WARN_UNDECLARED_SELECTOR = YES; 486 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 487 | GCC_WARN_UNUSED_FUNCTION = YES; 488 | GCC_WARN_UNUSED_VARIABLE = YES; 489 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 490 | MTL_ENABLE_DEBUG_INFO = NO; 491 | SDKROOT = iphoneos; 492 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 493 | SWIFT_VERSION = 4.0; 494 | TARGETED_DEVICE_FAMILY = "1,2"; 495 | VALIDATE_PRODUCT = YES; 496 | VERSIONING_SYSTEM = "apple-generic"; 497 | VERSION_INFO_PREFIX = ""; 498 | }; 499 | name = Release; 500 | }; 501 | 3CB476911FF775C400C6B265 /* Debug */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | APPLICATION_EXTENSION_API_ONLY = YES; 505 | DEFINES_MODULE = YES; 506 | DYLIB_COMPATIBILITY_VERSION = 1; 507 | DYLIB_CURRENT_VERSION = 1; 508 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 509 | INFOPLIST_FILE = Framework/Info.plist; 510 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 512 | LIBRARY_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"; 513 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.TiltedTabView; 514 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 515 | SKIP_INSTALL = YES; 516 | }; 517 | name = Debug; 518 | }; 519 | 3CB476921FF775C400C6B265 /* Release */ = { 520 | isa = XCBuildConfiguration; 521 | buildSettings = { 522 | APPLICATION_EXTENSION_API_ONLY = YES; 523 | DEFINES_MODULE = YES; 524 | DYLIB_COMPATIBILITY_VERSION = 1; 525 | DYLIB_CURRENT_VERSION = 1; 526 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 527 | INFOPLIST_FILE = Framework/Info.plist; 528 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 529 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 530 | LIBRARY_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"; 531 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.TiltedTabView; 532 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 533 | SKIP_INSTALL = YES; 534 | }; 535 | name = Release; 536 | }; 537 | 3CB4769E1FF7761400C6B265 /* Debug */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | APPLICATION_EXTENSION_API_ONLY = YES; 541 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 542 | OTHER_LDFLAGS = "-ObjC"; 543 | PRODUCT_NAME = TiltedTabView; 544 | SKIP_INSTALL = YES; 545 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 546 | }; 547 | name = Debug; 548 | }; 549 | 3CB4769F1FF7761400C6B265 /* Release */ = { 550 | isa = XCBuildConfiguration; 551 | buildSettings = { 552 | APPLICATION_EXTENSION_API_ONLY = YES; 553 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 554 | OTHER_LDFLAGS = "-ObjC"; 555 | PRODUCT_NAME = TiltedTabView; 556 | SKIP_INSTALL = YES; 557 | }; 558 | name = Release; 559 | }; 560 | /* End XCBuildConfiguration section */ 561 | 562 | /* Begin XCConfigurationList section */ 563 | 3C13AC36201AF74B002F36B2 /* Build configuration list for PBXNativeTarget "TiltedTabView Sample" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 3C13AC34201AF74B002F36B2 /* Debug */, 567 | 3C13AC35201AF74B002F36B2 /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | 3CB476821FF775C400C6B265 /* Build configuration list for PBXProject "TiltedTabView" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 3CB4768E1FF775C400C6B265 /* Debug */, 576 | 3CB4768F1FF775C400C6B265 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | 3CB476901FF775C400C6B265 /* Build configuration list for PBXNativeTarget "TiltedTabView" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 3CB476911FF775C400C6B265 /* Debug */, 585 | 3CB476921FF775C400C6B265 /* Release */, 586 | ); 587 | defaultConfigurationIsVisible = 0; 588 | defaultConfigurationName = Release; 589 | }; 590 | 3CB4769D1FF7761400C6B265 /* Build configuration list for PBXNativeTarget "libTiltedTabView" */ = { 591 | isa = XCConfigurationList; 592 | buildConfigurations = ( 593 | 3CB4769E1FF7761400C6B265 /* Debug */, 594 | 3CB4769F1FF7761400C6B265 /* Release */, 595 | ); 596 | defaultConfigurationIsVisible = 0; 597 | defaultConfigurationName = Release; 598 | }; 599 | /* End XCConfigurationList section */ 600 | }; 601 | rootObject = 3CB4767F1FF775C400C6B265 /* Project object */; 602 | } 603 | -------------------------------------------------------------------------------- /TiltedTabView.xcodeproj/xcshareddata/xcschemes/TiltedTabView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /TiltedTabView.xcodeproj/xcshareddata/xcschemes/libTiltedTabView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /build.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | TiltedTabView 7 | build 8 | 9 | buildSystem 10 | xcode 11 | buildArgs 12 | 13 | -project 14 | TiltedTabView.xcodeproj 15 | -target 16 | libTiltedTabView 17 | 18 | outputs 19 | 20 | libTiltedTabView.a 21 | 22 | 23 | dependencies 24 | 25 | 26 | 27 | --------------------------------------------------------------------------------