├── .gitignore ├── .travis.yml ├── Example ├── GridTimerView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── GridTimerView-Example.xcscheme ├── GridTimerView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── GridTimerView │ ├── AppDelegate.swift │ ├── Cells │ │ ├── ChannelView.swift │ │ └── ChannelView.xib │ ├── Controllers │ │ ├── DetailViewController │ │ │ ├── DetailViewController.swift │ │ │ ├── DetailViewController.xib │ │ │ └── DetailViewSource.swift │ │ └── MainViewController │ │ │ ├── MainViewController+Tools.swift │ │ │ ├── MainViewController.swift │ │ │ └── MainViewController.xib │ ├── Info.plist │ ├── Models │ │ ├── Channel.swift │ │ ├── ChannelFactory.swift │ │ └── Event.swift │ ├── Resources │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ └── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-20@2x.png │ │ │ ├── Icon-20@3x.png │ │ │ ├── Icon-29@2x.png │ │ │ ├── Icon-29@3x.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ └── iTunesArtwork@2x.png │ │ │ ├── Channel Abc.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_abc@1x.png │ │ │ ├── channel_abc@2x.png │ │ │ └── channel_abc@3x.png │ │ │ ├── Channel Amc.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_amc@1x.png │ │ │ ├── channel_amc@2x.png │ │ │ └── channel_amc@3x.png │ │ │ ├── Channel Axn.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_axn@1x.png │ │ │ ├── channel_axn@2x.png │ │ │ └── channel_axn@3x.png │ │ │ ├── Channel Cnbc.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_cnbc@1x.png │ │ │ ├── channel_cnbc@2x.png │ │ │ └── channel_cnbc@3x.png │ │ │ ├── Channel Cnn.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_cnn@1x.png │ │ │ ├── channel_cnn@2x.png │ │ │ └── channel_cnn@3x.png │ │ │ ├── Channel Fox.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_fox@1x.png │ │ │ ├── channel_fox@2x.png │ │ │ └── channel_fox@3x.png │ │ │ ├── Channel Hbo.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_hbo@1x.png │ │ │ ├── channel_hbo@2x.png │ │ │ └── channel_hbo@3x.png │ │ │ ├── Channel History.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_history@1x.png │ │ │ ├── channel_history@2x.png │ │ │ └── channel_history@3x.png │ │ │ ├── Channel Uni.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_uni@1x.png │ │ │ ├── channel_uni@2x.png │ │ │ └── channel_uni@3x.png │ │ │ ├── Channel Xd.imageset │ │ │ ├── Contents.json │ │ │ ├── channel_xd@1x.png │ │ │ ├── channel_xd@2x.png │ │ │ └── channel_xd@3x.png │ │ │ ├── Contents.json │ │ │ ├── Placeholder.imageset │ │ │ ├── Contents.json │ │ │ ├── placeholder-2.png │ │ │ └── placeholder-3.png │ │ │ └── Splash.imageset │ │ │ ├── Contents.json │ │ │ ├── splash_GridTimerView@3x.png │ │ │ ├── splash_GridTimerView_@1x.png │ │ │ └── splash_GridTimerView_@2x.png │ └── Utils │ │ ├── Constants │ │ └── Colors.swift │ │ ├── Extensions │ │ ├── Array+Tools.swift │ │ ├── UIColor+Tools.swift │ │ └── UIView+Tools.swift │ │ └── Globals │ │ └── Globals.swift ├── Podfile ├── Podfile.lock └── Pods │ ├── Local Podspecs │ └── GridTimerView.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── Target Support Files │ ├── GridTimerView │ ├── GridTimerView-dummy.m │ ├── GridTimerView-prefix.pch │ ├── GridTimerView-umbrella.h │ ├── GridTimerView.modulemap │ ├── GridTimerView.xcconfig │ └── Info.plist │ ├── Pods-GridTimerView_Example │ ├── Info.plist │ ├── Pods-GridTimerView_Example-acknowledgements.markdown │ ├── Pods-GridTimerView_Example-acknowledgements.plist │ ├── Pods-GridTimerView_Example-dummy.m │ ├── Pods-GridTimerView_Example-frameworks.sh │ ├── Pods-GridTimerView_Example-resources.sh │ ├── Pods-GridTimerView_Example-umbrella.h │ ├── Pods-GridTimerView_Example.debug.xcconfig │ ├── Pods-GridTimerView_Example.modulemap │ └── Pods-GridTimerView_Example.release.xcconfig │ └── Pods-GridTimerView_Tests │ ├── Info.plist │ ├── Pods-GridTimerView_Tests-acknowledgements.markdown │ ├── Pods-GridTimerView_Tests-acknowledgements.plist │ ├── Pods-GridTimerView_Tests-dummy.m │ ├── Pods-GridTimerView_Tests-frameworks.sh │ ├── Pods-GridTimerView_Tests-resources.sh │ ├── Pods-GridTimerView_Tests-umbrella.h │ ├── Pods-GridTimerView_Tests.debug.xcconfig │ ├── Pods-GridTimerView_Tests.modulemap │ └── Pods-GridTimerView_Tests.release.xcconfig ├── GridTimerView.podspec ├── GridTimerView ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── CustomCollectionViewLayout.swift │ ├── Extensions │ ├── Date+Tools.swift │ ├── Int+Tools.swift │ ├── UICollectionView+Tools.swift │ ├── UICollectionViewCell+Tools.swift │ ├── UIImage+Tools.swift │ └── UIView+Tools.swift │ ├── Globals.swift │ ├── GridItemCollectionViewLayout.swift │ ├── GridItemViewCell.swift │ ├── GridItemViewCell.xib │ ├── GridTimeLineView.swift │ ├── GridTimerConfiguration.swift │ ├── GridTimerView+DataSource.swift │ ├── GridTimerView+Delegate.swift │ ├── GridTimerView+Protocols.swift │ ├── GridTimerView.swift │ ├── GridTimerView.xib │ ├── RuleView.swift │ └── RuleView.xib ├── Images ├── Pods │ ├── AnimatedField.png │ ├── CiaoTransitions.png │ ├── ContentLoader.png │ ├── DateScrollPicker.png │ ├── EmptyStateKit.png │ ├── GridTimerView.png │ └── PaintCodeKit.png ├── header_GridTimerView.png ├── screenshot_1.png └── video_1.gif ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | _Pods.xcodeproj 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/GridTimerView.xcworkspace -scheme GridTimerView-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Example/GridTimerView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/GridTimerView.xcodeproj/xcshareddata/xcschemes/GridTimerView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Example/GridTimerView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/GridTimerView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/GridTimerView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 2/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | 18 | let vc = MainViewController() 19 | let nav = UINavigationController(rootViewController: vc) 20 | 21 | self.window = UIWindow(frame: UIScreen.main.bounds) 22 | self.window?.rootViewController = nav 23 | self.window?.makeKeyAndVisible() 24 | 25 | return true 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Example/GridTimerView/Cells/ChannelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelView.swift 3 | // GridTimerView_Example 4 | // 5 | // Created by Alberto Aznar de los Ríos on 28/9/18. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class ChannelView: UIView { 12 | 13 | struct ViewModel { 14 | var title = "Loading" 15 | var subtitle = "" 16 | var image: UIImage? 17 | } 18 | 19 | @IBOutlet private var contentView: UIView! 20 | @IBOutlet weak var channelImageView: UIImageView! 21 | @IBOutlet weak var titleLabel: UILabel! 22 | @IBOutlet weak var subtitleLabel: UILabel! 23 | 24 | var viewModel: ViewModel? { 25 | didSet { 26 | channelImageView.image = viewModel?.image 27 | titleLabel.text = viewModel?.title 28 | subtitleLabel.text = viewModel?.subtitle 29 | } 30 | } 31 | 32 | override init(frame: CGRect) { 33 | super.init(frame: frame) 34 | commonInit() 35 | setupView() 36 | } 37 | 38 | required public init?(coder aDecoder: NSCoder) { 39 | super.init(coder: aDecoder) 40 | commonInit() 41 | setupView() 42 | } 43 | 44 | private func commonInit() { 45 | _ = fromNib() 46 | } 47 | 48 | private func setupView() { 49 | viewModel = ViewModel() 50 | channelImageView.layer.cornerRadius = 5 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Example/GridTimerView/Cells/ChannelView.xib: -------------------------------------------------------------------------------- 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 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 | -------------------------------------------------------------------------------- /Example/GridTimerView/Controllers/DetailViewController/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // GridTimerView_Example 4 | // 5 | // Created by Alberto Aznar de los Ríos on 7/9/18. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DetailViewController: UIViewController { 12 | 13 | @IBOutlet weak var titleLabel: UILabel! 14 | @IBOutlet weak var subtitleLabel: UILabel! 15 | 16 | var source: DetailViewSource? 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | titleLabel.text = source?.title 21 | subtitleLabel.text = source?.subtitle 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Example/GridTimerView/Controllers/DetailViewController/DetailViewController.xib: -------------------------------------------------------------------------------- 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 | 34 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 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 | -------------------------------------------------------------------------------- /Example/GridTimerView/Controllers/DetailViewController/DetailViewSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewSource.swift 3 | // GridTimerView_Example 4 | // 5 | // Created by Alberto Aznar de los Ríos on 7/9/18. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct DetailViewSource { 12 | var title: String? 13 | var subtitle: String? 14 | } 15 | -------------------------------------------------------------------------------- /Example/GridTimerView/Controllers/MainViewController/MainViewController+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController+Tools.swift 3 | // GridTimerView_Example 4 | // 5 | // Created by Alberto Aznar de los Ríos on 7/9/18. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension MainViewController { 12 | 13 | public func channelAt(_ index: Int) -> Channel? { 14 | return channels.element(at: index) 15 | } 16 | 17 | public func eventAt(_ indexPath: IndexPath) -> Event? { 18 | return channelAt(indexPath.section)?.events.element(at: indexPath.row) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Example/GridTimerView/Controllers/MainViewController/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 2/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import GridTimerView 11 | 12 | class MainViewController: UIViewController { 13 | 14 | @IBOutlet weak var gridTimerView: GridTimerView! 15 | 16 | public var channels = ChannelFactory.generateChannels(40, withShows: 0) 17 | private var firstLoad = false 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | setupNavigation() 22 | setupGridTimerView() 23 | } 24 | 25 | override func viewDidAppear(_ animated: Bool) { 26 | super.viewDidAppear(animated) 27 | } 28 | 29 | override func viewDidLayoutSubviews() { 30 | super.viewDidLayoutSubviews() 31 | if !firstLoad { 32 | let date = Date.add(days: -1).addingTimeInterval(60*60*3) 33 | gridTimerView?.scrollToDate(date: date) 34 | firstLoad = true 35 | } 36 | } 37 | 38 | @objc func didPressTodayButton(_ sender: UIButton) { 39 | gridTimerView?.scrollToDate(date: Date()) 40 | } 41 | 42 | private func setupNavigation() { 43 | let rightButtonItem = UIBarButtonItem.init( 44 | title: "Now", 45 | style: .done, 46 | target: self, 47 | action: #selector(didPressTodayButton) 48 | ) 49 | title = "GridTimerView" 50 | navigationItem.rightBarButtonItems = [rightButtonItem] 51 | navigationController?.navigationBar.tintColor = Colors.Fucsia 52 | } 53 | 54 | private func setupGridTimerView() { 55 | 56 | var configuration = GridTimerConfiguration() 57 | configuration.ruleTicksColor = Colors.White 58 | configuration.ruleBackgroundColor = Colors.Black 59 | configuration.timerColor = Colors.Fucsia 60 | configuration.lineColor = Colors.Fucsia 61 | configuration.selectedItemColor = Colors.Fucsia 62 | 63 | gridTimerView.configuration = configuration 64 | gridTimerView.dataSource = self 65 | gridTimerView.delegate = self 66 | } 67 | 68 | func simulateEventsRequest(forRow rowIndex: Int) { 69 | channels[rowIndex].loaded = true 70 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 71 | self.channels[rowIndex].events = ChannelFactory.generateEvents(200, inRow: rowIndex) 72 | self.gridTimerView.reloadGridRowIndex(rowIndex) 73 | } 74 | } 75 | } 76 | 77 | extension MainViewController: GridTimerViewDataSource { 78 | 79 | func numberOfRows(inGridTimerView gridTimerView: GridTimerView) -> Int { 80 | return channels.count 81 | } 82 | 83 | func heightForRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat { 84 | return 54.0 85 | } 86 | 87 | func heightForTimelineRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat { 88 | return 8.0 89 | } 90 | 91 | func gridTimerView(gridTimerView: GridTimerView, numberOfItemsAtRowIndex rowIndex: Int) -> Int { 92 | return channelAt(rowIndex)?.events.count ?? 0 93 | } 94 | 95 | func gridTimerView(gridTimerView: GridTimerView, viewForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIView { 96 | 97 | if !channels[rowIndex].loaded { 98 | simulateEventsRequest(forRow: rowIndex) 99 | } 100 | 101 | let channelView = ChannelView() 102 | let channel = channels[rowIndex] 103 | var viewModel = ChannelView.ViewModel() 104 | 105 | if channel.events.count > 0 { 106 | viewModel.title = channel.events[itemIndex].title 107 | viewModel.subtitle = channel.events[itemIndex].subtitle 108 | viewModel.image = channel.channelImage 109 | } 110 | 111 | channelView.viewModel = viewModel 112 | return channelView 113 | } 114 | 115 | func gridTimerView(gridTimerView: GridTimerView, startTimeForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date { 116 | let event = eventAt(IndexPath(item: itemIndex, section: rowIndex)) 117 | return event?.initTime ?? Date() 118 | } 119 | 120 | func gridTimerView(gridTimerView: GridTimerView, endTimeForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date { 121 | let event = eventAt(IndexPath(item: itemIndex, section: rowIndex)) 122 | return event?.endTime ?? Date() 123 | } 124 | 125 | func gridTimerView(gridTimerView: GridTimerView, colorForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIColor? { 126 | return itemIndex == 5 ? .green : nil 127 | } 128 | } 129 | 130 | extension MainViewController: GridTimerViewDelegate { 131 | 132 | func gridTimerView(gridTimerView: GridTimerView, didHighlightAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) { 133 | 134 | } 135 | 136 | func gridTimerView(gridTimerView: GridTimerView, didSelectRowAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) { 137 | 138 | let event = channels[rowIndex].events[itemIndex] 139 | let vc = DetailViewController() 140 | vc.source = DetailViewSource(title: event.title, subtitle: event.subtitle) 141 | navigationController?.pushViewController(vc, animated: true) 142 | } 143 | 144 | func didPullToRefresh(inGridTimerView gridTimerView: GridTimerView) { 145 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 146 | gridTimerView.endRefresh() 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Example/GridTimerView/Controllers/MainViewController/MainViewController.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Example/GridTimerView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Example/GridTimerView/Models/Channel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Section.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 2/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Channel { 12 | var events = [Event]() 13 | var channelImage = UIImage(named: "Placeholder") 14 | var loaded = false 15 | } 16 | -------------------------------------------------------------------------------- /Example/GridTimerView/Models/Event.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Item.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 2/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Event { 12 | var initTime: Date? 13 | var endTime: Date? 14 | var title = "" 15 | var subtitle = "" 16 | } 17 | -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "1024x1024", 53 | "idiom" : "ios-marketing", 54 | "filename" : "iTunesArtwork@2x.png", 55 | "scale" : "1x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-20@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-20@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_abc@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_abc@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_abc@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/channel_abc@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/channel_abc@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/channel_abc@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/channel_abc@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/channel_abc@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Abc.imageset/channel_abc@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_amc@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_amc@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_amc@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/channel_amc@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/channel_amc@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/channel_amc@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/channel_amc@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/channel_amc@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Amc.imageset/channel_amc@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_axn@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_axn@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_axn@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/channel_axn@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/channel_axn@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/channel_axn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/channel_axn@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/channel_axn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Axn.imageset/channel_axn@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_cnbc@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_cnbc@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_cnbc@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/channel_cnbc@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/channel_cnbc@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/channel_cnbc@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/channel_cnbc@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/channel_cnbc@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Cnbc.imageset/channel_cnbc@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_cnn@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_cnn@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_cnn@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/channel_cnn@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/channel_cnn@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/channel_cnn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/channel_cnn@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/channel_cnn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Cnn.imageset/channel_cnn@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_fox@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_fox@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_fox@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/channel_fox@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/channel_fox@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/channel_fox@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/channel_fox@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/channel_fox@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Fox.imageset/channel_fox@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_hbo@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_hbo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_hbo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/channel_hbo@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/channel_hbo@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/channel_hbo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/channel_hbo@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/channel_hbo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Hbo.imageset/channel_hbo@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_history@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_history@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_history@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/channel_history@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/channel_history@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/channel_history@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/channel_history@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/channel_history@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel History.imageset/channel_history@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_uni@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_uni@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_uni@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/channel_uni@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/channel_uni@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/channel_uni@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/channel_uni@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/channel_uni@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Uni.imageset/channel_uni@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "channel_xd@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "channel_xd@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "channel_xd@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/channel_xd@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/channel_xd@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/channel_xd@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/channel_xd@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/channel_xd@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Channel Xd.imageset/channel_xd@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Placeholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "placeholder-2.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "placeholder-3.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Placeholder.imageset/placeholder-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Placeholder.imageset/placeholder-2.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Placeholder.imageset/placeholder-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Placeholder.imageset/placeholder-3.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash_GridTimerView_@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "splash_GridTimerView_@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "splash_GridTimerView@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/splash_GridTimerView@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/splash_GridTimerView@3x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/splash_GridTimerView_@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/splash_GridTimerView_@1x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/splash_GridTimerView_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Example/GridTimerView/Resources/Images.xcassets/Splash.imageset/splash_GridTimerView_@2x.png -------------------------------------------------------------------------------- /Example/GridTimerView/Utils/Constants/Colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Colors.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 5/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /* 12 | ------------------------------------------------------------------- 13 | Colors 14 | -------------------------------------------------------------------- 15 | let sample = UIColor(hex: "20232A") 16 | let sample = UIColor(red: 40/255, green: 40/255, blue: 40/255, alpha: 1) 17 | **/ 18 | 19 | enum Colors 20 | { 21 | static let Green = UIColor(hex: "35d7b7") 22 | static let Black = UIColor(hex: "2B2F3A") 23 | static let Fucsia = UIColor(hex: "ED3269") 24 | static let White = UIColor(hex: "F7F8FA") 25 | static let White2 = UIColor(hex: "EEEEEE") 26 | } 27 | -------------------------------------------------------------------------------- /Example/GridTimerView/Utils/Extensions/Array+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 09/05/2018. 6 | // Copyright (c) 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Array { 12 | 13 | public func element(at index: Int) -> Element? { 14 | return index < self.count && index >= 0 ? self[index] : nil 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/GridTimerView/Utils/Extensions/UIColor+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 5/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | convenience init(hex: String) { 14 | let scanner = Scanner(string: hex) 15 | scanner.scanLocation = 0 16 | var rgbValue: UInt64 = 0 17 | scanner.scanHexInt64(&rgbValue) 18 | 19 | let r = (rgbValue & 0xff0000) >> 16 20 | let g = (rgbValue & 0xff00) >> 8 21 | let b = rgbValue & 0xff 22 | 23 | self.init( 24 | red: CGFloat(r) / 0xff, 25 | green: CGFloat(g) / 0xff, 26 | blue: CGFloat(b) / 0xff, alpha: 1 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Example/GridTimerView/Utils/Extensions/UIView+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Tools.swift 3 | // GridTimerView_Example 4 | // 5 | // Created by Alberto Aznar de los Ríos on 28/9/18. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | func fromNib() -> T? { 14 | guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else { 15 | // xib not loaded, or its top view is of the wrong type 16 | return nil 17 | } 18 | self.addSubview(contentView) 19 | contentView.translatesAutoresizingMaskIntoConstraints = false 20 | contentView.fixConstraintsInView(self) 21 | return contentView 22 | } 23 | 24 | func fixConstraintsInView(_ container: UIView!) -> Void { 25 | NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true 26 | NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true 27 | NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1.0, constant: 0).isActive = true 28 | NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Example/GridTimerView/Utils/Globals/Globals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Globals.swift 3 | // GridTimerView_Example 4 | // 5 | // Created by Alberto Aznar de los Ríos on 7/9/18. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.4' 2 | use_frameworks! 3 | 4 | target 'GridTimerView_Example' do 5 | pod 'GridTimerView', :path => '../' 6 | 7 | target 'GridTimerView_Tests' do 8 | inherit! :search_paths 9 | 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GridTimerView (0.1.7) 3 | 4 | DEPENDENCIES: 5 | - GridTimerView (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | GridTimerView: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | GridTimerView: de82111c4fbdabeeb078c850f461fe8d5900d165 13 | 14 | PODFILE CHECKSUM: 726095825b5b8d655e2a7b7b341cfa300d7e7b5d 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/GridTimerView.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GridTimerView", 3 | "platforms": { 4 | "ios": "11.4" 5 | }, 6 | "summary": "GridTimerView shows a schedule with timer controller. Each cell can manage multiple events. Used for listing TV programs shows in a table.", 7 | "requires_arc": true, 8 | "version": "0.1.7", 9 | "license": { 10 | "type": "MIT", 11 | "file": "LICENSE" 12 | }, 13 | "authors": { 14 | "Alberto Aznar": "info@alberdev.com" 15 | }, 16 | "homepage": "https://github.com/alberdev/GridTimerView", 17 | "source": { 18 | "git": "https://github.com/alberdev/GridTimerView.git", 19 | "tag": "0.1.7" 20 | }, 21 | "frameworks": "UIKit", 22 | "source_files": "GridTimerView/**/*.{swift}", 23 | "resources": "GridTimerView/**/*.{png,jpeg,jpg,storyboard,xib,xcassets}", 24 | "swift_version": "4.2" 25 | } 26 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GridTimerView (0.1.7) 3 | 4 | DEPENDENCIES: 5 | - GridTimerView (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | GridTimerView: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | GridTimerView: de82111c4fbdabeeb078c850f461fe8d5900d165 13 | 14 | PODFILE CHECKSUM: 726095825b5b8d655e2a7b7b341cfa300d7e7b5d 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/GridTimerView/GridTimerView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_GridTimerView : NSObject 3 | @end 4 | @implementation PodsDummy_GridTimerView 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/GridTimerView/GridTimerView-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/GridTimerView/GridTimerView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double GridTimerViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char GridTimerViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/GridTimerView/GridTimerView.modulemap: -------------------------------------------------------------------------------- 1 | framework module GridTimerView { 2 | umbrella header "GridTimerView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/GridTimerView/GridTimerView.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = -framework "UIKit" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/GridTimerView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.7 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## GridTimerView 5 | 6 | Copyright (c) 2018 Alberto Aznar 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2018 Alberto Aznar <info@alberdev.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | GridTimerView 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GridTimerView_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GridTimerView_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/GridTimerView/GridTimerView.framework" 147 | fi 148 | if [[ "$CONFIGURATION" == "Release" ]]; then 149 | install_framework "${BUILT_PRODUCTS_DIR}/GridTimerView/GridTimerView.framework" 150 | fi 151 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 152 | wait 153 | fi 154 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_GridTimerView_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_GridTimerView_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView/GridTimerView.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "GridTimerView" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_GridTimerView_Example { 2 | umbrella header "Pods-GridTimerView_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Example/Pods-GridTimerView_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView/GridTimerView.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "GridTimerView" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GridTimerView_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GridTimerView_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_GridTimerView_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_GridTimerView_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView/GridTimerView.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_GridTimerView_Tests { 2 | umbrella header "Pods-GridTimerView_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-GridTimerView_Tests/Pods-GridTimerView_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GridTimerView/GridTimerView.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /GridTimerView.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | 4 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 5 | 6 | s.platform = :ios 7 | s.ios.deployment_target = '11' 8 | s.name = "GridTimerView" 9 | s.summary = "GridTimerView shows a schedule with timer controller. Each cell can manage multiple events. Used for listing TV programs shows in a table." 10 | s.requires_arc = true 11 | s.version = "1.0.1" 12 | 13 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 14 | 15 | s.license = { :type => "MIT", :file => "LICENSE" } 16 | 17 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 18 | 19 | s.author = { "Alberto Aznar" => "info@alberdev.com" } 20 | s.homepage = "https://github.com/alberdev/GridTimerView" 21 | 22 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 23 | 24 | s.source = { :git => "https://github.com/alberdev/GridTimerView.git", 25 | :tag => "#{s.version}" } 26 | 27 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 28 | 29 | s.framework = "UIKit" 30 | # s.dependency 'Alamofire', '~> 4.7' 31 | # s.dependency 'MBProgressHUD', '~> 1.1.0' 32 | 33 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 34 | 35 | s.source_files = "GridTimerView/**/*.{swift}" 36 | 37 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 38 | 39 | s.resources = "GridTimerView/**/*.{png,jpeg,jpg,storyboard,xib,xcassets}" 40 | 41 | # ――― Swift Version ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 42 | 43 | s.swift_version = "4.2" 44 | 45 | end 46 | -------------------------------------------------------------------------------- /GridTimerView/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/GridTimerView/Assets/.gitkeep -------------------------------------------------------------------------------- /GridTimerView/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/GridTimerView/Classes/.gitkeep -------------------------------------------------------------------------------- /GridTimerView/Classes/CustomCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomCollectionViewLayout.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 5/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CustomCollectionViewLayoutDataSource: class { 12 | // func initDateForIndexPath(indexPath: IndexPath) -> Date? 13 | // func endDateForIndexPath(indexPath: IndexPath) -> Date? 14 | // func cellHeaderHeight() -> CGFloat? 15 | func cellItemHeight() -> CGFloat? 16 | } 17 | 18 | class CustomCollectionViewLayout: UICollectionViewFlowLayout { 19 | 20 | weak var dataSource: CustomCollectionViewLayoutDataSource? 21 | 22 | private var cellItemsAttributes = Dictionary() 23 | private var contentSize = CGSize(width: 0, height: 0) 24 | private var screenSize = UIScreen.main.bounds.size 25 | 26 | var cellItemHeight: CGFloat = 76.0 27 | var reloadAttributes = true 28 | var ruleDaysFrom = 1 29 | var ruleDaysTo = 2 30 | 31 | override func prepare() { 32 | 33 | guard reloadAttributes else { return } 34 | guard let collectionView = collectionView else { return } 35 | 36 | reloadAttributes = false 37 | 38 | // Clear cache 39 | cellItemsAttributes = Dictionary() 40 | 41 | cellItemHeight = dataSource?.cellItemHeight() ?? cellItemHeight 42 | 43 | let numberOfItems = collectionView.numberOfItems(inSection: 0) 44 | let contentWidth: Double = Double(ruleWidth) * 24 * Double(ruleDaysTo + ruleDaysFrom) 45 | let contentHeight: Double = Double(numberOfItems) * Double(cellItemHeight) 46 | var yPos: CGFloat = 0 47 | 48 | contentSize = CGSize(width: contentWidth, height: contentHeight) 49 | 50 | if numberOfItems > 0 { 51 | for i in 0 ..< numberOfItems { 52 | let itemIndexPath = IndexPath(item: i, section: 0) 53 | let attributes = UICollectionViewLayoutAttributes(forCellWith: itemIndexPath) 54 | attributes.frame = CGRect(x: 0, y: yPos, width: screenSize.width, height: cellItemHeight) 55 | cellItemsAttributes[itemIndexPath] = attributes 56 | yPos += cellItemHeight 57 | } 58 | } 59 | } 60 | 61 | override var collectionViewContentSize: CGSize { 62 | return contentSize 63 | } 64 | 65 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 66 | 67 | var attributesInRect = [UICollectionViewLayoutAttributes]() 68 | guard let collectionView = collectionView else { return attributesInRect } 69 | 70 | for attributes in cellItemsAttributes.values { 71 | attributes.frame.origin.x = collectionView.contentOffset.x 72 | if rect.intersects(attributes.frame) { 73 | attributesInRect.append(attributes) 74 | } 75 | } 76 | 77 | return attributesInRect 78 | } 79 | 80 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 81 | // We have to return true here so that the layout attributes would be recalculated 82 | // everytime we scroll the collection view. 83 | return true 84 | } 85 | } 86 | 87 | //class CustomCollectionViewLayout: UICollectionViewFlowLayout { 88 | // 89 | // weak var dataSource: CustomCollectionViewLayoutDataSource? 90 | // 91 | // var cellItemHeight: CGFloat = 8.0 92 | // var cellHeaderHeight: CGFloat = 54.0 93 | // var cellSeparation: CGFloat = 10.0 94 | // var ruleDaysFrom = 1 95 | // var ruleDaysTo = 2 96 | // 97 | // private var cellItemsAttributes = Dictionary() 98 | // private var cellHeadersAttributes = Dictionary() 99 | // private var contentSize = CGSize(width: 0, height: 0) 100 | // private var screenSize = UIScreen.main.bounds.size 101 | // var reloadAttributes = true 102 | // 103 | // override var collectionViewContentSize: CGSize { 104 | // return contentSize 105 | // } 106 | // 107 | // override func prepare() { 108 | // 109 | // guard reloadAttributes else { return } 110 | // guard let collectionView = collectionView else { return } 111 | // 112 | // reloadAttributes = false 113 | // 114 | // // Clear cache 115 | // cellItemsAttributes = Dictionary() 116 | // cellHeadersAttributes = Dictionary() 117 | // 118 | // cellItemHeight = dataSource?.cellItemHeight() ?? cellItemHeight 119 | // cellHeaderHeight = dataSource?.cellHeaderHeight() ?? cellHeaderHeight 120 | // 121 | // let numberOfSections = collectionView.numberOfSections 122 | // let contentWidth: Double = Double(ruleWidth) * 24 * Double(ruleDaysTo + ruleDaysFrom) 123 | // let contentHeight: Double = Double(numberOfSections) * Double(cellHeaderHeight + cellSeparation + cellItemHeight) 124 | // var yPos: CGFloat = 0 125 | // 126 | // contentSize = CGSize(width: contentWidth, height: contentHeight) 127 | // 128 | // guard numberOfSections > 0 else { return } 129 | // for s in 0 ..< numberOfSections { 130 | // 131 | // let headerIndexPath = IndexPath(item: 0, section: s) 132 | // let headerAttributes = self.headerAttributes(forIndexPath: headerIndexPath, position: CGPoint(x: 0.0, y: yPos)) 133 | // cellHeadersAttributes[headerIndexPath] = headerAttributes 134 | // 135 | // let numberOfItems = collectionView.numberOfItems(inSection: s) 136 | // if numberOfItems > 0 { 137 | // for i in 0 ..< numberOfItems { 138 | // let itemIndexPath = IndexPath(item: i, section: s) 139 | // let itemAttributes = self.itemAttributes(forIndexPath: itemIndexPath, yPosition: yPos) 140 | // cellItemsAttributes[itemIndexPath] = itemAttributes 141 | // } 142 | // } 143 | // yPos += cellHeaderHeight + cellSeparation + cellItemHeight 144 | // } 145 | // } 146 | // 147 | // override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 148 | // 149 | // var attributesInRect = [UICollectionViewLayoutAttributes]() 150 | // 151 | // for attributes in cellItemsAttributes.values { 152 | // if rect.intersects(attributes.frame) { 153 | // attributesInRect.append(attributes) 154 | // } 155 | // } 156 | // for attributes in cellHeadersAttributes.values { 157 | // if let collectionView = collectionView { 158 | // attributes.frame.origin.x = collectionView.contentOffset.x 159 | // if rect.intersects(attributes.frame) { 160 | // attributesInRect.append(attributes) 161 | // } 162 | // } 163 | // } 164 | // 165 | // return attributesInRect 166 | // } 167 | // 168 | // override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 169 | // // We have to return true here so that the layout attributes would be recalculated 170 | // // everytime we scroll the collection view. 171 | // return true 172 | // } 173 | // 174 | // // ----------------------------------------------------------------------- 175 | // // Private 176 | // // ----------------------------------------------------------------------- 177 | // 178 | // private func headerAttributes(forIndexPath indexPath: IndexPath, position: CGPoint) -> UICollectionViewLayoutAttributes { 179 | // 180 | // let attributes = UICollectionViewLayoutAttributes( 181 | // forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, 182 | // with: indexPath) 183 | // 184 | // attributes.frame = CGRect(x: position.x, y: position.y + cellItemHeight, width: screenSize.width, height: cellHeaderHeight) 185 | // return attributes 186 | // } 187 | // 188 | // private func itemAttributes(forIndexPath indexPath: IndexPath, yPosition: CGFloat) -> UICollectionViewLayoutAttributes? { 189 | // 190 | // guard 191 | // let initDate = dataSource?.initDateForIndexPath(indexPath: indexPath), 192 | // let endDate = dataSource?.endDateForIndexPath(indexPath: indexPath) 193 | // else { return nil } 194 | // 195 | // let initTime = initDate.timeIntervalSince1970 196 | // let endTime = endDate.timeIntervalSince1970 197 | // let cellTimeDuration = Int(endTime - initTime) 198 | // let cellItemWidth = floatForTime(seconds: cellTimeDuration) 199 | // let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) 200 | // let xPos = xPosition(byDate: initDate, fromInitDate: Date.add(days: -ruleDaysFrom)) 201 | // attributes.frame = CGRect(x: xPos, y: yPosition, width: cellItemWidth, height: cellItemHeight) 202 | // return attributes 203 | // } 204 | //} 205 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Extensions/Date+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 3/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Date { 12 | 13 | static func generateFrom(text: String?, format: String = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") -> Date? { 14 | guard let text = text else { return nil } 15 | let dateFormatter = DateFormatter() 16 | dateFormatter.dateFormat = format 17 | return dateFormatter.date(from: text) 18 | } 19 | 20 | func format(dateFormat: String) -> String { 21 | let dateFormatter = DateFormatter() 22 | dateFormatter.dateFormat = dateFormat 23 | return dateFormatter.string(from: self) 24 | } 25 | } 26 | 27 | extension Date { 28 | 29 | var dayNumber: Int { 30 | let cal = NSCalendar.current 31 | let component = cal.component(.day, from: self) 32 | return Int(component) 33 | } 34 | 35 | var year: Int { 36 | let cal = NSCalendar.current 37 | let component = cal.component(.year, from: self) 38 | return Int(component) 39 | } 40 | 41 | static public func today() -> Date { 42 | let cal = NSCalendar.current 43 | let components = cal.dateComponents([.year, .month, .day], from: Date()) 44 | let today = cal.date(from: components) 45 | return today! 46 | } 47 | 48 | static public func add(days: Int) -> Date { 49 | return Date.today().addingTimeInterval(days.days) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Extensions/Int+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 3/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Int { 12 | 13 | var days: TimeInterval { 14 | let DAY_IN_SECONDS = 60 * 60 * 24 15 | let days:Double = Double(DAY_IN_SECONDS) * Double(self) 16 | return days 17 | } 18 | 19 | func date() -> Date { 20 | return Date(timeIntervalSince1970: TimeInterval(self)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Extensions/UICollectionView+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionView+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar de los Ríos on 28/9/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UICollectionView { 11 | 12 | func registerInClass(_ className: AnyClass, forCellWithReuseIdentifier identifier: String) { 13 | self.register(UINib(nibName: identifier, bundle: Bundle(for: className)), forCellWithReuseIdentifier: identifier) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Extensions/UICollectionViewCell+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewCell+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 2/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UICollectionViewCell { 12 | 13 | public static var uniqueIdentifier: String { 14 | return String(describing: self) 15 | } 16 | 17 | public var uniqueIdentifier: String { 18 | return type(of: self).uniqueIdentifier 19 | } 20 | 21 | public static var hasNib: Bool { 22 | return Bundle.main.path(forResource: self.uniqueIdentifier, ofType: "nib") != nil 23 | } 24 | 25 | public static var nib: UINib { 26 | return UINib(nibName: self.uniqueIdentifier, bundle: nil) 27 | } 28 | 29 | // MARK: - cells 30 | 31 | public static func register(nibFor collectionView: UICollectionView) { 32 | collectionView.register(self.nib, forCellWithReuseIdentifier: self.uniqueIdentifier) 33 | } 34 | 35 | public static func register(classFor collectionView: UICollectionView) { 36 | collectionView.register(self, forCellWithReuseIdentifier: self.uniqueIdentifier) 37 | } 38 | 39 | public static func register(itemFor collectionView: UICollectionView) { 40 | if self.hasNib { 41 | return self.register(nibFor: collectionView) 42 | } 43 | self.register(classFor: collectionView) 44 | } 45 | 46 | public static func reuse(_ collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { 47 | return collectionView.dequeueReusableCell(withReuseIdentifier: self.uniqueIdentifier, for: indexPath) 48 | } 49 | 50 | // MARK: - supplementary views 51 | 52 | public static func register(nibFor collectionView: UICollectionView, kind: String) { 53 | collectionView.register(self.nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: self.uniqueIdentifier) 54 | } 55 | 56 | public static func register(classFor collectionView: UICollectionView, kind: String) { 57 | collectionView.register(self, forSupplementaryViewOfKind: kind, withReuseIdentifier: self.uniqueIdentifier) 58 | } 59 | 60 | public static func register(itemFor collectionView: UICollectionView, kind: String) { 61 | if self.hasNib { 62 | return self.register(nibFor: collectionView, kind: kind) 63 | } 64 | self.register(classFor: collectionView, kind: kind) 65 | } 66 | 67 | public static func reuse(_ collectionView: UICollectionView, indexPath: IndexPath, kind: String) -> UICollectionReusableView { 68 | return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: self.uniqueIdentifier, for: indexPath) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Extensions/UIImage+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 5/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | 13 | func maskWithColor(color: UIColor) -> UIImage? { 14 | let maskImage = cgImage! 15 | 16 | let width = size.width 17 | let height = size.height 18 | let bounds = CGRect(x: 0, y: 0, width: width, height: height) 19 | 20 | let colorSpace = CGColorSpaceCreateDeviceRGB() 21 | let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) 22 | let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)! 23 | 24 | context.clip(to: bounds, mask: maskImage) 25 | context.setFillColor(color.cgColor) 26 | context.fill(bounds) 27 | 28 | if let cgImage = context.makeImage() { 29 | let coloredImage = UIImage(cgImage: cgImage) 30 | return coloredImage 31 | } else { 32 | return nil 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Extensions/UIView+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Tools.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 4/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public let kShapeDashed : String = "kShapeDashed" 12 | 13 | extension UIView { 14 | 15 | func fromNib() -> T? { 16 | guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else { 17 | // xib not loaded, or its top view is of the wrong type 18 | return nil 19 | } 20 | self.addSubview(contentView) 21 | contentView.translatesAutoresizingMaskIntoConstraints = false 22 | contentView.fixConstraintsInView(self) 23 | return contentView 24 | } 25 | 26 | func fixConstraintsInView(_ container: UIView!) -> Void { 27 | NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true 28 | NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true 29 | NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1.0, constant: 0).isActive = true 30 | NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true 31 | } 32 | 33 | func removeDashedBorder(_ view: UIView) { 34 | view.layer.sublayers?.forEach { 35 | if kShapeDashed == $0.name { 36 | $0.removeFromSuperlayer() 37 | } 38 | } 39 | } 40 | 41 | func addDashedBorder(width: CGFloat? = nil, height: CGFloat? = nil, lineWidth: CGFloat = 2, lineDashPattern:[NSNumber]? = [6,3], strokeColor: UIColor = UIColor.red, fillColor: UIColor = UIColor.clear) { 42 | 43 | var fWidth: CGFloat? = width 44 | var fHeight: CGFloat? = height 45 | 46 | if fWidth == nil { 47 | fWidth = self.frame.width 48 | } 49 | 50 | if fHeight == nil { 51 | fHeight = self.frame.height 52 | } 53 | 54 | let shapeLayer:CAShapeLayer = CAShapeLayer() 55 | 56 | let shapeRect = CGRect(x: 0, y: 0, width: fWidth!, height: fHeight!) 57 | 58 | shapeLayer.bounds = shapeRect 59 | shapeLayer.position = CGPoint(x: fWidth!/2, y: fHeight!/2) 60 | shapeLayer.fillColor = fillColor.cgColor 61 | shapeLayer.strokeColor = strokeColor.cgColor 62 | shapeLayer.lineWidth = lineWidth 63 | shapeLayer.lineJoin = CAShapeLayerLineJoin.round 64 | shapeLayer.lineDashPattern = lineDashPattern 65 | shapeLayer.name = kShapeDashed 66 | shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 5).cgPath 67 | 68 | self.layer.addSublayer(shapeLayer) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /GridTimerView/Classes/Globals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Globals.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar de los Ríos on 9/9/18. 6 | // 7 | 8 | import Foundation 9 | 10 | let ruleWidth: CGFloat = 96.05 // in 3600s 11 | 12 | func xPosition(byDate date: Date, fromInitDate initDate: Date) -> CGFloat { 13 | let ruleInitialDate = initDate.timeIntervalSince1970 14 | let eventInitialDate = date.timeIntervalSince1970 15 | let secondForDifference = Int(eventInitialDate - ruleInitialDate) 16 | return floatForTime(seconds: secondForDifference) 17 | } 18 | 19 | func floatForTime(seconds: Int) -> CGFloat { 20 | let pointsPerSecond: CGFloat = CGFloat(ruleWidth)/3600 21 | return CGFloat(seconds) * pointsPerSecond 22 | } 23 | 24 | func printLayoutTrace(view: UIView) { 25 | print(view.value(forKey: "_autolayoutTrace") ?? "No layout trace") 26 | } 27 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridItemCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridItemCollectionViewLayout.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar de los Ríos on 28/9/18. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol GridItemCollectionViewLayoutDataSource: class { 11 | func initDateForIndexPath(indexPath: IndexPath) -> Date? 12 | func endDateForIndexPath(indexPath: IndexPath) -> Date? 13 | func cellItemHeight() -> CGFloat? 14 | } 15 | 16 | class GridItemCollectionViewLayout: UICollectionViewFlowLayout { 17 | 18 | weak var dataSource: GridItemCollectionViewLayoutDataSource? 19 | 20 | private var cellItemsAttributes = Dictionary() 21 | private var contentSize = CGSize(width: 0, height: 0) 22 | private var screenSize = UIScreen.main.bounds.size 23 | 24 | var cellItemHeight: CGFloat = 8.0 25 | var ruleDaysFrom = 1 26 | var ruleDaysTo = 2 27 | // var reloadAttributes = true 28 | 29 | override func prepare() { 30 | 31 | // guard reloadAttributes else { return } 32 | guard let collectionView = collectionView else { return } 33 | 34 | // reloadAttributes = false 35 | // Clear cache 36 | cellItemsAttributes = Dictionary() 37 | 38 | cellItemHeight = dataSource?.cellItemHeight() ?? cellItemHeight 39 | 40 | let numberOfItems = collectionView.numberOfItems(inSection: 0) 41 | let contentWidth: Double = Double(ruleWidth) * 24 * Double(ruleDaysTo + ruleDaysFrom) 42 | let contentHeight: Double = Double(cellItemHeight) 43 | 44 | contentSize = CGSize(width: contentWidth, height: contentHeight) 45 | 46 | if numberOfItems > 0 { 47 | for i in 0 ..< numberOfItems { 48 | let itemIndexPath = IndexPath(item: i, section: 0) 49 | let attributes = self.itemAttributes(forIndexPath: itemIndexPath) 50 | cellItemsAttributes[itemIndexPath] = attributes 51 | } 52 | } 53 | } 54 | 55 | override var collectionViewContentSize: CGSize { 56 | return contentSize 57 | } 58 | 59 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 60 | var attributesInRect = [UICollectionViewLayoutAttributes]() 61 | 62 | for attributes in cellItemsAttributes.values { 63 | if rect.intersects(attributes.frame) { 64 | attributesInRect.append(attributes) 65 | } 66 | } 67 | 68 | return attributesInRect 69 | } 70 | 71 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 72 | // We have to return true here so that the layout attributes would be recalculated 73 | // everytime we scroll the collection view. 74 | return false 75 | } 76 | 77 | // ----------------------------------------------------------------------- 78 | // Private 79 | // ----------------------------------------------------------------------- 80 | 81 | private func itemAttributes(forIndexPath indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 82 | 83 | guard 84 | let initDate = dataSource?.initDateForIndexPath(indexPath: indexPath), 85 | let endDate = dataSource?.endDateForIndexPath(indexPath: indexPath) 86 | else { return nil } 87 | 88 | let initTime = initDate.timeIntervalSince1970 89 | let endTime = endDate.timeIntervalSince1970 90 | let cellTimeDuration = Int(endTime - initTime) 91 | let cellItemWidth = floatForTime(seconds: cellTimeDuration) 92 | let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) 93 | let xPos = xPosition(byDate: initDate, fromInitDate: Date.add(days: -ruleDaysFrom)) 94 | attributes.frame = CGRect(x: xPos, y: 0, width: cellItemWidth, height: cellItemHeight) 95 | return attributes 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridItemViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderCollectionViewCell.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 5/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol GridItemViewCellDataSource: class { 12 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, numberOfItemsInRowIndex rowIndex: Int) -> Int 13 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, colorForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIColor? 14 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, containerViewForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIView? 15 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, initDateForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date? 16 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, endDateForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date? 17 | } 18 | 19 | protocol GridItemViewCellDelegate: class { 20 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, didHighlightAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) 21 | } 22 | 23 | public class GridItemViewCell: UICollectionViewCell { 24 | 25 | struct ViewModel { 26 | var selectedCellColor: UIColor 27 | var selectedItemColor: UIColor 28 | var unselectedItemColor: UIColor 29 | var containerViewHeight: CGFloat 30 | var collectionViewHeight: CGFloat 31 | var item: Int 32 | var row: Int 33 | } 34 | 35 | weak var dataSource: GridItemViewCellDataSource? 36 | weak var delegate: GridItemViewCellDelegate? 37 | 38 | @IBOutlet weak var collectionView: UICollectionView! 39 | @IBOutlet weak var containerView: UIView! 40 | @IBOutlet weak var containerViewHeightConstraint: NSLayoutConstraint! 41 | @IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint! 42 | 43 | private let screenSize = UIScreen.main.bounds.size 44 | 45 | var userView: UIView? 46 | 47 | var viewModel: ViewModel? { 48 | didSet { 49 | guard let viewModel = viewModel else { return } 50 | containerViewHeightConstraint.constant = viewModel.containerViewHeight 51 | collectionViewHeightConstraint.constant = viewModel.collectionViewHeight 52 | } 53 | } 54 | 55 | open override func awakeFromNib() { 56 | collectionView.insetsLayoutMarginsFromSafeArea = false 57 | collectionView.dataSource = self 58 | collectionView.register(GridTimeLineView.self, forCellWithReuseIdentifier: GridTimeLineView.uniqueIdentifier) 59 | 60 | if let collectionViewLayout = collectionView.collectionViewLayout as? GridItemCollectionViewLayout { 61 | collectionViewLayout.dataSource = self 62 | } 63 | } 64 | 65 | func addCustomUserView(view: UIView) { 66 | view.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: containerView.bounds.height) 67 | containerView.addSubview(view) 68 | userView = view 69 | } 70 | 71 | func removeCustomUserView() { 72 | userView?.removeFromSuperview() 73 | } 74 | 75 | internal func updateHighlightedItems() { 76 | for cell in collectionView.visibleCells as! [GridTimeLineView] { 77 | // let timerLineFrame = convert(CGRect(x: screenSize.width/2, y: 0, width: 1, height: screenSize.height), to: collectionView) 78 | // if cell.frame.intersects(timerLineFrame) { 79 | 80 | let initPoint = cell.frame.origin.x 81 | let endPoint = cell.frame.origin.x + cell.frame.size.width 82 | let timerLineCenter = CGPoint(x: screenSize.width/2, y: 0) 83 | let timerLinePoint = convert(timerLineCenter, to: collectionView) 84 | if initPoint < timerLinePoint.x && endPoint >= timerLinePoint.x { 85 | 86 | guard 87 | let indexPath = collectionView.indexPath(for: cell), 88 | cell.isOn == false 89 | else { return } 90 | 91 | cell.isOn = true 92 | viewModel?.item = indexPath.item 93 | delegate?.gridItemViewCell(self, didHighlightAtItemIndex: indexPath.item, inRowIndex: viewModel?.row ?? 0) 94 | 95 | // Update cell with highlighted item 96 | guard let view = dataSource?.gridItemViewCell(self, containerViewForItemIndex: indexPath.item, inRowIndex: viewModel?.row ?? 0) else { return } 97 | removeCustomUserView() 98 | addCustomUserView(view: view) 99 | 100 | } else { 101 | cell.isOn = false 102 | } 103 | } 104 | } 105 | 106 | override public var isHighlighted: Bool { 107 | didSet { 108 | if isHighlighted { 109 | containerView.backgroundColor = viewModel?.selectedCellColor 110 | } else { 111 | containerView.backgroundColor = .white 112 | } 113 | } 114 | } 115 | } 116 | 117 | extension GridItemViewCell: UICollectionViewDataSource { 118 | 119 | public func numberOfSections(in collectionView: UICollectionView) -> Int { 120 | return 1 121 | } 122 | 123 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 124 | return dataSource?.gridItemViewCell(self, numberOfItemsInRowIndex: viewModel?.row ?? 0) ?? 0 125 | } 126 | 127 | public func collectionView(_ collectionView: UICollectionView, 128 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 129 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: GridTimeLineView.uniqueIdentifier, for: indexPath) as! GridTimeLineView 130 | cell.layer.borderColor = UIColor.white.cgColor 131 | cell.layer.borderWidth = 0.5 132 | cell.selectedItemColor = dataSource?.gridItemViewCell(self, colorForItemIndex: indexPath.item, inRowIndex: viewModel?.row ?? 0) ?? viewModel?.selectedItemColor 133 | cell.unselectedItemColor = viewModel?.unselectedItemColor 134 | cell.isOn = false 135 | return cell 136 | } 137 | } 138 | 139 | extension GridItemViewCell: GridItemCollectionViewLayoutDataSource { 140 | 141 | func initDateForIndexPath(indexPath: IndexPath) -> Date? { 142 | return dataSource?.gridItemViewCell(self, initDateForItemIndex: indexPath.item, inRowIndex: viewModel?.row ?? 0) 143 | } 144 | 145 | func endDateForIndexPath(indexPath: IndexPath) -> Date? { 146 | return dataSource?.gridItemViewCell(self, endDateForItemIndex: indexPath.item, inRowIndex: viewModel?.row ?? 0) 147 | } 148 | 149 | func cellItemHeight() -> CGFloat? { 150 | return collectionViewHeightConstraint.constant 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridItemViewCell.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimeLineView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridTimeLineView.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar de los Ríos on 10/9/18. 6 | // 7 | 8 | import UIKit 9 | 10 | public class GridTimeLineView: UICollectionViewCell { 11 | 12 | var selectedItemColor: UIColor? 13 | var unselectedItemColor: UIColor? 14 | 15 | /// Is true when cell is highlighted 16 | /// Used only to save the current state 17 | var isOn = false { 18 | didSet { 19 | backgroundColor = isOn ? selectedItemColor : unselectedItemColor 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimerConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Configuration.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar de los Ríos on 6/9/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct GridTimerConfiguration { 11 | 12 | /// Font for timer labels in rule 13 | public var ruleFont = UIFont.systemFont(ofSize: 10, weight: .semibold) 14 | 15 | /// Color for timer labels in rule 16 | public var ruleTextColor = UIColor.lightGray 17 | 18 | /// Days before today for initial time 19 | public var ruleDaysFrom = 1 20 | 21 | /// Days after today for end time 22 | public var ruleDaysTo = 2 23 | 24 | /// Rule background color 25 | public var ruleBackgroundColor = UIColor.darkGray 26 | 27 | /// Rule ticks color 28 | public var ruleTicksColor = UIColor.white 29 | 30 | /// Font used in current time 31 | public var timerFont = UIFont.systemFont(ofSize: 12, weight: .semibold) 32 | 33 | /// Background color used in current time 34 | public var timerColor = UIColor.blue 35 | 36 | /// Text color used in current time 37 | public var timerTextColor = UIColor.white 38 | 39 | /// Selected date line color 40 | public var lineColor = UIColor.blue 41 | 42 | /// Current date line color 43 | public var currentTimeLineColor = UIColor.blue 44 | 45 | /// Current date line color 46 | public var currentTimeLineDashed = false 47 | 48 | /// Selected highlight color on event 49 | public var selectedItemColor = UIColor.blue 50 | 51 | /// Unselected color on event 52 | public var unselectedItemColor = UIColor.lightGray 53 | 54 | /// Selected highlight color when row cell touched 55 | public var selectedColorOnTouch = UIColor.init(red: 0.95, green: 0.95, blue: 0.95, alpha: 1) 56 | 57 | /// Row separation 58 | public var rowSeparation: CGFloat = 10.0 59 | 60 | /// Enable refresh 61 | public var enableRefresh: Bool = false 62 | 63 | public init() {} 64 | } 65 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimerView+DataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridTimerView+DataSource.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 4/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension GridTimerView: UICollectionViewDataSource { 12 | 13 | public func numberOfSections(in collectionView: UICollectionView) -> Int { 14 | return 1 15 | } 16 | 17 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 18 | return dataSource?.numberOfRows(inGridTimerView: self) ?? 0 19 | } 20 | 21 | public func collectionView(_ collectionView: UICollectionView, 22 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 23 | 24 | guard 25 | let dataSource = dataSource, 26 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: GridItemViewCell.uniqueIdentifier, for: indexPath) as? GridItemViewCell 27 | else { 28 | fatalError("Custom item view register is needed! Register your custom item view once GridTimerView is initializated\n") 29 | } 30 | 31 | // TODO: Increase performance 32 | let view = dataSource.gridTimerView(gridTimerView: self, viewForItemIndex: 0, inRowIndex: indexPath.item) 33 | cell.removeCustomUserView() 34 | cell.addCustomUserView(view: view) 35 | 36 | let viewModel = GridItemViewCell.ViewModel( 37 | selectedCellColor: configuration.selectedColorOnTouch, 38 | selectedItemColor: configuration.selectedItemColor, 39 | unselectedItemColor: configuration.unselectedItemColor, 40 | containerViewHeight: dataSource.heightForRow(inGridTimerView: self), 41 | collectionViewHeight: dataSource.heightForTimelineRow(inGridTimerView: self), 42 | item: 0, 43 | row: indexPath.item) 44 | 45 | cell.viewModel = viewModel 46 | cell.dataSource = self 47 | cell.delegate = self 48 | 49 | // TODO: Increase performance 50 | cell.collectionView.reloadData() 51 | 52 | return cell 53 | } 54 | } 55 | 56 | extension GridTimerView: CustomCollectionViewLayoutDataSource { 57 | 58 | func cellItemHeight() -> CGFloat? { 59 | let heightForRow = dataSource?.heightForRow(inGridTimerView: self) ?? 0 60 | let heightForTimelineRow = dataSource?.heightForTimelineRow(inGridTimerView: self) ?? 0 61 | let heightForSeparation = configuration.rowSeparation 62 | return heightForRow + heightForTimelineRow + heightForSeparation 63 | } 64 | } 65 | 66 | extension GridTimerView: GridItemViewCellDataSource { 67 | 68 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, numberOfItemsInRowIndex rowIndex: Int) -> Int { 69 | return dataSource?.gridTimerView(gridTimerView: self, numberOfItemsAtRowIndex: rowIndex) ?? 0 70 | } 71 | 72 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, colorForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIColor? { 73 | return dataSource?.gridTimerView(gridTimerView: self, colorForItemIndex: itemIndex, inRowIndex: rowIndex) 74 | } 75 | 76 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, containerViewForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIView? { 77 | return dataSource?.gridTimerView(gridTimerView: self, viewForItemIndex: itemIndex, inRowIndex: rowIndex) 78 | } 79 | 80 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, initDateForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date? { 81 | return dataSource?.gridTimerView(gridTimerView: self, startTimeForItemIndex: itemIndex, inRowIndex: rowIndex) 82 | } 83 | 84 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, endDateForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date? { 85 | return dataSource?.gridTimerView(gridTimerView: self, endTimeForItemIndex: itemIndex, inRowIndex: rowIndex) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimerView+Delegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridTimerView+Delegate.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 4/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension GridTimerView: UICollectionViewDelegateFlowLayout { 12 | 13 | 14 | public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 15 | 16 | guard 17 | let cell = collectionView.cellForItem(at: indexPath) as? GridItemViewCell, 18 | let viewModel = cell.viewModel 19 | else { return } 20 | 21 | delegate?.gridTimerView(gridTimerView: self, didSelectRowAtItemIndex: viewModel.item, inRowIndex: viewModel.row) 22 | } 23 | 24 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 25 | 26 | if scrollView == self.collectionView { 27 | 28 | backScrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y), animated: false) 29 | 30 | // Init grid refresh action 31 | if 32 | let isRefreshing = refresher?.isRefreshing, 33 | scrollView.contentOffset.y < scrollRefreshLimitY, 34 | !isRefreshing && firstScroll { 35 | refresher?.beginRefreshing() 36 | refresher?.sendActions(for: .valueChanged) 37 | } else if scrollView.contentOffset.y > -50 { 38 | refresher?.endRefreshing() 39 | } 40 | 41 | // Update rule offset 42 | ruleView.updateContentOffset(x: scrollView.contentOffset.x) 43 | 44 | // Update visible cells with offset 45 | for cell in collectionView.visibleCells as! [GridItemViewCell] { 46 | cell.collectionView.contentOffset.x = scrollView.contentOffset.x 47 | cell.updateHighlightedItems() 48 | } 49 | } 50 | } 51 | 52 | public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 53 | firstScroll = true 54 | } 55 | } 56 | 57 | extension GridTimerView: GridItemViewCellDelegate { 58 | 59 | func gridItemViewCell(_ gridItemViewCell: GridItemViewCell, didHighlightAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) { 60 | delegate?.gridTimerView(gridTimerView: self, didHighlightAtItemIndex: itemIndex, inRowIndex: rowIndex) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimerView+Protocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridTimerView+Protocols.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar de los Ríos on 7/9/18. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol GridTimerViewDataSource: class { 11 | 12 | /** 13 | ------------------------------------------------------------------------------------------ 14 | Returns number of rows in the table (no events) 15 | ------------------------------------------------------------------------------------------ 16 | - parameter gridTimerView: current gridTimerView 17 | - returns: number of rows 18 | */ 19 | func numberOfRows(inGridTimerView gridTimerView: GridTimerView) -> Int 20 | 21 | /** 22 | ------------------------------------------------------------------------------------------ 23 | Returns height of custom row in the table (no events) 24 | ------------------------------------------------------------------------------------------ 25 | - parameter gridTimerView: current gridTimerView 26 | - returns: height of custom row 27 | */ 28 | func heightForRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat 29 | 30 | /** 31 | ------------------------------------------------------------------------------------------ 32 | Returns height of highlighted items 33 | ------------------------------------------------------------------------------------------ 34 | - parameter gridTimerView: current gridTimerView 35 | - returns: height of highlighted items 36 | */ 37 | func heightForTimelineRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat 38 | 39 | /** 40 | ------------------------------------------------------------------------------------------ 41 | Returns number of items in row 42 | ------------------------------------------------------------------------------------------ 43 | - parameter gridTimerView: current gridTimerView 44 | - parameter rowIndex: row index 45 | - returns: number of items in row 46 | */ 47 | func gridTimerView(gridTimerView: GridTimerView, numberOfItemsAtRowIndex rowIndex: Int) -> Int 48 | 49 | /** 50 | ------------------------------------------------------------------------------------------ 51 | Returns custom row view (no event) 52 | ------------------------------------------------------------------------------------------ 53 | - parameter gridTimerView: current gridTimerView 54 | - parameter itemIndex: item index 55 | - parameter rowIndex: row index 56 | - returns: row view 57 | */ 58 | func gridTimerView(gridTimerView: GridTimerView, viewForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIView 59 | 60 | /** 61 | ------------------------------------------------------------------------------------------ 62 | Returns initial event date 63 | ------------------------------------------------------------------------------------------ 64 | - parameter gridTimerView: current gridTimerView 65 | - parameter itemIndex: item index 66 | - parameter rowIndex: row index 67 | - returns: initial date 68 | */ 69 | func gridTimerView(gridTimerView: GridTimerView, endTimeForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date 70 | 71 | /** 72 | ------------------------------------------------------------------------------------------ 73 | Returns end event date 74 | ------------------------------------------------------------------------------------------ 75 | - parameter gridTimerView: current gridTimerView 76 | - parameter itemIndex: item index 77 | - parameter rowIndex: row index 78 | - returns: end date 79 | */ 80 | func gridTimerView(gridTimerView: GridTimerView, startTimeForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date 81 | 82 | /** 83 | ------------------------------------------------------------------------------------------ 84 | Returns color by event in row. 85 | If returns nil, `selectedItemColor` in configuration will be the selected color 86 | ------------------------------------------------------------------------------------------ 87 | - parameter gridTimerView: current gridTimerView 88 | - parameter itemIndex: item index 89 | - parameter rowIndex: row index 90 | - returns: event color 91 | */ 92 | func gridTimerView(gridTimerView: GridTimerView, colorForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIColor? 93 | } 94 | 95 | public extension GridTimerViewDataSource { 96 | 97 | func heightForRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat { 98 | // Optional 99 | return 66 100 | } 101 | 102 | func heightForTimelineRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat { 103 | // Optional 104 | return 8 105 | } 106 | } 107 | 108 | public protocol GridTimerViewDelegate: class { 109 | 110 | /** 111 | ------------------------------------------------------------------------------------------ 112 | Is called when item is highlighted 113 | ------------------------------------------------------------------------------------------ 114 | - parameter gridTimerView: gridTimerView is using 115 | - parameter itemIndex: item index 116 | - parameter rowIndex: row index 117 | */ 118 | func gridTimerView(gridTimerView: GridTimerView, didHighlightAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) 119 | 120 | /** 121 | ------------------------------------------------------------------------------------------ 122 | Is called when row is selected 123 | ------------------------------------------------------------------------------------------ 124 | - parameter gridTimerView: gridTimerView is using 125 | - parameter rowIndex: row index 126 | */ 127 | func gridTimerView(gridTimerView: GridTimerView, didSelectRowAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) 128 | 129 | /** 130 | ------------------------------------------------------------------------------------------ 131 | Is called when row is selected 132 | ------------------------------------------------------------------------------------------ 133 | - parameter gridTimerView: gridTimerView is using 134 | - parameter rowIndex: row index 135 | */ 136 | func didPullToRefresh(inGridTimerView gridTimerView: GridTimerView) 137 | } 138 | 139 | public extension GridTimerViewDelegate { 140 | 141 | func gridTimerView(gridTimerView: GridTimerView, didHighlightAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) { 142 | // Optional 143 | } 144 | 145 | func gridTimerView(gridTimerView: GridTimerView, didSelectRowAtIndex rowIndex: Int) { 146 | // Optional 147 | } 148 | 149 | func didPullToRefresh(inGridTimerView gridTimerView: GridTimerView) { 150 | // Optional 151 | } 152 | } 153 | 154 | public protocol GridTimerViewInterface { 155 | 156 | /** 157 | ------------------------------------------------------------------------------------------ 158 | Scroll imer to single date programatically 159 | ------------------------------------------------------------------------------------------ 160 | - parameter date: date you want scroll to 161 | */ 162 | func scrollToDate(date: Date) 163 | 164 | /** 165 | ------------------------------------------------------------------------------------------ 166 | This method obtain row by index 167 | ------------------------------------------------------------------------------------------ 168 | - parameter rowIndex: row index 169 | */ 170 | func viewForRowIndex(rowIndex: Int) -> UIView? 171 | 172 | // Register your own view for row is needed for reuse in table 173 | // func register(type: T.Type) 174 | // Deque reusable custom view 175 | // func dequeReusableView(withType type: T.Type, forRowIndex rowIndex: Int) -> T? 176 | 177 | /** 178 | ------------------------------------------------------------------------------------------ 179 | End refreshing table 180 | ------------------------------------------------------------------------------------------ 181 | */ 182 | func endRefresh() 183 | 184 | /** 185 | ------------------------------------------------------------------------------------------ 186 | Reload collection view data 187 | ------------------------------------------------------------------------------------------ 188 | */ 189 | func reloadGridData() 190 | 191 | /** 192 | ------------------------------------------------------------------------------------------ 193 | Reload collection view data for row index 194 | ------------------------------------------------------------------------------------------ 195 | */ 196 | func reloadGridRowIndex(_ rowIndex: Int) 197 | } 198 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerGridView.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 4/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | 8 | import UIKit 9 | 10 | open class GridTimerView: UIView { 11 | 12 | @IBOutlet private var contentView: UIView! 13 | @IBOutlet weak public var collectionView: UICollectionView! 14 | @IBOutlet weak public var ruleView: RuleView! 15 | @IBOutlet weak public var timerLineView: UIView! 16 | @IBOutlet weak public var backScrollView: UIScrollView! 17 | 18 | public var refresher: UIRefreshControl? 19 | public var currentTimeLine: UIView? 20 | public var firstScroll = false 21 | public let scrollRefreshLimitY: CGFloat = -120 22 | private let screenSize = UIScreen.main.bounds.size 23 | private let initialInset = UIEdgeInsets(top: 45, left: 0, bottom: 0, right: 0) 24 | private let loadingInset = UIEdgeInsets(top: 100, left: 0, bottom: 0, right: 0) 25 | 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | /// The object that provides the data for the collection view 29 | /// - Note: The data source must adopt the `GridTimerViewDataSource` protocol. 30 | 31 | weak open var dataSource: GridTimerViewDataSource? 32 | 33 | ///////////////////////////////////////////////////////////////////////////// 34 | /// The object that acts as the delegate of the gridtimer view. The delegate 35 | /// object is responsible for managing selection behavior and interactions with 36 | /// individual items. 37 | /// - Note: The delegate must adopt the `GridTimerViewDelegate` protocol. 38 | 39 | weak open var delegate: GridTimerViewDelegate? 40 | 41 | ///////////////////////////////////////////////////////////////////////////// 42 | /// Object that configure `GridTimerView` view. You can setup `GridTimerView` with 43 | /// your own parameters. See also `GridTimerConfiguration` implementation. 44 | 45 | open var configuration = GridTimerConfiguration() { 46 | didSet { 47 | ruleView.ruleFont = configuration.ruleFont 48 | ruleView.ruleTextColor = configuration.ruleTextColor 49 | ruleView.ruleDaysFrom = configuration.ruleDaysFrom 50 | ruleView.ruleDaysTo = configuration.ruleDaysTo 51 | ruleView.ruleBackgroundColor = configuration.ruleBackgroundColor 52 | ruleView.ruleTicksColor = configuration.ruleTicksColor 53 | ruleView.timerFont = configuration.timerFont 54 | ruleView.timerColor = configuration.timerColor 55 | ruleView.timerTextColor = configuration.timerTextColor 56 | ruleView.currentTimeLineDashed = configuration.currentTimeLineDashed 57 | ruleView.currentTimeLineColor = configuration.currentTimeLineColor 58 | timerLineView.backgroundColor = configuration.lineColor 59 | backScrollView.isHidden = !configuration.enableRefresh 60 | 61 | if let collectionViewLayout = collectionView.collectionViewLayout as? CustomCollectionViewLayout { 62 | collectionViewLayout.ruleDaysFrom = configuration.ruleDaysFrom 63 | collectionViewLayout.ruleDaysTo = configuration.ruleDaysTo 64 | // collectionViewLayout.cellSeparation = configuration.rowSeparation 65 | } 66 | } 67 | } 68 | 69 | override init(frame: CGRect) { 70 | super.init(frame: frame) 71 | commonInit() 72 | } 73 | 74 | required public init?(coder aDecoder: NSCoder) { 75 | super.init(coder: aDecoder) 76 | commonInit() 77 | } 78 | 79 | private func commonInit() { 80 | _ = fromNib() 81 | setupCollectionView() 82 | setupRefresher() 83 | setupRuleView() 84 | setupLineView() 85 | } 86 | 87 | private func setupCollectionView() { 88 | 89 | if let collectionViewLayout = collectionView.collectionViewLayout as? CustomCollectionViewLayout { 90 | collectionViewLayout.dataSource = self 91 | } 92 | 93 | collectionView.delegate = self 94 | collectionView.dataSource = self 95 | collectionView.alwaysBounceVertical = true 96 | collectionView.contentInset = initialInset 97 | collectionView.registerInClass(self.classForCoder, forCellWithReuseIdentifier: GridItemViewCell.uniqueIdentifier) 98 | } 99 | 100 | private func setupRuleView() { 101 | ruleView.ruleFont = configuration.ruleFont 102 | ruleView.ruleTextColor = configuration.ruleTextColor 103 | ruleView.ruleDaysFrom = configuration.ruleDaysFrom 104 | ruleView.ruleDaysTo = configuration.ruleDaysTo 105 | ruleView.timerFont = configuration.timerFont 106 | ruleView.timerColor = configuration.timerColor 107 | ruleView.timerTextColor = configuration.timerTextColor 108 | ruleView.currentTimeLineDashed = configuration.currentTimeLineDashed 109 | ruleView.currentTimeLineColor = configuration.currentTimeLineColor 110 | } 111 | 112 | private func setupLineView() { 113 | timerLineView.backgroundColor = configuration.lineColor 114 | } 115 | 116 | private func setupRefresher() { 117 | refresher = UIRefreshControl() 118 | refresher?.tintColor = UIColor.lightGray 119 | refresher?.addTarget(self, action: #selector(didPullToRefresh), for: .valueChanged) 120 | refresher?.backgroundColor = .clear 121 | backScrollView.isHidden = !configuration.enableRefresh 122 | backScrollView.addSubview(refresher!) 123 | } 124 | 125 | @objc private func didPullToRefresh() { 126 | if configuration.enableRefresh { 127 | UIView.animate(withDuration: 0.3) { 128 | self.collectionView.contentInset = self.loadingInset 129 | } 130 | delegate?.didPullToRefresh(inGridTimerView: self) 131 | } 132 | } 133 | } 134 | 135 | extension GridTimerView: GridTimerViewInterface { 136 | 137 | open func scrollToDate(date: Date) { 138 | let offsetY = collectionView.contentOffset.y 139 | let offsetX = CGFloat(date.timeIntervalSince1970 - Date.add(days: -configuration.ruleDaysFrom).timeIntervalSince1970)*96.2/3600 - screenSize.width/2 140 | collectionView.setContentOffset(CGPoint(x: offsetX, y: offsetY), animated: true) 141 | } 142 | 143 | open func viewForRowIndex(rowIndex: Int) -> UIView? { 144 | let cell = collectionView.cellForItem(at: IndexPath(item: rowIndex, section: 0)) as! GridItemViewCell 145 | return cell.userView 146 | } 147 | 148 | open func endRefresh() { 149 | UIView.animate(withDuration: 0.3) { 150 | self.collectionView.contentInset = self.initialInset 151 | } 152 | } 153 | 154 | open func reloadGridData() { 155 | if let collectionViewLayout = collectionView.collectionViewLayout as? CustomCollectionViewLayout { 156 | collectionViewLayout.reloadAttributes = true 157 | collectionView.reloadData() 158 | } 159 | } 160 | 161 | open func reloadGridRowIndex(_ rowIndex: Int) { 162 | UIView.performWithoutAnimation { 163 | collectionView.reloadItems(at: [IndexPath(item: rowIndex, section: 0)]) 164 | } 165 | } 166 | } 167 | 168 | 169 | -------------------------------------------------------------------------------- /GridTimerView/Classes/GridTimerView.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /GridTimerView/Classes/RuleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuleView.swift 3 | // GridTimerView 4 | // 5 | // Created by Alberto Aznar on 3/9/18. 6 | // Copyright © 2018 Alberto Aznar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class RuleView: UIView { 12 | 13 | @IBOutlet private var contentView: UIView! 14 | @IBOutlet weak var scrollView: UIScrollView! 15 | 16 | private var currentTimeLabel: UILabel? 17 | private var currentTimeLine: UIView? 18 | private let ruleTimeInterval: TimeInterval = 60*60 // 60 min 19 | private let screenSize = UIScreen.main.bounds.size 20 | 21 | var ruleDaysFrom = 1 { 22 | didSet { 23 | scrollView.subviews.forEach { $0.removeFromSuperview() } 24 | setupScroll() 25 | setupCurrentTimeLine() 26 | } 27 | } 28 | 29 | var ruleDaysTo = 2 { 30 | didSet { 31 | scrollView.subviews.forEach { $0.removeFromSuperview() } 32 | setupScroll() 33 | setupCurrentTimeLine() 34 | } 35 | } 36 | 37 | var currentTimeLineDashed = false { 38 | didSet { 39 | currentTimeLine?.removeFromSuperview() 40 | setupCurrentTimeLine() 41 | } 42 | } 43 | var currentTimeLineColor = UIColor.blue { 44 | didSet { 45 | currentTimeLine?.removeFromSuperview() 46 | setupCurrentTimeLine() 47 | } 48 | } 49 | 50 | var ruleFont = UIFont.systemFont(ofSize: 10, weight: .semibold) { 51 | didSet { 52 | for view in scrollView.subviews { 53 | if let label = view as? UILabel { 54 | label.font = ruleFont 55 | } 56 | } 57 | } 58 | } 59 | var ruleTextColor = UIColor.lightGray{ 60 | didSet { 61 | for view in scrollView.subviews { 62 | if let label = view as? UILabel { 63 | label.textColor = ruleTextColor 64 | } 65 | } 66 | } 67 | } 68 | var ruleBackgroundColor = UIColor.darkGray { 69 | didSet { 70 | backgroundColor = ruleBackgroundColor 71 | } 72 | } 73 | var ruleTicksColor = UIColor.white { 74 | didSet { 75 | scrollView.subviews.forEach { $0.removeFromSuperview() } 76 | setupScroll() 77 | setupCurrentTimeLine() 78 | } 79 | } 80 | var timerFont = UIFont.systemFont(ofSize: 12, weight: .semibold) { 81 | didSet { 82 | currentTimeLabel?.font = timerFont 83 | } 84 | } 85 | var timerColor = UIColor.blue { 86 | didSet { 87 | currentTimeLabel?.backgroundColor = timerColor 88 | } 89 | } 90 | var timerTextColor = UIColor.white { 91 | didSet { 92 | currentTimeLabel?.textColor = timerTextColor 93 | } 94 | } 95 | var scrollDate: Date? 96 | 97 | override init(frame: CGRect) { 98 | super.init(frame: frame) 99 | commonInit() 100 | setupScroll() 101 | setupCurrentTimeLabel() 102 | setupCurrentTimeLine() 103 | } 104 | 105 | required public init?(coder aDecoder: NSCoder) { 106 | super.init(coder: aDecoder) 107 | commonInit() 108 | setupScroll() 109 | setupCurrentTimeLabel() 110 | setupCurrentTimeLine() 111 | } 112 | 113 | private func commonInit() { 114 | _ = fromNib() 115 | } 116 | 117 | private func setupScroll() { 118 | 119 | backgroundColor = ruleBackgroundColor 120 | 121 | var initialDate = Date.add(days: -ruleDaysFrom).timeIntervalSince1970 122 | 123 | var j = 0 124 | for i in 0 ..< 20 * 24 * (ruleDaysFrom + ruleDaysTo) + 1 { 125 | let timeXPos = CGFloat(i)*(ruleWidth/20) 126 | let timeTick = UIView(frame: CGRect(x: timeXPos, y: 0, width: 0.5, height: j == 10 ? 15 : 10)) 127 | timeTick.backgroundColor = ruleTicksColor 128 | scrollView.addSubview(timeTick) 129 | 130 | if j == 10 { 131 | j = 1 132 | } else { 133 | j += 1 134 | } 135 | } 136 | 137 | for i in 0 ..< 24 * (ruleDaysFrom + ruleDaysTo) + 1 { 138 | let timeXPos = CGFloat(i)*ruleWidth 139 | let timeLabel = UILabel(frame: CGRect(x: timeXPos - 25, y: 7, width: 50, height: 40)) 140 | timeLabel.font = ruleFont 141 | timeLabel.text = Date(timeIntervalSince1970: initialDate).format(dateFormat: "HH:mm") 142 | timeLabel.textColor = ruleTextColor 143 | timeLabel.textAlignment = .center 144 | scrollView.addSubview(timeLabel) 145 | 146 | let timeTick = UIView(frame: CGRect(x: timeXPos, y: 0, width: 0.5, height: 20)) 147 | timeTick.backgroundColor = ruleTicksColor 148 | scrollView.addSubview(timeTick) 149 | 150 | initialDate += ruleTimeInterval 151 | } 152 | } 153 | 154 | private func setupCurrentTimeLabel() { 155 | 156 | currentTimeLabel = UILabel(frame: CGRect(x: screenSize.width/2 - 25, y: 5, width: 50, height: 30)) 157 | currentTimeLabel?.font = timerFont 158 | currentTimeLabel?.textAlignment = .center 159 | currentTimeLabel?.text = Date().format(dateFormat: "HH:mm") 160 | currentTimeLabel?.backgroundColor = timerColor 161 | currentTimeLabel?.textColor = timerTextColor 162 | currentTimeLabel?.layer.cornerRadius = 5 163 | currentTimeLabel?.clipsToBounds = true 164 | addSubview(currentTimeLabel!) 165 | } 166 | 167 | private func setupCurrentTimeLine() { 168 | let xPos = xPosition(byDate: Date(), fromInitDate: Date.add(days: -ruleDaysFrom)) 169 | currentTimeLine = UIView(frame: CGRect(x: xPos, y: frame.size.height, width: 1, height: screenSize.height)) 170 | if currentTimeLineDashed { 171 | currentTimeLine?.addDashedBorder(width: nil, height: nil, lineWidth: 0.25, lineDashPattern: [6,3], strokeColor: currentTimeLineColor, fillColor: .clear) 172 | } else { 173 | currentTimeLine?.backgroundColor = currentTimeLineColor 174 | } 175 | scrollView.addSubview(currentTimeLine!) 176 | } 177 | 178 | func updateContentOffset(x: CGFloat) { 179 | scrollView.contentOffset = CGPoint(x: x, y: 0) 180 | let scrollTimeInterval = TimeInterval(x + screenSize.width/2)*ruleTimeInterval/TimeInterval(ruleWidth) + Date.today().timeIntervalSince1970 181 | scrollDate = Date(timeIntervalSince1970: scrollTimeInterval) 182 | currentTimeLabel?.text = scrollDate?.format(dateFormat: "HH:mm") 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /GridTimerView/Classes/RuleView.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Images/Pods/AnimatedField.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/AnimatedField.png -------------------------------------------------------------------------------- /Images/Pods/CiaoTransitions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/CiaoTransitions.png -------------------------------------------------------------------------------- /Images/Pods/ContentLoader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/ContentLoader.png -------------------------------------------------------------------------------- /Images/Pods/DateScrollPicker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/DateScrollPicker.png -------------------------------------------------------------------------------- /Images/Pods/EmptyStateKit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/EmptyStateKit.png -------------------------------------------------------------------------------- /Images/Pods/GridTimerView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/GridTimerView.png -------------------------------------------------------------------------------- /Images/Pods/PaintCodeKit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/Pods/PaintCodeKit.png -------------------------------------------------------------------------------- /Images/header_GridTimerView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/header_GridTimerView.png -------------------------------------------------------------------------------- /Images/screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/screenshot_1.png -------------------------------------------------------------------------------- /Images/video_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alberdev/GridTimerView/75436e3f39a5ff3a73527314576193c422ef1435/Images/video_1.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Alberto Aznar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | GridTimerView  Logo 3 |

4 | 5 |

6 | 7 | Version 8 | 9 | 14 | 15 | License 16 | 17 | 18 | Swift 5.0 19 | 20 | 21 | Platform 22 | 23 | 28 | 29 | Twitter 30 | 31 | 32 |

33 | 34 |
35 | 36 |

37 | With GridTimerView you can show a schedule with timer controller. Each cell can manage multiple events with different durations. It's perfect for listing TV programs shows in a simulated table. And the good news is that you can customise most of these features with your own fonts, colors, sizes... and many more. 38 |

39 | 40 |
41 | 42 |

43 | GridTimerView 44 |

45 | 46 | # GridTimerView 47 | 48 | - [x] Show multiple events for each cell 49 | - [x] Totally customizable 50 | - [x] Smooth scrolling experience 51 | - [x] Easy usage 52 | - [x] Supports iOS, developed in Swift 4 53 | 54 | 55 | ## Table of Contents 56 | 57 | - [Usage](#usage) 58 | - [UIView in your xib / storyboard](#uiviewinyourxib/storyboard) 59 | - [Configuration](#configuration) 60 | - [DataSource](#datasource) 61 | - [Delegates](#delegates) 62 | - [Extra](#extra) 63 | - [Installation](#installation) 64 | - [Author](#author) 65 | - [License](#license) 66 | 67 | ## Usage 68 | 69 | It is important to know that this pod is composed of rows. Each row has a set of events with start time and end time of type date. You must create a custom view to show in each row. 70 | 71 | Once you've installed this pod, you can follow next steps. It's really simple: 72 | 73 | ### UIView in your xib / storyboard 74 | 75 | Add a `UIView` in the xib where you want to place GridTimerView. Then you have to input the class name in the view, you can change this in the identity inspector of the interface builder. Remember to input `GridTimerView` in both (Class & Module) 76 | 77 | Screenshot 1 78 | 79 | Then, connect the IBOutlet in your UIViewController 80 | 81 | ```swift 82 | @IBOutlet weak var gridTimerView: GridTimerView! 83 | ``` 84 | 85 | ### Make your custom item row (required) 86 | 87 | Make your own custom item subclassing `UIView`. Then you can use it in DataSource protocol. 88 | 89 | ### Implement datasource and delegate 90 | 91 | The first way to customize this `GridTimerView` is implementing delegate and datasource methods. These methods handle the most common use cases. 92 | 93 | ```swift 94 | gridTimerView.dataSource = self 95 | gridTimerView.delegate = self 96 | ``` 97 | 98 | ### Configuration 99 | 100 | You can setup `GridTimerView`with your own parameters. See default values: 101 | 102 | ```swift 103 | var configuration = GridTimerConfiguration() 104 | 105 | // Font for timer labels in rule 106 | configuration.ruleFont = UIFont.systemFont(ofSize: 10, weight: .semibold) 107 | 108 | // Color for timer labels in rule 109 | configuration.ruleTextColor = UIColor.lightGray 110 | 111 | // Days before today for initial time 112 | configuration.ruleDaysFrom = 1 113 | 114 | // Days after today for end time 115 | configuration.ruleDaysTo = 2 116 | 117 | // Rule ticks color 118 | configuration.ruleTicksColor = UIColor.white 119 | 120 | // Rule background color 121 | configuration.ruleBackgroundColor = UIColor.darkGray 122 | 123 | // Font used in current time 124 | configuration.timerFont = UIFont.systemFont(ofSize: 12, weight: .semibold) 125 | 126 | // Background color used in current time 127 | configuration.timerColor = UIColor.blue 128 | 129 | // Text color used in current time 130 | configuration.timerTextColor = UIColor.white 131 | 132 | // Selected date line color 133 | configuration.lineColor = UIColor.blue 134 | 135 | // Current date line color 136 | configuration.currentTimeLineColor = UIColor.blue 137 | 138 | /// Current date line dashed 139 | configuration.currentTimeLineDashed = false 140 | 141 | // Selected highlight color on event 142 | configuration.selectedItemColor = UIColor.blue 143 | 144 | // Unselected color on event 145 | configuration.unselectedItemColor = UIColor.lightGray 146 | 147 | /// Selected highlight color when row cell touched 148 | configuration.selectedColorOnTouch = UIColor.init(red: 0.95, green: 0.95, blue: 0.95, alpha: 1) 149 | 150 | // Row separation 151 | configuration.rowSeparation = 10.0 152 | 153 | /// Enable refresh control when dragged down 154 | configuration.enableRefresh = false 155 | ``` 156 | 157 | Is important to finally assign configuration to `GridTimerView` 158 | 159 | ```swift 160 | gridTimerView.configuration = configuration 161 | ``` 162 | 163 | ### DataSource 164 | 165 | Is needed to show your own cells with events in collection table. 166 | 167 | ```swift 168 | // Needed for displaying rows. Returns number of rows in the table 169 | func numberOfRows(inGridTimerView gridTimerView: GridTimerView) -> Int 170 | 171 | // Needed for displaying rows. Returns height of custom row in the table 172 | func heightForRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat 173 | 174 | // Needed for displaying items in the timeline row. Returns height of highlighted items 175 | func heightForTimelineRow(inGridTimerView gridTimerView: GridTimerView) -> CGFloat 176 | 177 | // Needed for displaying items in the timeline row. Returns number of items in row 178 | func gridTimerView(gridTimerView: GridTimerView, numberOfItemsAtRowIndex rowIndex: Int) -> Int 179 | 180 | // Needed for drawing your custom row with item index and row index 181 | func gridTimerView(gridTimerView: GridTimerView, viewForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIView { 182 | 183 | let channelView = ChannelView() 184 | let channel = channels[rowIndex] 185 | var viewModel = ChannelView.ViewModel() 186 | 187 | if channel.events.count > 0 { 188 | viewModel.title = channel.events[itemIndex].title 189 | viewModel.subtitle = channel.events[itemIndex].subtitle 190 | viewModel.image = channel.channelImage 191 | } 192 | 193 | channelView.viewModel = viewModel 194 | return channelView 195 | } 196 | 197 | // Needed for drawing item in the timeline row 198 | func gridTimerView(gridTimerView: GridTimerView, startTimeForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date 199 | 200 | // Needed for drawing item in the timeline row 201 | func gridTimerView(gridTimerView: GridTimerView, endTimeForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> Date 202 | 203 | // Returns color by item in row. 204 | // If returns nil, `selectedItemColor` in configuration will be the selected color 205 | func gridTimerView(gridTimerView: GridTimerView, colorForItemIndex itemIndex: Int, inRowIndex rowIndex: Int) -> UIColor? 206 | ``` 207 | 208 | ### Delegates 209 | 210 | In order to add more functionality in your app, you must implement te `GridTimerViewDelegate` and set delegate to your view controller instance. 211 | 212 | ```swift 213 | // Called when item is highlighted. 214 | func gridTimerView(gridTimerView: GridTimerView, didHighlightAtItemIndex itemIndex: Int, inRowIndex rowIndex: Int) 215 | 216 | // Called when row is selected 217 | func gridTimerView(gridTimerView: GridTimerView, didSelectRowAtIndex rowIndex: Int) 218 | 219 | // Called when you refresh the table 220 | func didPullToRefresh(inGridTimerView gridTimerView: GridTimerView) 221 | ``` 222 | 223 | ### Extra 224 | 225 | You can also use next methods for scrolling timer, registering and reuse your custom view row or end refreshing table when new data has loaded. 226 | 227 | ```swift 228 | // Scroll the timer to single date programatically 229 | func scrollToDate(date: Date) 230 | 231 | // Obtain your custom view 232 | func viewForRowIndex(rowIndex: Int) -> UIView? 233 | 234 | // End refreshing table. Used when finish loading data 235 | func endRefresh() 236 | 237 | // Reload collection view data 238 | func reloadGridData() 239 | 240 | // Reload collection view data for row index 241 | func reloadGridRowIndex(_ rowIndex: Int) 242 | ``` 243 | 244 | 245 | ## Installation 246 | 247 | **GridTimerView** is available through [CocoaPods](https://cocoapods.org). To install 248 | it, simply add the following line to your Podfile: 249 | 250 | ```ruby 251 | pod 'GridTimerView' 252 | ``` 253 | 254 | ## Author 255 | 256 | Alberto Aznar, info@alberdev.com 257 | 258 | ## License 259 | 260 | GridTimerView is available under the MIT liceßnse. See the LICENSE file for more info. 261 | 262 | ## Libraries by @alberdev 263 | 264 | AnimatedField  Logo 265 | ContentLoader  Logo 266 | CiaoTransitions  Logo 267 | DateScrollPicker  Logo 268 | EmptyStateKit  Logo 269 | GridTimerView  Logo 270 | PaintCodeKit  Logo 271 | --------------------------------------------------------------------------------