Repository: CosmicMind/Material
Branch: development
Commit: ea59f7c4d72a
Files: 185
Total size: 854.8 KB
Directory structure:
gitextract_djfw36z0/
├── .gitignore
├── .gitmodules
├── .swift-version
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Cartfile
├── LICENSE.md
├── Material.podspec
├── Material.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── Material.xcscmblueprint
│ └── xcshareddata/
│ └── xcschemes/
│ └── Material.xcscheme
├── Package.swift
├── README.md
└── Sources/
├── Assets.xcassets/
│ ├── Contents.json
│ ├── cm_add_white.imageset/
│ │ └── Contents.json
│ ├── cm_arrow_back_white.imageset/
│ │ └── Contents.json
│ ├── cm_arrow_downward_white.imageset/
│ │ └── Contents.json
│ ├── cm_audio_library_white.imageset/
│ │ └── Contents.json
│ ├── cm_audio_white.imageset/
│ │ └── Contents.json
│ ├── cm_bell_white.imageset/
│ │ └── Contents.json
│ ├── cm_check_white.imageset/
│ │ └── Contents.json
│ ├── cm_close_white.imageset/
│ │ └── Contents.json
│ ├── cm_image_white.imageset/
│ │ └── Contents.json
│ ├── cm_menu_white.imageset/
│ │ └── Contents.json
│ ├── cm_microphone_white.imageset/
│ │ └── Contents.json
│ ├── cm_more_horiz_white.imageset/
│ │ └── Contents.json
│ ├── cm_more_vert_white.imageset/
│ │ └── Contents.json
│ ├── cm_movie_white.imageset/
│ │ └── Contents.json
│ ├── cm_pause_white.imageset/
│ │ └── Contents.json
│ ├── cm_pen_white.imageset/
│ │ └── Contents.json
│ ├── cm_photo_camera_white.imageset/
│ │ └── Contents.json
│ ├── cm_photo_library_white.imageset/
│ │ └── Contents.json
│ ├── cm_play_white.imageset/
│ │ └── Contents.json
│ ├── cm_search_white.imageset/
│ │ └── Contents.json
│ ├── cm_settings_white.imageset/
│ │ └── Contents.json
│ ├── cm_share_white.imageset/
│ │ └── Contents.json
│ ├── cm_shuffle_white.imageset/
│ │ └── Contents.json
│ ├── cm_skip_backward_white.imageset/
│ │ └── Contents.json
│ ├── cm_skip_forward_white.imageset/
│ │ └── Contents.json
│ ├── cm_star_white.imageset/
│ │ └── Contents.json
│ ├── cm_videocam_white.imageset/
│ │ └── Contents.json
│ ├── cm_volume_high_white.imageset/
│ │ └── Contents.json
│ ├── cm_volume_medium_white.imageset/
│ │ └── Contents.json
│ ├── cm_volume_off_white.imageset/
│ │ └── Contents.json
│ ├── ic_add_circle_outline_white.imageset/
│ │ └── Contents.json
│ ├── ic_add_circle_white.imageset/
│ │ └── Contents.json
│ ├── ic_add_white.imageset/
│ │ └── Contents.json
│ ├── ic_arrow_back_white.imageset/
│ │ └── Contents.json
│ ├── ic_arrow_downward_white.imageset/
│ │ └── Contents.json
│ ├── ic_audiotrack_white.imageset/
│ │ └── Contents.json
│ ├── ic_camera_front_white.imageset/
│ │ └── Contents.json
│ ├── ic_camera_rear_white.imageset/
│ │ └── Contents.json
│ ├── ic_check_white.imageset/
│ │ └── Contents.json
│ ├── ic_close_white.imageset/
│ │ └── Contents.json
│ ├── ic_edit_white.imageset/
│ │ └── Contents.json
│ ├── ic_email_white.imageset/
│ │ └── Contents.json
│ ├── ic_favorite_border_white.imageset/
│ │ └── Contents.json
│ ├── ic_favorite_white.imageset/
│ │ └── Contents.json
│ ├── ic_flash_auto_white.imageset/
│ │ └── Contents.json
│ ├── ic_flash_off_white.imageset/
│ │ └── Contents.json
│ ├── ic_flash_on_white.imageset/
│ │ └── Contents.json
│ ├── ic_history_white.imageset/
│ │ └── Contents.json
│ ├── ic_home_white.imageset/
│ │ └── Contents.json
│ ├── ic_image_white.imageset/
│ │ └── Contents.json
│ ├── ic_menu_white.imageset/
│ │ └── Contents.json
│ ├── ic_more_horiz_white.imageset/
│ │ └── Contents.json
│ ├── ic_more_vert_white.imageset/
│ │ └── Contents.json
│ ├── ic_movie_white.imageset/
│ │ └── Contents.json
│ ├── ic_phone_white.imageset/
│ │ └── Contents.json
│ ├── ic_photo_camera_white.imageset/
│ │ └── Contents.json
│ ├── ic_photo_library_white.imageset/
│ │ └── Contents.json
│ ├── ic_place_white.imageset/
│ │ └── Contents.json
│ ├── ic_search_white.imageset/
│ │ └── Contents.json
│ ├── ic_settings_white.imageset/
│ │ └── Contents.json
│ ├── ic_share_white.imageset/
│ │ └── Contents.json
│ ├── ic_star_border_white.imageset/
│ │ └── Contents.json
│ ├── ic_star_half_white.imageset/
│ │ └── Contents.json
│ ├── ic_star_white.imageset/
│ │ └── Contents.json
│ ├── ic_videocam_white.imageset/
│ │ └── Contents.json
│ ├── ic_visibility_off_white.imageset/
│ │ └── Contents.json
│ ├── ic_visibility_white.imageset/
│ │ └── Contents.json
│ └── ic_work_white.imageset/
│ └── Contents.json
├── Info.plist
├── LICENSE
├── Material.h
└── iOS/
├── Animation/
│ ├── PulseAnimation.swift
│ └── SpringAnimation.swift
├── Application/
│ └── Application.swift
├── Bar/
│ └── Bar.swift
├── BottomTabBar/
│ └── BottomNavigationController.swift
├── Button/
│ ├── BaseIconLayerButton.swift
│ ├── Button.swift
│ ├── CheckButton.swift
│ ├── FABButton.swift
│ ├── FlatButton.swift
│ ├── IconButton.swift
│ ├── RadioButton.swift
│ └── RaisedButton.swift
├── ButtonGroup/
│ ├── BaseButtonGroup.swift
│ ├── CheckButtonGroup.swift
│ └── RadioButtonGroup.swift
├── Card/
│ ├── Card.swift
│ ├── ImageCard.swift
│ └── PresenterCard.swift
├── Chip/
│ ├── ChipBar.swift
│ └── ChipBarController.swift
├── Collection/
│ ├── CardCollectionView/
│ │ ├── CardCollectionViewCell.swift
│ │ └── CardCollectionViewController.swift
│ ├── CollectionReusableView.swift
│ ├── CollectionView.swift
│ ├── CollectionViewCell.swift
│ ├── CollectionViewController.swift
│ └── CollectionViewLayout.swift
├── Color/
│ └── Color.swift
├── Data/
│ └── DataSourceItem.swift
├── Device/
│ └── Device.swift
├── Dialogs/
│ ├── Dialog.swift
│ ├── DialogController.swift
│ └── DialogView.swift
├── Divider/
│ └── Divider.swift
├── Extension/
│ ├── Material+Array.swift
│ ├── Material+CALayer.swift
│ ├── Material+MotionAnimation.swift
│ ├── Material+NSMutableAttributedString.swift
│ ├── Material+String.swift
│ ├── Material+UIButton.swift
│ ├── Material+UIColor.swift
│ ├── Material+UIFont.swift
│ ├── Material+UIImage.swift
│ ├── Material+UILabel.swift
│ ├── Material+UIView.swift
│ ├── Material+UIViewController.swift
│ └── Material+UIWindow.swift
├── FABMenu/
│ ├── FABMenu.swift
│ └── FABMenuController.swift
├── Font/
│ ├── DynamicFontType.swift
│ ├── Font.swift
│ └── RobotoFont.swift
├── Grid/
│ └── Grid.swift
├── Height/
│ └── HeightPreset.swift
├── Icon/
│ └── Icon.swift
├── Layer/
│ └── Layer.swift
├── Layout/
│ ├── Layout.swift
│ ├── LayoutAnchor.swift
│ ├── LayoutAttribute.swift
│ └── LayoutConstraint.swift
├── Navigation/
│ ├── NavigationBar.swift
│ ├── NavigationController.swift
│ └── NavigationItem.swift
├── NavigationDrawer/
│ └── NavigationDrawerController.swift
├── Screen/
│ └── Screen.swift
├── SearchBar/
│ ├── SearchBar.swift
│ └── SearchBarController.swift
├── Snackbar/
│ ├── Snackbar.swift
│ └── SnackbarController.swift
├── StatusBar/
│ └── StatusBarController.swift
├── Switch/
│ └── Switch.swift
├── Tab/
│ ├── TabBar.swift
│ └── TabsController.swift
├── Table/
│ ├── TableView.swift
│ ├── TableViewCell.swift
│ └── TableViewController.swift
├── Text/
│ ├── Editor.swift
│ ├── ErrorTextField.swift
│ ├── ErrorTextFieldValidator.swift
│ ├── TextField.swift
│ ├── TextStorage.swift
│ └── TextView.swift
├── Theme/
│ └── Theme.swift
├── Toolbar/
│ ├── Toolbar.swift
│ └── ToolbarController.swift
├── Transition/
│ ├── DisplayStyle.swift
│ └── TransitionController.swift
├── Type/
│ ├── Border.swift
│ ├── CornerRadius.swift
│ ├── Depth.swift
│ ├── EdgeInsets.swift
│ ├── InterimSpace.swift
│ ├── Offset.swift
│ └── Shape.swift
└── View/
├── PulseView.swift
├── View.swift
└── ViewController.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
#OS X
.DS_Store
# Xcode
build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
profile
*.moved-aside
*.playground
*.framework
DerivedData
Index
Build
Checkouts
================================================
FILE: .gitmodules
================================================
[submodule "Sources/Frameworks/Motion"]
path = Sources/Frameworks/Motion
url = https://github.com/CosmicMind/Motion.git
================================================
FILE: .swift-version
================================================
5.0
================================================
FILE: CHANGELOG.md
================================================
## 3.1.8
- [pr-1269](https://github.com/CosmicMind/Material/pull/1269): Fixed Xcode 11 crash, where layoutMargins are not available before iOS 13.
- [pr-1270](https://github.com/CosmicMind/Material/pull/1270): Fixed missing argument in Swift Package Manager.
* Updated to [Motion 3.1.3](https://github.com/CosmicMind/Motion/releases/tag/3.1.3).
## 3.1.7
* Fixed Grid issues, where the layout calculations were being deferred and causing inconsistencies in layouts.
* Updated to [Motion 3.1.2](https://github.com/CosmicMind/Motion/releases/tag/3.1.2).
## 3.1.6
- [issue-1245](https://github.com/CosmicMind/Material/issues/1245): Fixed issue where completion block was not executed when calling Switch.toggle.
## 3.1.5
- [pr-1248](https://github.com/CosmicMind/Material/pull/1248): Exposed Obj-C methods for NavigationDrawerController.
- [issue-1247](https://github.com/CosmicMind/Material/issues/1247): Several methods in NavigationDrawerController not visible in Obj-C.
## 3.1.4
- [pr-1239](https://github.com/CosmicMind/Material/pull/1239): Fixed regression with intrinsic content sizing in Switch control.
- [pr-1240](https://github.com/CosmicMind/Material/pull/1240): Fixed prepare method called twice.
- [issue-1215](https://github.com/CosmicMind/Material/issues/1215): prepare() is called twice on NavigationDrawerController.
## 3.1.3
* Added installation instructions to README.
- [pr-1236](https://github.com/CosmicMind/Material/pull/1236): Added Layout relations.
- [issue-1220](https://github.com/CosmicMind/Material/issues/1220): Support all relations for Layout constraints.
## 3.1.2
- [pr-1233](https://github.com/CosmicMind/Material/pull/1233): Fixed Layout breaks - subview ordering.
- [issue-1232](https://github.com/CosmicMind/Material/issues/1232): Layout breaks view arrangements.
## 3.1.1
- [pr-2131](https://github.com/CosmicMind/Material/pull/2131): Storyboard TextField fixes.
- [issue-1229](https://github.com/CosmicMind/Material/issues/1229): TextField's tintColor doesn't support user setting.
- [issue-1230](https://github.com/CosmicMind/Material/issues/1230): Button's title font doesn't support user setting.
## 3.1.0
- Updated to swift 5.
## 3.0.0
- Updated to swift 4.2.
- [pr-1124](https://github.com/CosmicMind/Material/pull/1124): Fixed issue-1123, TextField is not scrolling.
- [issue-1123](https://github.com/CosmicMind/Material/issues/1123): TextField is not scrolling when inputing characters and using a large Font size.
- [pr-1126](https://github.com/CosmicMind/Material/pull/1126): Cleaned up TextField.
- [pr-1130](https://github.com/CosmicMind/Material/pull/1130): Addressed multiple issues.
- [issue-1125](https://github.com/CosmicMind/Material/issues/1125): TextView with animated placeholder.
- [issue-1127](https://github.com/CosmicMind/Material/issues/1127): TextView auto-adjust height based on text lines.
- [issue-1128](https://github.com/CosmicMind/Material/issues/1128): TextField animates weird when text alignment is .right and we have textInset.
- Removed `textInset: CGFloat` and added `textInsets: EdgeInsets` to `TextField`.
- [pr-1134](https://github.com/CosmicMind/Material/pull/1134): Added swipe feature to BottomNavigationController.
- [issue-1132](https://github.com/CosmicMind/Material/issues/1132): BottomNavigationController same swipe behaviour as TabsController.
- [pr-1147](https://github.com/CosmicMind/Material/pull/1147): Allow framework to be linked from extensions.
- [pr-1151](https://github.com/CosmicMind/Material/pull/1151): New features.
- Added `left/right/above/below` directions to `DepthPreset`.
- Added `.custom(x)` case for `HeightPreset`.
- Added support for `heightPreset` in `BottomNavigationController`. [issue-1150](https://github.com/CosmicMind/Material/issues/1150)
- [pr-1165](https://github.com/CosmicMind/Material/pull/1165): Added interactive swipe.
- [issue-1135](https://github.com/CosmicMind/Material/issues/1135): Convert swiping in TabsController and BottomNavigationController to interactive.
- [pr-1115](https://github.com/CosmicMind/Material/pull/1115): Introducing Theming to Material.
- [pr-1173](https://github.com/CosmicMind/Material/pull/1173): Added dialogs.
- [pr-1174](https://github.com/CosmicMind/Material/pull/1174): Added disabling theming globally and per-class.
- [pr-1183](https://github.com/CosmicMind/Material/pull/1183): Added global theme font.
- [pr-1185](https://github.com/CosmicMind/Material/pull/1185): Reworked layout system.
- [pr-1186](https://github.com/CosmicMind/Material/pull/1186): Fixed SnackBar laid out incorrectly.
- [pr-1187](https://github.com/CosmicMind/Material/pull/1187): Added option for disabling snackbar layout edge inset calculation
## 2.17.0
* Updated for Swift 4.2.
* Updated to [Motion 1.5.0](https://github.com/CosmicMind/Motion/releases/tag/1.5.0)
## 2.16.4
* [pr-1120](https://github.com/CosmicMind/Material/pull/1120): Fixed issue where TextField cursor was not being repositioned correctly.
* [issue-1119](https://github.com/CosmicMind/Material/issues/1119): Cursor position was incorrectly being positioned when toggling security entry.
## 2.16.3
* Updated to [Motion 1.4.3](https://github.com/CosmicMind/Motion/releases/tag/1.4.3)
* [pr-1116](https://github.com/CosmicMind/Material/pull/1116): ViewController-oriented clean up.
* [pr-1117](https://github.com/CosmicMind/Material/pull/1117): Fixed TextView font issue with emojis.
* [issue-838](https://github.com/CosmicMind/Material/issues/838): TextView's font breaks when you type emoji.
## 2.16.2
* [pr-1113](https://github.com/CosmicMind/Material/pull/1113): Added update() to Grid.
* [pr-1112](https://github.com/CosmicMind/Material/pull/1112): Added tab bar centering.
* [issue-926](https://github.com/CosmicMind/Material/issues/926): TabsController - centering TabItem after selection.
* [pr-1114](https://github.com/CosmicMind/Material/pull/1114): Added option to adjust tabBar line width.
* [issue-1109](https://github.com/CosmicMind/Material/issues/1109): Want to change TabBar line width.
## 2.16.1
* [issue-1110](https://github.com/CosmicMind/Material/issues/1110): Fixed an issue where the depth of a view was being clipped incorrectly.
* [pr-1111](https://github.com/CosmicMind/Material/pull/1111): Fixed TabItem - was not being changed on swipe.
* [pr-1106](https://github.com/CosmicMind/Material/pull/1106): Added ability to show visibility and clear button at the same time.
* [issue-992](https://github.com/CosmicMind/Material/issues/992): Visibility & Clear Button can't be shown in TextField at the same time.
* [pr-1104](https://github.com/CosmicMind/Material/pull/1104): Added missing devices.
* [pr-1101](https://github.com/CosmicMind/Material/pull/1101): Enum for support iPhoneX.
## 2.16.0
* Updated to [Motion 1.4.2](https://github.com/CosmicMind/Motion/releases/tag/1.4.2).
* [pr-1004](https://github.com/CosmicMind/Material/pull/1004): Added RadioButton/CheckButton and RadioButtonGroup/CheckButtonGroup.
* [issue-505](https://github.com/CosmicMind/Material/issues/505): Add RadioButton and Checkbox.
* Updated to [Motion 1.4.0](https://github.com/CosmicMind/Motion/releases/tag/1.4.0).
* [issue-1078](https://github.com/CosmicMind/Material/issues/1078): Update Motion Dependency.
* [pr-1047](https://github.com/CosmicMind/Material/pull/1047): Document material color codes.
* [issue-1000](https://github.com/CosmicMind/Material/issues/1000): Color: Document mapping from codes (e.g. a400) to names (e.g. accent1).
* [pr-1055](https://github.com/CosmicMind/Material/pull/1055): Open up FABMenu a little bit.
* Updated Copyright years.
* [pr-1079](https://github.com/CosmicMind/Material/pull/1079): Added custom navigationBarClass support to NavigationController.
* [issue-1074](https://github.com/CosmicMind/Material/issues/1074): Need to use a NavigationBar subclass with NavigationController.
* [pr-1080](https://github.com/CosmicMind/Material/pull/1080): Fixed license badge href.
* [pr-1046](https://github.com/CosmicMind/Material/pull/1046): Added ShouldOpen and ShouldClose delegate methods to FABMenuDelegate.
* [issue-1043](https://github.com/CosmicMind/Material/issues/1043): ShouldOpen and ShouldClose delegate methods FABMenu.
* [pr-1086](https://github.com/CosmicMind/Material/pull/1086): Fix delegations never fired on tab swipe.
* [issue-1087](https://github.com/CosmicMind/Material/issues/1087): TabBar item is selected even though TabsController delegate shouldSelect always returns false.
* [issue-1056](https://github.com/CosmicMind/Material/issues/1056): Delegation methods never fired on Tab swipe.
* [pr-1088](https://github.com/CosmicMind/Material/pull/1088): Removed unnecessary convenience initializers.
* [issue-1085](https://github.com/CosmicMind/Material/issues/1085): `convenience init()` across the framework prevents generic initialization of the components.
* [pr-1082](https://github.com/CosmicMind/Material/pull/1082): Added ErrorTextField validation.
* [issue-1017](https://github.com/CosmicMind/Material/issues/1017): Can we make the error detail for textfields dynamic?
* [issue-1053](https://github.com/CosmicMind/Material/issues/1053): TextField Detail Label not Layed-Out correctly with Left-Image.
* [pr-1103](https://github.com/CosmicMind/Material/pull/1103): Added ability to change password visibility icons.
* [pr-1097](https://github.com/CosmicMind/Material/pull/1097):: Added new extensions: UIColor(argb:), UIColor(rgb:), UIButton.fontSize, UILabel.fontSize.
* [pr-1093](https://github.com/CosmicMind/Material/pull/1093):: Fix TextField placeholderLabel position.
* [issue-1092](https://github.com/CosmicMind/Material/issues/1092): TextField.placeholderLabel is positioned higher than before in version 2.x.x.
* [pr-1103](https://github.com/CosmicMind/Material/pull/1103): Added ability to change password visibility icons.
* [issue-1012](https://github.com/CosmicMind/Material/issues/1012): Can we set visibility icon custom for password textfield.
## 2.15.0
* [issue-1057](https://github.com/CosmicMind/Material/issues/1057): Added image states for TabItems used in TabBar and TabsController.
## 2.14.0
* [issue-995](https://github.com/CosmicMind/Material/issues/995): Updated iOS 11 layout margins for NavigationBar.
* [pr-1038](https://github.com/CosmicMind/Material/pull/1038): Merged PR for iOS 11 layout margins fix.
## 2.13.7
* Updated TabsController to no longer force the default animation to change between tabs and not return to normal behavior.
* [issue-1044](https://github.com/CosmicMind/Material/issues/1044): Fixed issue where TabBar items were not correctly laying out.
## 2.13.6
* [issue-841](https://github.com/CosmicMind/Material/issues/841): Adjusted default sizing for Switch to be more like the original sizing.
* [pr-1030](https://github.com/CosmicMind/Material/pull/1032): Added workaround for known issue where trailing whitespace is apparent in UITextField.
* Updated to [Motion 1.3.5](https://github.com/CosmicMind/Motion/releases/tag/1.3.5).
## 2.13.5
* [pr-1019](https://github.com/CosmicMind/Material/pull/1019): Added swipe gesture handling to TabsController.
* Updated to [Motion 1.3.4](https://github.com/CosmicMind/Motion/releases/tag/1.3.4).
## 2.13.4
* [issue-1016](https://github.com/CosmicMind/Material/issues/1016): Updated hierarchy traversal for TransitionController types to no longer skip over non TransitionController types.
## 2.13.3
* [issue-1015](https://github.com/CosmicMind/Material/issues/1015): Fixed regression where view lifecycle functions were not being called.
* Motion disabled by default for NavigationController to avoid unbalanced calls to view lifecycle when presenting a NavigationController modally.
* Updated to [Motion 1.3.3](https://github.com/CosmicMind/Motion/releases/tag/1.3.3).
## 2.13.2
* Updated to [Motion 1.3.2](https://github.com/CosmicMind/Motion/releases/tag/1.3.2).
* Fixed unbalanced calls in Motion transitions.
## 2.13.1
* Updated to [Motion 1.3.1](https://github.com/CosmicMind/Motion/releases/tag/1.3.1).
## 2.13.0
* Updated to [Motion 1.3.0](https://github.com/CosmicMind/Motion/releases/tag/1.3.0).
## 2.12.19
* [issue-997](https://github.com/CosmicMind/Material/issues/977): Fixed NavigationDrawerController where swiping off device caused a partial correct state.
## 2.12.18
* Fixed layout issues in CollectionView, where the sizing was not correctlly being initialized.
* [issue-495](https://github.com/CosmicMind/Material/issues/495): Made TextField.textInset available in Obj-C.
## 2.12.17
* [pr-979](https://github.com/CosmicMind/Material/pull/979): Added `visibilityOff` icon and updated `TextField` to utilize it.
* [issue-982](https://github.com/CosmicMind/Material/issues/982): Updated Icon let declarations to var declarations to allow custom icon sets.
* [issue-980](https://github.com/CosmicMind/Material/issues/980): Added `@objc` to extension properties in Material+UIView.
* [issue-650](https://github.com/CosmicMind/Material/issues/650): Fixed issue where `NavigationBar.backButton` would incorrectly be laid out when caching view controllers.
* [issue-973](https://github.com/CosmicMind/Material/issues/973): Fixed issue where `Button.prepare` was not being called in the correct order.
## 2.12.16
* [issue-965](https://github.com/CosmicMind/Material/issues/965): Removed duplicate `prepare` call in initializer.
* Rework of Layout's internal process - removed an async call to layout views.
* Updated the layout calls for FABMenu's fabButton.
## 2.12.15
* [issue-957](https://github.com/CosmicMind/Material/issues/957): Fixed StatusBar height issue in iOS 9 and iOS 10.
## 2.12.14
* [samples issue-95](https://github.com/CosmicMind/Samples/issues/95): Fixed TabBar image colors that were not correctly being set for a given state.
* Fixed layout issue, where the calculation of the layout item was not being set in the current render cycle.
## 2.12.13
* Fixed issue where sizing of pulse was incorrectly animating when using the NavigationController on iOS 11.
## 2.12.12
* [issue-924](https://github.com/CosmicMind/Material/issues/924): Fixed NavigationController display in iOS 10.
## 2.12.11
* Fixed iPhoneX topLayoutGuide constraints not properly being set for StatusBarController types.
* Fixed iOS 11 layout issues for NavigationController.
* [pr-945](https://github.com/CosmicMind/Material/pull/945): iPhoneX update for TabBar bottom line alignment.
## 2.12.10
* [samples-issue-78](https://github.com/CosmicMind/Samples/issues/78): Fixed iPhoneX bottomLayoutGuide constraints not properly being set.
## 2.12.9
* Fixed breaking change to loading the TabsController.
## 2.12.8
* [issue-933](https://github.com/CosmicMind/Material/issues/933): Fixed issue where `NavigationDrawerController` was not displaying the `leftViewController` and `rightViewController`.
* [issue-940](https://github.com/CosmicMind/Material/issues/940): Fixed an issue where the `TransitionController` was not executing the lifecycle functions for the initial `rootViewController`.
## 2.12.7
* [pr-938](https://github.com/CosmicMind/Material/pull/938): An expansion on this PR to fix the lifecycle issues with transitions.
## 2.12.6
* Fixed issue where TabBar.lineColor was incorrectly being updated.
## 2.12.5
* Updated to [Motion 1.2.4](https://github.com/CosmicMind/Motion/releases/tag/1.2.4).
* [issue-937](https://github.com/CosmicMind/Material/issues/937): Added @objc to `TabBar.lineColor` for access availability.
* [pr-934](https://github.com/CosmicMind/Material/pull/934): Added access to the `TabBar.line` view.
## 2.12.4
* Updated to [Motion 1.2.3](https://github.com/CosmicMind/Motion/releases/tag/1.2.3).
* [issue-919](https://github.com/CosmicMind/Material/issues/919): Fixed issue where lifecycle methods were being called on tab item view controllers prematurely.
* [pr-923](https://github.com/CosmicMind/Material/pull/923): Merge PR that fixes [issue-919](https://github.com/CosmicMind/Material/issues/919).
* [issue-931](https://github.com/CosmicMind/Material/issues/931): Fixed issue where selectedTabItem was not updated correctly during a porgrammatic transition.
## 2.12.3
* [issue-907](https://github.com/CosmicMind/Material/issues/907): Fixed Layout ordering issues.
## 2.12.2
* [issue-860](https://github.com/CosmicMind/Material/issues/860): Updated TabBar color states and added an independent line color state.
## 2.12.1
* [issue-911](https://github.com/CosmicMind/Material/issues/911): Added @objc to TabBar.tabItems for visibility in Obj-C.
## 2.12.0
* [issue-860](https://github.com/CosmicMind/Material/issues/860): Added `TabBar` color states.
## 2.11.4
* Added Cartfile for Carthage package manager, which includes the Motion dependency.
## 2.11.3
* Updated Motion submodule to use `https` over `git@`.
## 2.11.2
* Updated to [Motion 1.2.2](https://github.com/CosmicMind/Motion/releases/tag/1.2.2).
## 2.11.1
* Fixed duplicate `prepare` call in `TabsController`.
## 2.11.0
* Updated the installation guide for Material, [Material - It's time to download](https://www.cosmicmind.com/danieldahan/lesson/6). Material now uses [Motion](https://github.com/CosmicMind/Motion) as a submodule and CocoaPods dependancy.
* [samples issue-70](https://github.com/CosmicMind/Samples/issues/70#issuecomment-335533243): Made an internal `_TabBarDelegate` to avoid needing to override the `TabBarDelegate` in `TabsController`.
## 2.10.4
* [issue-891](https://github.com/CosmicMind/Material/pull/891): Fixed conflict with addAttributes method in the NSMutableAttributedString extension.
## 2.10.3
* [issue-773](https://github.com/CosmicMind/Material/pull/773): Added `Swift 4` support.
* [pr-873](https://github.com/CosmicMind/Material/pull/873): Fixes PlaceholderLabel position when right-aligned - iOS 11.0
* [issue-886](https://github.com/CosmicMind/Material/issues/886): Fixed a memory leak within Motion's references to previous `UINavigationControllerDelegate` and `UITabBarControllerDelegate`.
* [issue-861](https://github.com/CosmicMind/Material/pull/861): Fixed `NavigationBar` being `nil` in some cases.
* [issue-845](https://github.com/CosmicMind/Material/pull/845): Fixed ambiguity issues with all properties.
## 2.10.2
* [issue-849](https://github.com/CosmicMind/Material/issues/849): Fixed issue where `TextView.placeholderNormalColor` was not correctly displaying. Renamed `TextView.placeholderNormalColor` to `TextView.placeholderColor`.
* [issue-856](https://github.com/CosmicMind/Material/issues/856): Fixed issue where `TextField.placeholderAnimation = .hidden` was not correctly being displayed when text was set to nil.
* All default instances of `Color.grey.lighten3` have been switched to `Color.grey.lighten2`.
## 2.10.1
* [issue-833](https://github.com/CosmicMind/Material/issues/833): `TabsController` now be selected programmatically.
* [issue-859](https://github.com/CosmicMind/Material/issues/859): `TabsController` now has a delegation protocol `TabsControllerDelegate`.
* [issue-830](https://github.com/CosmicMind/Material/issues/830): Bug fix where `TabsController` did not animate to the correct tab when programmatically set.
## 2.10.0
* [issue-857](https://github.com/CosmicMind/Material/issues/857): Fixed an issue where setting the `statusBar` property for the `ToolbarController` was not updating the background color correctly.
* [issue-858](https://github.com/CosmicMind/Material/issues/858): Fixed `Photos` sample project that was not updated to reflect the changes in the `TabBar`.
* [pr-715](https://github.com/CosmicMind/Material/pull/715): Added `isPlaceholderUppercasedWhenEditing` property to `TextField`.
* [pr-721](https://github.com/CosmicMind/Material/pull/721): Added `FABMenuItemTitleLabelPosition` which allows the `FABMenu` to place its `FABBMenuItems` to either the `left` or `right` position of the `FABButton`.
* [pr-851](https://github.com/CosmicMind/Material/pull/851): Added `placeholderHorizontalOffset` property to `TextField`.
* [pr-847](https://github.com/CosmicMind/Material/pull/847): Added `placeholderActiveScale` property to `TextField`.
* [pr-848](https://github.com/CosmicMind/Material/pull/848): Natural motion transition added to `TabsController` when view controller `motionModalTransitionType` is set to `.auto`.
* `Card` types default to a `depthPreset` of `.none`.
* Added `shouldSelect` method to `TabBarDelegate`.
* Updated `TabsController` to use `TabBarDelegate` rather than button handlers.
* Updated `EdgeInsetsPreset` values to:
```swift
.square1: EdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
.square2: EdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
.square3: EdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
.square4: EdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
.square5: EdgeInsets(top: 24, left: 24, bottom: 24, right: 24)
.square6: EdgeInsets(top: 28, left: 28, bottom: 28, right: 28)
.square7: EdgeInsets(top: 32, left: 32, bottom: 32, right: 32)
.square8: EdgeInsets(top: 36, left: 36, bottom: 36, right: 36)
.square9: EdgeInsets(top: 40, left: 40, bottom: 40, right: 40)
.square10: EdgeInsets(top: 44, left: 44, bottom: 44, right: 44)
.square11: EdgeInsets(top: 48, left: 48, bottom: 48, right: 48)
.square12: EdgeInsets(top: 52, left: 52, bottom: 52, right: 52)
.square13: EdgeInsets(top: 56, left: 56, bottom: 56, right: 56)
.square14: EdgeInsets(top: 60, left: 60, bottom: 60, right: 60)
.square15: EdgeInsets(top: 64, left: 64, bottom: 64, right: 64)
```
## 2.9.4
* Added `ToolbarAlignment` to allow placement of the `Toolbar` at the top or bottom of the view controller.
* Added `SearchBarAlignment` to allow placement of the `SearchBar` at the top or bottom of the view controller.
## 2.9.3
* Renamed `TabBarController` to `TabsController` to avoid name confusion with iOS `UITabBarController` helper properties.
* Updated layout with `TabsController` to properly adjust during orientation changes.
## 2.9.2
* `TabBarController` now subclasses `TransitionController` to minimize code.
* Fixed regression in `TabBarController` where line was incorrectly animation upon initial interaction.
## 2.9.1
* Renamed `TabsController` to `TabBarController`.
* Renamed `ChipsController` to `ChipBarController`.
* [issue-832](https://github.com/CosmicMind/Material/issues/832): Fixed issue where `TabBar` line was incorrectly laying out.
## 2.9.0
* Replaced `RootController` with `TransitionController`.
* Updated CharacterAttribute API.
* Added Chips - alpha phase.
* [issue-824](https://github.com/CosmicMind/Material/issues/824): Fixed issue where `TabBar` line alignment was not correctly animating when initial engaged.
* [issue-820](https://github.com/CosmicMind/Material/issues/820): Fixed retain cycle found in FABMenuController.
## 2.8.1
* [issue-815](https://github.com/CosmicMind/Material/issues/815): Fixed the TabBar line alignment issue when rotating the device orientation.
## 2.8.0
Removed PageTabBarController and added the TabsController. Now removing the following issues and feature requests:
* [issue-642](https://github.com/CosmicMind/Material/issues/642)
* [issue-742](https://github.com/CosmicMind/Material/issues/742)
* [issue-768](https://github.com/CosmicMind/Material/issues/768)
* [issue-605](https://github.com/CosmicMind/Material/issues/605)
* [issue-743](https://github.com/CosmicMind/Material/issues/743)
* [issue-619](https://github.com/CosmicMind/Material/issues/619)
## 2.7.1
* [issue-811](https://github.com/CosmicMind/Material/issues/811): Removed scrollable TabBar style, until feature is ready.
## 2.7.0
* Added [Motion](https://github.com/CosmicMind/Motion) framework to Material as new animation and transitions library.
* Removed `Capture`.
* Removed `PhotoLibrary`.
* Removed `Reminders`.
* [issue-809](https://github.com/CosmicMind/Material/issues/809): Fixed detailTextLabel visibility issue.
* [issue-797](https://github.com/CosmicMind/Material/issues/797): Added `back button` hiding features to `NavigationController`.
* [issue-552](https://github.com/CosmicMind/Material/issues/552): Fixed `@objc` declaration issues.
* [pr-662](https://github.com/CosmicMind/Material/pull/662): Added the ability to set a text inset for a `TextField`.
* [pr-707](https://github.com/CosmicMind/Material/pull/707): Moved the `statusBarStyle` extension to `NavigationController` from `UINavigationController`.
* Updated `Display` to `DisplayStyle` and renamed corresponding properties, in `ToolbarController`, `StatusBarController`, `SearchBarController`, `CaptureController`, and `ImageCard`.
## 2.6.3
* Fixed an issue where using child view controllers would break `presenting` and `dismissing` transition animations.
## 2.6.2
* [issue-608](https://github.com/CosmicMind/Material/issues/608): Updated `PageTabBarController` to allow programmatic selection of the current index using the `selectedIndex` property.
## 2.6.1
* Updated for Xcode 8.3.
## 2.6.0
* [issue-704](https://github.com/CosmicMind/Material/issues/704): Fixed an issue where `TextView.clipsToBounds` was revealing the scrollable text.
* [issue-663](https://github.com/CosmicMind/Material/issues/663): Added the ability to add insets for the `TextView`.
* [issue-598](https://github.com/CosmicMind/Material/issues/598): Added a placeholder to the `TextView`.
* Removed `Editor` and added all the necessary functionality in `TextView`.
## 2.5.2
* [issue-695](https://github.com/CosmicMind/Material/issues/695): Fixed issue with [pr-696](https://github.com/CosmicMind/Material/pull/696), where FABMenu was incorrectly being displayed used the SpringAnimation API.
## 2.5.1
* [issue-681](https://github.com/CosmicMind/Material/issues/681): Fixed regression where `NavigationBar` was not properly setting the background color.
* Fixed issue where transitions using `UINavigationController` would sometimes flicker.
## 2.5.0
* Renamed `FabButton` to `FABButton`.
* Moved `Menu` to `FABMenu`.
* Moved `MenuController` to `FABMenuController`.
* Added `FABMenuBacking` enum type to set a `blur` or `fade` for the opened `FABMenu` state using a `FABMenuController`.
* Renamed `MenuItem` to `FABMenuItem`.
* Added `SpringAnimation` animation API.
* [issue-641](https://github.com/CosmicMind/Material/issues/641): Added a new `PulseAnimation.tap` type, which has an instant feedback response when tapping.
* Updated `Toolbar.display` to `Toolbar.toolbarDisplay`.
* Updated `SearchBar.display` to `SearchBar.searchBarDisplay`.
* Added `StatusBar.statusBarDisplay`.
* Added `MotionAnimation` enum type and helper methods to `CALayer` and `UIView` as extensions.
* Added [Motion](https://github.com/CosmicMind/Motion) to Material.
## 2.4.19
* [issue-692](https://github.com/CosmicMind/Material/issues/692): Fixed issue where Carthage was failing to build the macOS target for Material.
## 2.4.18
* Removed macOS support.
## 2.4.17
* Updated Material.podspec to fix potential macOS issue.
## 2.4.16
* [issue-678](https://github.com/CosmicMind/Material/issues/678): Fixed issue where `Card` was incorrectly displaying its `contentView`.
## 2.4.15
* Fixed an issue where the `Card` was not displaying the `contentView` and `presenterView` correctly on initial load.
## 2.4.14
* Added missing `UIView` properties to `Material+UIView` extension that are used in the [Motion](https://github.com/CosmicMind/Motion) [PhotoCollection](https://github.com/CosmicMind/Samples/tree/master/Motion/PhotoCollection) sample.
## 2.4.13
* Added [Motion](https://github.com/CosmicMind/Motion) as a separate framework from [Material](https://github.com/CosmicMind/Material).
## 2.4.12
* [issue-676](https://github.com/CosmicMind/Material/issues/676): Fixed an issue where `Card` types were not adjusting size correctly when using `UILabels`.
## 2.4.11
* [issue-658](https://github.com/CosmicMind/Material/issues/658): Added `TextFieldPlaceholderAnimation` enum type to enable `TextField` to have different animations.
## 2.4.10
* Fixed an issue where `TabBar` was not correctly setting the `contentEdgeInsets*` and `interimSpace*`.
## 2.4.9
* [issue-655](https://github.com/CosmicMind/Material/issues/655): Updated date for release in README to 2017.
## 2.4.8
* [issue-653](https://github.com/CosmicMind/Material/issues/653): Fixed a `TextField` issue where the animation was not correctly responding to a programmatic text update.
## 2.4.7
* Updated README release dates.
## 2.4.6
* [issue-640](https://github.com/CosmicMind/Material/issues/640): Fixed an issue where `TextField` was not laying out correctly when setting the text programmatically.
## 2.4.5
* [issue-646](https://github.com/CosmicMind/Material/issues/646): Fixed an issue where calling `TextField.becomeFirstResponder` in `viewDidLoad` would cause a layout issue.
## 2.4.4
* [issue-644](https://github.com/CosmicMind/Material/issues/644): Fixed issue where `Switch.isOn` and `Switch.switchState` were not updating correctly.
## 2.4.3
* [issue-643](https://github.com/CosmicMind/Material/issues/643): Fixed an issue where `Layout` was not anchoring correctly for `View` subclasses.
## 2.4.2
* Fixed double `Menu.hitTest` call that was causing the delegation method to be fired more than once.
* Updated `Toolbar.title`, `Toolbar.titleLabel`, `Toolbar.detail`, and `Toolbar.detailLabel` to be `@IBInspectable`.
## 2.4.1
* [issue-194](https://github.com/CosmicMind/Material/issues/194): Fixed an issue where hitTest was failing after translation animation.
* [issue-624](https://github.com/CosmicMind/Material/issues/624): Updated `Switch` & `TabBar` control to only call the delegation methods when the control is updated through a user interaction.
* Renamed `Switch.on` to `Switch.isOn`.
* Removed `Switch.setOn` function.
* [issue-630](https://github.com/CosmicMind/Material/issues/630): Added `Reminders` and `RemindersController`.
* Added `isDividerHidden` for the `Divider UIView` extension.
## 2.4.0
* [issue-551](https://github.com/CosmicMind/Material/issues/551): Fixed issue where `TabBar` was not laying out buttons correctly when more than 6 were used.
* [issue-618](https://github.com/CosmicMind/Material/issues/618): Updated `Grid` and `Layout` to solve a `Toolbar` layout challenge.
* [issue-620](https://github.com/CosmicMind/Material/issues/620): Fixed issue where setting the `CAAnimation.delegate` was causing an animation issue.
## 2.3.22
* [issue-597](https://github.com/CosmicMind/Material/issues/597): Fixed an issue where `NavigationBar` was not adjusting to all sizes correctly when using modal presentation styles.
* Fixed an issue when `cornerRadius` was not being calculated correctly when the `CALayer` was rotated.
## 2.3.21
* [issue-612](https://github.com/CosmicMind/Material/issues/612): Fixed issue where SnackbarController was not resizing correctly.
* [issue-615](https://github.com/CosmicMind/Material/issues/615): Added `snackbarEdgeInsets` and `snackbarEdgeInsetsPreset` to position the `Snackbar` from the `SnackbarController's` edges.
## 2.3.20
* [issue-613](https://github.com/CosmicMind/Material/issues/613): Fixed an issue where the Grid system was not laying out in all cases that it should.
* Removed `statusBarStyle` and `isStatusBarHidden` properties from `RootController` types, in favor of using the `Application` class. `StatusBarController` now provides `statusBarStyle` and `isStatusBarHidden` properties.
## 2.3.19
* [issue-553](https://github.com/CosmicMind/Material/issues/553): Fixed an issue where the `NavigationDrawerController.leftViewController` was sizing incorrectly.
## 2.3.18
* [issue-610](https://github.com/CosmicMind/Material/issues/610): Fixed issue where the `RootController.rootViewController` was not transitioning correctly when using certain UIViewController types.
## 2.3.17
* Added the ability to modify the `contentViewAlignment` of a `NavigationItem` dynamically.
* Renamed the `ContentViewAlignment.any` value to `ContentViewAlignment.full`.
## 2.3.16
* Minor updates to `Card` types for code clarity.
## 2.3.15
* Fixed issue where `ImageCard` was not ordering the `UIImageView` behind the `Toolbar` correctly.
## 2.3.14
* Fixed an issue where iOS animations with `Motion` were not correctly writing their end value using `CAAnimationDelegate`.
## 2.3.13
* Fixed an issue where the `NavigationBar.backButton` was not placed at the most left position when present.
## 2.3.12
* Updates `Motion.translation*` to `Motion.translate*`.
* [issue-595](https://github.com/CosmicMind/Material/issues/595): Fixed issue where CAAnimations for iOS 10 were not working correctly.
* [issue-600](https://github.com/CosmicMind/Material/issues/600): Fixed issue where `Carthage` was not able to build due to failing to recognize the `NavigationDrawerController` gesture recognizer.
## 2.3.11
* [issue-600](https://github.com/CosmicMind/Material/issues/600): Fixed issue where `Carthage` was not able to build due to failing to recognize the `NavigationDrawerController` gesture recognizer.
## 2.3.10
* [issue-583](https://github.com/CosmicMind/Material/issues/583): Fixed issue where `TextField.detail` was not being displayed unless it was set upon preparation time.
* [issue-594](https://github.com/CosmicMind/Material/issues/594): Added feature request to set the `TextField.leftView` coloring based on the `normal` or `active` states, using the `TextField.detail .leftViewNormalColor` and `TextField.detail .leftViewActiveColor` properties respectively.
## 2.3.9
* [issue-584](https://github.com/CosmicMind/Material/issues/584): Added enum types for `Device.model` value.
* Divided `Device` into `Application`, `Device`, and `Screen`. This is for expansion of their APIs.
* Fixed issue where `NavigationDrawer` and `RootController` types would conflict when showing the statusBar.
## 2.3.8
* [issue-588](https://github.com/CosmicMind/Material/issues/588): removed memory leaks that surround Grid.
* `Grid` is now a struct from a class type.
* `Divider` is now a struct from a class type.
* `Pulse` is now a struct and views that have pulse now conform to the `Pulseable` protocol.
## 2.3.7
* [issue-592](https://github.com/CosmicMind/Material/issues/592): fixed `NavigationDrawerController` regression, where transitioning the `rootViewController` would go behind the `contentViewController`.
## 2.3.6
* Updated blur logic.
## 2.3.5
* [issue-591](https://github.com/CosmicMind/Material/issues/591): Fixed `blur` calculation complexity for `Carthage` compilation.
## 2.3.4
* [issue-588](https://github.com/CosmicMind/Material/issues/588): Fixed GridAxis memory leak issue.
## 2.3.3
* [issue-568](https://github.com/CosmicMind/Material/issues/568): fixed issue where `placeholderActiveColor` was not being set correctly when `TextField` was in an active state.
* [issue-568](https://github.com/CosmicMind/Material/issues/568): fixed issue where setting `text` to `nil` broke the `TextField` layout.
* [issue-581](https://github.com/CosmicMind/Material/issues/568): Added `UIImage.blur` extension.
## 2.3.2
* Fixed [issue-557](https://github.com/CosmicMind/Material/issues/577) where the transitioned view controller was overlapping the `RootController` type controls.
## 2.3.1
* Added `Capture` delegation methods that notify when `videoOrientation` changes.
## 2.3.0
* Merged in [PR-566](https://github.com/CosmicMind/Material/pull/566) to move `CAAnimation` String constants to enum types.
* Renamed the `Animation` struct to `Motion`, in order to initiate the expansion of the Motion library within Material.
* Fixed [issue-573](https://github.com/CosmicMind/Material/issues/573) where sample had incorrect spelling for `Material`.
* Updated `Bar` type layout mathematics.
* Updated `FabButton` default `backgroundColor` to `white`.
* Updated `Capture` API with [sample project](https://github.com/CosmicMind/Samples/tree/master/Material/Programmatic/CaptureController).
## 2.2.5
* Merged in [PR-563](https://github.com/CosmicMind/Material/pull/563) for [issue-549](https://github.com/CosmicMind/Material/issues/549), where Privacy related features are now using CocoaPods subspecs.
## 2.2.4
* Fixed issue where `ImageCard` `top` and `bottom` `EdgeInsets` were not being applied correctly.
## 2.2.3
* Updated Card internals for better performance.
* Added sample [CardTableView](https://github.com/CosmicMind/Samples/tree/master/Graph/CardTableView).
## 2.2.2
We moved all sample projects to a separate repo named [Samples](https://github.com/CosmicMind/Samples) to allow their development to be independent of the Material framework. There has been instances where we needed to update the versions of the framework to accommodate changes that only occurred in the sample projects.
## 2.2.1
* Fixed recursion issue with `Snackbar` and reloading.
* issue-552: Removed extraneous @objc declarations.
* Added `dividerContentEdgeInsets` and `dividerContentEdgeInsetsPreset` to Material+UIView extension.
## 2.2.0
* Updated default `pulseColor` to `Color.grey.base`.
* Added `HeightPreset` type to dynamically set the height of the `CALayer` & `UIView` types.
* `UIImage.tintWithColor(color: UIColor)` is now `UIImage.tint(with color: UIColor)` and always returns with `.alwaysOriginal` rendering mode.
* `UIImage.imageWithColor(color: UIColor)` is now `UIImage.image(with color: UIColor)` and always returns with `.alwaysOriginal` rendering mode.
* Merged in [PR 544](https://github.com/CosmicMind/Material/pull/544#pullrequestreview-3892111), which allows for the left view to be used in the `TextField`.
* Updated the `TextField` example project to reflect [PR 544](https://github.com/CosmicMind/Material/pull/544#pullrequestreview-3892111).
* Reworked `TextField`.
* Added `contentEdgeInsets` to `Divider`.
* Fixed issue where `pulse layer` was covering `Button` images when engaged.
* For `Divider`, renamed `dividerHeight` to `dividerThickness` to accommodate alignment logic.
* Added SearchBar delegation methods for when text changes and for when the text has been cleared.
## 2.1.2
* Updated default `pulseColor` to `Color.white`.
* Updated [NavigationDrawerController Example Project](https://github.com/CosmicMind/Material/tree/master/Examples/Programmatic/NavigationDrawerController) to demonstrate how to transition the `rootViewController`, both with a `ToolbarController` and without, issue-546.
## 2.1.1
* Moved the Switch `trackLayer` property from a `CAShapeLayer` to a `UIView` that is now named `track`.
* Fixed an issue where `Switch` in a `Bar` type or `NavigationBar` would behave incorrectly, issue-540.
* Added a `ToolbarController` to the programmatic `NavigationDrawerController` example project.
## 2.1.0
* Added a new feature where `TextField`'s `placeholder` can be fixed at the top and not animated by setting the `isPlaceholderAnimated` property to `false` (issue-534).
* Added a new feature where a `centerViews` property is added to `Bar` types, to automatically align views in the center outward positions. Updated sample `Card` projects and `Bar` projects to reflect this.
* Added a new feature to `Grid`, where `columns` and `rows` are automatically set if their values have not been changed from the default `0` value.
## 2.0.0
* Renamed `MaterialColor` to `Color`.
* Renamed `MaterialIcon` to `Icon`.
* Renamed `MaterialSpacing` to `InterimSpacePreset`.
* Renamed `MaterialButton` to `Button`.
* Renamed `MaterialView` to `View`.
* Renamed `MaterialPulseView` to `PulseView`.
* Renamed `MaterialSwitch` to `Switch`.
* Renamed `MaterialLayer` to `Layer`.
* Renamed `MaterialFont` to `Font.
* Renamed `MaterialEdgeInset` to `EdgeInsetsPreset`.
* Renamed `MaterialDevice` to `Device`.
* Renamed `MaterialDepth` to `Depth`.
* Renamed `CaptureView` to `Capture`.
* Renamed `CardView` to `Card`.
* Renamed `ImageCardView` to `ImageCard`.
* Renamed `MaterialBorder` to `BorderWidthPreset`.
* Renamed `MaterialRadius` to `CornerRadiusPreset`.
* Renamed `MaterialDataSourceItem` to `DataSourceItem`.
* Renamed `MaterialTableViewCell` to `TableViewCell`.
* Renamed `shadowPathAutoSizeEnabled` to `isShadowPathAutoSizing`.
* Fixed issue where TextField placeholder was not respecting initial vertical offset (issue-469).
* Added @objc to all enums to allow Obj-C to see the enum types and associated methods (issue-472).
* Added `PageTabBarController`.
* Added `JSON` to simplify working with JSON objects.
## 1.42.9
* Fixed issue where `textColor` was not being respected in Storyboards for `TextField` (issue-487).
## 1.42.8
* Fixed issue where initially setting the `TextField` did not correctly align the `placeholder` offset (issue-469).
## 1.42.7
* Fixed issue where `TextField` alignment was incorrect with RTL (issue-456).
## 1.42.6
* Fixed issue where `StatusBarController` was not calling its `super.prepareView` method.
## 1.42.5
* Fixed issue where NavigationBar was not aligning correctly with Storyboards.
* Updated `CGRectZero` values to `CGRect.zero`.
* Minor cleanups.
## 1.42.4
* Fixed issue where left and right view controllers are not enabled on `NavigationDrawerController` (issue-452).
## 1.42.3
* Fixed an issue where `MaterialSwitch` was exposing a memory leak with its `delegate` (issue-449).
* Fixed a regression where FabButton was losing shape (issue-450).
* Updated property `enableHideStatusbar` to `enableHideStatusBar` for `NavigationDrawerController`.
* Updated `CGRectZero` to `CGRect.zero`.
## 1.42.2
* Fixed an issue where `Toolbar` and `NavigationBar` `title` alignment was off with a `detail` value of "" (issue-445).
* Fixed an issue where `NavigationDrawerController` was crashing when transitioning between `rootViewController` (issue-444).
## 1.42.1
* Fixed issue with NavigationDrawerController rightView not aligning correctly when rotating device.
## 1.42.0
* Update `shadowPath` animation to happen when laying out subviews, rather than when laying out sublayers.
* Renamed `MaterialLayout` to `Layout` for simplicity.
* Added `Layout` extension to ease the usage of AutoLayout.
* Added [Layout Documentation](http://www.cosmicmind.io/material/layout).
* Updated the `MaterialLayout` project to `Layout`.
* fixed width issue for all `ControlView` types when using dynamic `intrinsicContentSize` (issue-436).
* Renamed `SideNavigationController` to `NavigationDrawerController`.
* Removed `SideNavigationController` example project for both programmatic and storyboards.
* Added `NavigationDrawerController` example project for both programmatic.
* Added `StatusBarController` to manage a statusBarView.
## 1.41.8
* Fixed an issue where the `UINavigationItem.title` KVO would crash when releasing the instance. The fix completely removes KVO and utilizes Swift `nonobjc` tagging.
## 1.41.7
* Added `adjustOrientationForImage` to `CaptureSession` in order to fix image alignment issues.
* Updated `CaptureView` sample project to reflect changes made in `ToolbarController`.
* Updated `MaterialView` sample project to demonstrate aligning a `MaterialView` in the `center` of a view controller.
* Fixed issue where UINavigationItem.title was not updating the titleLabel text.
## 1.41.6
* Fixed issue with ellipses in NavigationBar showing when panning back to the backItem (pr-409).
## 1.41.5
* Added delegation method `menuViewDidTapOutside` to `MenuView` to support closing the `Menu`, `MenuView`, or `MenuController` items when opened and clicking on any area of the view (issue-406).
## 1.41.4
* Removed `statusBarStyle` from `BarView` types.
* Added `statusBarStyle` to BarController types.
* Added `layoutInset` to `Grid` for an additional layer of flexibility.
* Fixed an issue where BarControllers were not allowing `contentInset.top` to be used correctly.
* Updated projects to reflect framework changes.
## 1.41.3
* Fixed issue where `MaterialSwitch` was referencing self and creating a bad access (issue-399).
* Fixed issue where `TextField.secureTextEntry` would break the font being displayed (issue-400).
* Moved `MenuViewController` to `MenuController`.
* Updated `MenuController.itemViewSize` to `MenuController.itemSize`.
* Updated `MenuController.baseViewSize` to `MenuController.baseSize`.
* Updated all references to `unowned self` to `weak self`.
* Added convenience properties `title` and `detail` to the `Toolbar`, which reload the view when changed.
* Added convenience properties `title` and `detail` to UINavigationItem to easily handle text changes.
* added to `TextField` the `placeholderVerticalOffset` and `detailVerticalOffset` to determine the alignment during animations and loading of the `placeholderLabel` and `detailLabel`.
## 1.41.2
* Fixed issue where Toolbar was not respective the frame size set (issue-382).
* Fixed issue where Toolbar was not drawing the titleLabel and detailLabel text without left/right controls (issue-381).
* `StatusBarView` is now `BarView`.
* `StatusBarViewController` is now `BarViewController`.
## 1.41.1
* Fixed text alignment issue in NavigationBar and Toolbar.
## 1.41.0
* All references to `detailView` are now `contentView`.
* Updated NavigationBar interface.
* Reworked NavigationBar.
* Reworked Toolbar measurements.
* Reworked SearchBar measurements.
## 1.40.1
* Fixed issue where initializing a Toolbar in a method was causing an ambiguous initializer error (issue-363).
* Added Boolean properties to SideNavigationController to enable and disable gestures (issue-365).
```swift
sideNavigationController.enabled = true
sideNavigationController.enabledLeftView = true
sideNavigationController.enabledLeftTapGesture = true
sideNavigationController.enabledLeftPanGesture = true
sideNavigationController.enabledRightView = true
sideNavigationController.enabledRightTapGesture = true
sideNavigationController.enabledRightPanGesture = true
```
* Updated the SideNavigationController `leftThreshold` and `rightThreshold` to 64 as a default.
* Updated MaterialIcon images to work better with CocoaPods (issue-362).
## 1.40.0
* Added Google visibility icon to MaterialIcon.
* Added Google check icon to MaterialIcon.
* Reworked TextField with [documentation](http://www.cosmicmind.io/material/textfield).
* Added ErrorTextField.
* Added visibility button and clear button auto enabling without conflicting with iOS clearButton for TextField.
* Reworked pulse animations.
* Added `PulseAnimation` enum type to select the type of pulse animation.
* Added `IconButton` to simplify the usage of using icons and buttons.
* Fixed issue where panning gestures were conflicting with the SideNavigationController rootViewController (1ssue-322, issue-320).
## 1.39.17
* Updated MaterialDepth to more accurately express Material Design's shadows (issue-323).
* Fixed an issue where MaterialButtons could not update `textColor` (issue-333).
## 1.39.16
* Fixed issue where TextField `resignFirstResponder` was not hiding the `titleLabel` (issue-332).
* TextField no longer needs to setup `detailLabel` property.
* TextField `detailLabel` now supports @IBInspectable.
## 1.39.15
* Fixed issue where TextField doesn't hide the titleLabel when programmatically cleared (issue-330) (pr-331).
## 1.39.14
* Added UIImage extension `tintWithColor`, which allows an image to be tinted with a passed in color.
* Added `pulseCenter` property, which forces the pulse animation to animate from the center of the view (pr-325).
* Updated `prepareView` to be public, which allows for better subclassing and preparation of views (pr-329).
* Fixed issue where TextField regressed when updating the `placeholder` value (issue-316).
## 1.39.13
* Fixed issue where TextField `placeholder` could be updated while a text value exists (issue-316).
## 1.39.12
* Updated Example/Programmatic/SideNavigationController project to demonstrate how to transition the rootViewController (issue-309).
## 1.39.11
* Added a link to download our sticker sheet.
* Updated App project with correct naming in AppDelegate file.
## 1.39.10
* README Update.
## 1.39.9
* Added storyboard CardView example with two CardViews (issue-304).
## 1.39.8
* Fixed issue where TextField animation references `unowned self` and should be `weak self` (issue-301) (pr-302).
* Added `lineLayerThickness` and `lineLayerActiveThickness` to TextField in order to adjust lineLayer during different states (issue-307).
## 1.39.7
* Fixed issue where TextField delegate method `textFieldShouldClear` was not being respected (issue-296).
## 1.39.6
* Updated TextField's default colors to the correct Material Design colors (pr-290).
* Added UIImage blur effect (pr-291).
* Added UIImage blur example project, FilterBlur.
* Added `SideNavigationController.statusBarUpdateAnimation` property to set the animation type when hiding the statusBar.
* Added `SideNavigationController.statusBarStyle` property to set the statusBar style.
## 1.39.5
* Added MaterialIcon example project.
* Added additional Google and CosmicMind icons.
* Added MaterialFontLoader to aid in loading packaged fonts with Material.
* Updated App project to properly handle SideNavigationController ```openLeftView``` if used.
## 1.39.4
* Updated the TextField animations.
## 1.39.3
* Fixed bundle identifier issue with CocoaPods and MaterialIcon.
## 1.39.2
* Updated Material bundle identifier.
## 1.39.1
* Updated TextField to match Material Design input text spec.
## 1.39.0
* Added early release of TabBar.
* Updated default `spacing` to `Spacing1` for Toolbar and SearchBar.
* Updated default `contentInsetPreset` to `Square1` for Toolbar, SearchBar, and NavigationBar.
* Updated ```MaterialGravityToString``` to ```MaterialGravityToValue```.
* TextField's ```clearButton``` no longer needs to be setup.
* TextField's ```titleLabel``` no longer needs to be setup (issue-241).
* TextField and SearchBar - added ```clearButtonAutoHandleEnabled``` flag that when set to ```false```, the handler for clearing text is removed (issue-229).
* TextField's ```titleLabelAnimationDistance``` is now 4 by default.
* TextField's ```titleLabel``` now floats when focused (issue-203).
* TextField's ```bottomBorderLayerDistance``` is now ```lineLayerDistance```.
* TextField now supports the ```lineLayerActiveColor```, which is set when the TextField is focused.
* TextField now supports the ```lineLayerDetailActiveColor```, which is set when the TextField detailLabel is displayed.
* Fixed issue-203, where TextField's ```lineLayer``` property was not the proper thickness when active.
* Fixed regression, where the TextField's ```bottomLayer``` color was not showing (issue-276).
* Removed shape code that was not needed in TextField and TextView.
* Updated example projects using SearchBar, and simplified the configuration for the SearchBar.
* Added OSX build target.
* Added MaterialColor to OSX.
* Added Google icons as default for ```MaterialIcon``` and additional CosmicMind icons with ```cm``` namespace (issue-285).
## 1.38.5
* Fix for MaterialIcon, [issue-279](https://github.com/CosmicMind/Material/issues/279).
* Fix for touch consumption, [issue-280](https://github.com/CosmicMind/Material/issues/280).
## 1.38.4
* Added custom icons to MaterialIcon.
## 1.38.3
* Updated App example project.
* Added MaterialCollectionView example project.
* Added NavigationController storyboard example project.
* Default colors for MaterialSwitch moved from MaterialColor.lightBlue.* to MaterialColor.blue.*.
* Updated MaterialColor with corrections to the colors from Material Design (issue-271).
## 1.38.2
* Switched BottomNavigationBar to BottomTabBar, as it is more appropriate in the context of iOS. BottomNavigationController will be comprised of a SnackBar (coming soon), and BottomTabBar.
* Added fix for Interface Builder, where MaterialSwitch was causing crashes (issue-259).
* Fixed issue where MaterialSwitch color was not propagating until engaged (issue-260).
## 1.38.1
* Added missing imports for UIKit in MaterialCollectionView classes.
## 1.38.0
* Added BottomNavigationBar.
* Added default Fade animation to BottomNavigationBar.
* Added programmatic BottomNavigationBar example project.
* Added storyboard BottomNavigationBar example project.
* Added BottomNavigationController.
* Added programmatic BottomNavigationController example.
* Removed c-style ```for``` loops.
* Updated bundle detection for MaterialIcon.
* References for `mainViewController` are now `rootViewController`.
* References for `transitionFromMainViewController` are now `transitionFromRootViewController`.
* The example App project demonstrates how to hide the `statusBar` without the `navigationBar` being animated.
* Fixed warnings for Swift 3.
* Added detection for latest iPad and iPhone.
## 1.37.3
* Fixed issue-244, where Test Coverage flag was causing an error.
## 1.37.2
* Updated the default width of the SideNavigationController to reflect the Material Design spec more accurately. For mobile, the default width is 280px, and for tablet, the default width is 320px.
## 1.37.1
* Removed SideNavigationControllerDelegate call from NavigationController class that was not used.
## 1.37.0
* Added `pulseFocus` Boolean flag that keeps the pulse displayed as the button is highlighted (issue-217).
* Switched `pulseColorOpacity` to `pulseOpacity`.
* Added @IBInspectable where appropriate (issue-151).
* Added @IBDesignable where appropriate (issue-151).
* Fixed an issue where the tests were being broken (issue-224).
* `NavigationBarView` is now called `Toolbar`.
* `SearchBarView` is now called `SearchBar`.
* `SideNavigationViewController` is now `SideNavigationController`.
* Added public `leftThreshold` and `rightThreshold` to `SideNavigationController` (issue-220).
## 1.36.0
* Added class NavigationBar and NavigationController.
* Added contentsGravityPreset where appropriate, and now contentsGravity takes a String.
* Fixed issue-213, `shadowPathAutoSizeEnabled` now defaults to true, from false.
## 1.35.3
* MaterialAnimation.rotate now accepts an `angle` parameter or a `rotation` parameter.
* Fixed issue-194, where the MaterialAnimation.translation animations were not capturing the final state value.
* Fixed issue-201, by guaranteeing that the minimal iOS version is 8.
## 1.35.2
* Updated storyboard NavigationBarView example project to correctly set the height of the NavigationBarView when rotating orientations on both iPad and iPhone.
* Removed NavigationBarViewDelegate and SearchBarViewDelegate.
## 1.35.1
* Fixed issue-195, where MaterialSwitch was switching the switchState automatically through the highlighted property.
* Fixed issue-196, where NavigationBarView was not sizing correctly on iPad.
* Added MaterialDevice helper class that provides useful values for orientation and bounds size.
## 1.35.0
* Removed backDropLayer for SideNavigationViewController. Now, the mainViewController.view property is set to 50% alpha when opened and 100% alpha when closed. The color is adopted from the backgroundColor of the mainViewController.view property.
* MenuViewController now animates its backdrop effect.
## 1.34.10
* Added `shadowPathAutoSizeEnabled` property that enables auto sizing for the shadowPath property. Defaults to false.
## 1.34.9
* Fixed shadowPath animation on rotation.
* Proposed fixed for NavigationBarView geometry issue.
* Updated CardView and ImageCardView to have a default rounded corner of .Radius1 as in the Material Design spec.
* Updated the example App project.
## 1.34.8
* Updated example App project.
## 1.34.7
* Updated App project example, by showing how to switch NavigationBarViewController's mainViewController from a SideNavigationViewController.
* Added a FeedViewController to the example App project that shows how to use CardViews in a MaterialCollectionView.
* Fixed a performance issue with shadows and animations, issue-186.
## 1.34.6
* Fixed issue with MaterialSwitch, issue-181.
* Fixed issue with NavigationBarView Geometry, issue-179.
* Added MaterialCollectionView, MaterialCollectionViewLayout, MaterialCollectionViewCell.
* Added enum MaterialSpacing type for spacing presets, spacingPreset.
* Added TextField storyboard example.
* Rework to better the performance of animations with depth.
## 1.34.5
* Fixed breaking examples.
## 1.34.4
* Added **NavigationBarViewControllerDelegate** that monitors the state of the `floatingViewController` property.
* Added **TextField** `detailLabelAutoHideEnabled`property that flags the animation to hide the detailLabel automatically when the text value has changed. Default is true.
## 1.34.3
* Added headers to public build phase.
## 1.34.2
* License text update.
## 1.34.1
* CaptureView example has been updated to use the latest NavigationBarView API.
* NavigationBarView default sizing:
- Only titleLabel, font size is 20.
- With titleLabel and detailLabel, font size is 17 for titleLabel, and 12 for detailLabel.
* Example projects updated.
## 1.34.0
* Added App example project in Examples/Programmatic.
* Added ControlView.
* Added StatusBarView.
* NavigationViewController is now NavigationBarViewController.
* Added StatusBarViewController.
* Added SearchBarViewController.
* MaterialSwitch now supports setting the "on", "selected", "highlighted", and "switchState" properties to toggle the state of the control.
* MaterialSwitch now supports setOn(on: Bool, animated: Bool) method to switch the state of the control.
* MaterialSwitch now supports 'on', 'highlighted', 'selected', 'state', and 'switchState' public mutators.
* MaterialSwitchDelegate updated 'materialSwitchStateChanged' delegation method to only pass a reference to the control, rather than control and state value.
* Added MenuViewController.
* SideNavigationBarViewController is now SideNavigationViewController.
* UIViewController Optional property sideNavigationBarViewController is now sideNavigationViewController.
* Added TextField placeholderTextColor property to set the placeholder text color.
* TextField detailLabel property hides automatically when typing.
* TextField now supports a custom clear UIButton by setting the clearButton property.
## 1.33.2
* Updated SerchBarView Example.
* A rotation issue is fixed when in landscape mode and toggling the SideNavigationViewController, where the statusBar would
* Added MaterialSwitch UIControl component with example projects in the Examples/Programmatic directory.
* MaterialEdgeInsetPreset is now MaterialEdgeInset.
* MaterialRadius preset values are now supported through the cornerRadiusPreset property. The cornerRadius property now supports CGFloat values directly.
* Updated TableCardView example.
* Added SearchBarView.
* Updated NavigationBarView API.
* Added NavigationViewController.
* Updated TextField and TextView issue, where letters such as "y g p" would not display correctly.
* MaterialButton has updated contentInsetPreset to contentEdgeInsetsPreset.
* Added MaterialTableViewCell with pulse animation.
* Updated SideNavigationViewController example project.
## 1.32.2
* Fixed an issue with MenuView, where outside views were not detected when touched.
* Updated API to reference views.
## 1.32.1
* MenuView wraps a Menu with a MaterialPulseView to ease the use of laying out menus, as well as, provide a more robust approach to Menus.
* Menus now hold an array of UIViews, allowing any UIView to be animated with Menu.
* The borderWidth property for Material views no longer uses an enum MaterialBorder type. It now supports the CGFloat type.
## 1.32.0
* CardView and ImageCardView no longer support the detailLabel property. Now, a detailView property has been added to allow any UIView, including UILabel to be added in the detail area.
## 1.31.6
* Grid now supports management of rows and columns in a single mapping.
* Updated Grid examples.
* Visit the Examples directory to see example projects using Material.
## 1.31.5
* Updated README.
## 1.31.4
* Updated Grid Example.
* Updated README.
## 1.31.3
* Updated Material usage description.
## 1.31.2
* Introducing Material Grid. A flexible grid system to handle complex layouts.
* Default depth is now .Depth1.
## 1.31.1
* Added two more examples using Menu.
## 1.31.0
* Added a new component, Menu!
* A Menu manages a group of UIButtons that may be animated open in the Up, Down, Left, and Right directions. The animations
## 1.30.2
* Fixed an issue where toggling the SideNavigationViewController enabled property caused the 'left' and 'right' view to stop
## 1.30.1
* Updated MaterialView example.
## 1.30.0
* Updated pulse animation to be a wave.
* Added `pulse` method to programmatically trigger the pulse animation.
* Updated examples to reflect pulse changes.
* Updated README to reflect pulse changes.
* Removed `pulseFill` and `spotlight` from pulse animatable views as they are no longer needed.
## 1.29.4
* Some minor updates to the internal references used for animations.
* Removed Task example to its own repository.
## 1.29.3
***This update is recommended.***
* Major update to SideNavigationViewController internals and animation.
* Added an additional duration parameter to setLeftViewWidth with animation and setRightViewWidth with animation.
## 1.29.2
***This is a recommended update.***
* Updated internal animations to handle UIView animations correctly.
* Updated SideNavigationViewController to allow quick swipe to close.
* Updated SideNavigationViewController to not block view touches when tap gesture is triggered.
* Updated workspace reference to Material project.
## 1.29.1
* To remove deprecated warnings, and provide an overall better solution, UIImage now utilizes NSURLSession for asynchronous image loading.
* The MaterialLayer example project demonstrates this feature update.
## 1.29.0
* SideNavigationViewController now supports the Right position for View Controllers.
* SideNavigationViewController API updates should be reviewed from the Class file.
## 1.28.1
* Updated README.
## 1.28.0
* Updated framework name to Material to avoid conflicts with other frameworks.
* Updated default TextField animation distance for titleLabel to 8.
* Updated default TextView animation distance for titleLabel to 8.
## 1.27.14
* The SideNavigationViewController example project has been updated.
* Updated README.
## 1.27.13
* An updated SideNavigationViewController example has been added to the Examples Programmatic directory. It includes a fresh new look and a reusable template for your applications.
## 1.27.12
* Updates to the TextView and TextField were made to follow Google's spacing guidelines.
* An issue with setting the TextView or TextField's text property has been fixed, where the title label was not being
* Additional updates were made to many of the example projects.
## 1.27.11
* Updated License Format.
## 1.27.10
* Updated License Format.
## 1.27.9
* Material is moving to a BSD license.
## 1.27.8
* Added @objc to TextView and TextViewDelegate to eliminate conflicts when using swift within an Objective-C project.
Below is an example of a medium CardView using Grid.
## 1.27.7
* Fixed an issue where the optional TextView.text property was being accessed and throwing an error when used in combination with Objective-C.
## 1.27.6
* Updated TextView internals to avoid optimization build issue with Carthage.
* Updated icon set in example projects.
* Updated Resources folder with latest graphics.
* Updated README.
## 1.27.4
* Removed the File Class.
* Updated README to correct TextField example.
Right out of the box to a fully customizable configuration, CardView always stands out. Take a look at a few examples in action.
### 1.27.2
* **titleLabelTextColor** is now **titleLabelColor**.
* **titleLabelActiveTextColor** is now **titleLabelActiveColor**.
* **detailLabelActiveTextColor** is now **detailLabelActiveColor**.
* **titleLabelTextColor** is now **titleLabelColor**.
* **titleLabelActiveTextColor** is now **titleLabelActiveColor**.
## 1.27.1
* Updated README to include Changelog link.
## 1.27.0
* Has been removed completely.
* **MaterialEdgeInsets** is now **MaterialEdgeInsetPreset**.
* **contentInsets** is now **contentInsetPreset**.
* **contentInsetsRef** is now **contentInset**.
* **dividerInsets** is now **dividerInsetPreset**.
* **dividerInsetRef** is now **dividerInset**.
* **contentInsets** is now **contentInsetPreset**.
* **contentInsetsRef** is now **contentInset**.
* **titleLabelInsets** is now **titleLabelInsetPreset**.
* **titleLabelInsetsRef** is now **titleLabelInset**.
* **detailLabelInsets** is now **detailLabelInsetPreset**.
* **detailLabelInsetsRef** is now **detailLabelInset**.
* **leftButtonsInsets** is now **leftButtonsInsetPreset**.
* **leftButtonsInsetsRef** is now **leftButtonsInset**.
* **rightButtonsInsets** is now **rightButtonsInsetPreset**.
* **rightButtonsInsetsRef** is now **rightButtonsInset**.
* **dividerInsets** is now **dividerInsetPreset**.
* **dividerInsetRef** is now **dividerInset**.
* **contentInsets** is now **contentInsetPreset**.
* **contentInsetsRef** is now **contentInset**.
* **titleLabelInsets** is now **titleLabelInsetPreset**.
* **titleLabelInsetsRef** is now **titleLabelInset**.
* **detailLabelInsets** is now **detailLabelInsetPreset**.
* **detailLabelInsetsRef** is now **detailLabelInset**.
* **leftButtonsInsets** is now **leftButtonsInsetPreset**.
* **leftButtonsInsetsRef** is now **leftButtonsInset**.
* **rightButtonsInsets** is now **rightButtonsInsetPreset**.
* **rightButtonsInsetsRef** is now **rightButtonsInset**.
* **shrink** is now **shrinkAnimation**.
* **wrapped** is no longer an Optional.
* **contentsScale** is no longer an Optional.
* **shrink** is now **shrinkAnimation**.
* **updatedPulseLayer** is now **updatePulseLayer**.
* **contentInsets** is now **contentInsetPreset**.
* **contentInsetsRef** is now **contentInset**.
* **titleLabelInsets** is now **titleLabelInsetPreset**.
* **titleLabelInsetsRef** is now **titleLabelInset**.
* **detailLabelInsets** is now **detailLabelInsetPreset**.
* **detailLabelInsetsRef** is now **detailLabelInset**.
* **leftButtonsInsets** is now **leftButtonsInsetPreset**.
* **leftButtonsInsetsRef** is now **leftButtonsInset**.
* **rightButtonsInsets** is now **rightButtonsInsetPreset**.
* **rightButtonsInsetsRef** is now **rightButtonsInset**.
* When setting the **enabled** property, gestures are removed and added appropriately.
* **titleLabelNormalColor** is now **titleLabelTextColor**.
* **titleLabelHighlightedColor** is now **titleLabelActiveTextColor**.
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@cosmicmind.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guidelines
This document contains information and guidelines about contributing to this project.
Please read it before you start participating.
**Topics**
* [Pull Request Submissions](#pull-request-submissions)
* [Asking Questions](#asking-questions)
* [Reporting Security Issues](#reporting-security-issues)
* [Reporting Issues](#reporting-other-issues)
* [Developers Certificate of Origin](#developers-certificate-of-origin)
* [Code of Conduct](#code-of-conduct)
## Pull Request Submissions.
All pull requests should be made to the development branch. Details should be available describing your fix. Before submitting a pull request, please confirm that merge issues are resolved.
## Asking Questions
We don't use GitHub as a support forum.
For any usage questions that are not specific to the project itself,
please ask on [Stack Overflow](http://stackoverflow.com/questions/tagged/cosmicmind) instead.
By doing so, you'll be more likely to quickly solve your problem,
and you'll allow anyone else with the same question to find the answer.
This also allows maintainers to focus on improving the project for others.
## Reporting Security Issues
CosmicMind takes security seriously.
If you discover a security issue, please bring it to our attention right away!
Please **DO NOT** file a public issue,
instead send your report privately to .
This will help ensure that any vulnerabilities that _are_ found
can be [disclosed responsibly](http://en.wikipedia.org/wiki/Responsible_disclosure)
to any affected parties.
## Reporting Other Issues
A great way to contribute to the project
is to send a detailed issue when you encounter an problem.
We always appreciate a well-written, thorough bug report.
Check that the project issues database
doesn't already include that problem or suggestion before submitting an issue.
If you find a match, add a quick "+1" or "I have this problem too."
Doing this helps prioritize the most common problems and requests.
When reporting issues, please include the following:
* The version of Xcode you're using
* The version of iOS you're targeting
* The full output of any stack trace or compiler error
* A code snippet that reproduces the described behavior, if applicable
* Any other details that would be useful in understanding the problem
This information will help us review and fix your issue faster.
## Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
- (a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
- (b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
- (c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
- (d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
---
*Some of the ideas and wording for the statements above were based on work by the [Alamofire](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) and [Linux](http://elinux.org/Developer_Certificate_Of_Origin) communities. We commend them for their efforts to facilitate collaboration in their projects.*
================================================
FILE: Cartfile
================================================
github "CosmicMind/Motion" >= 1.2.2
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (C) 2019, CosmicMind, Inc. .
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: Material.podspec
================================================
Pod::Spec.new do |s|
s.name = 'Material'
s.version = '3.1.8'
s.swift_version = '5.0'
s.license = 'BSD-3-Clause'
s.summary = 'A UI/UX framework for creating beautiful applications.'
s.homepage = 'http://cosmicmind.com'
s.social_media_url = 'https://www.facebook.com/cosmicmindcom'
s.authors = { 'CosmicMind, Inc.' => 'support@cosmicmind.com' }
s.source = { :git => 'https://github.com/CosmicMind/Material.git', :tag => s.version }
s.default_subspec = 'Core'
s.platform = :ios, '8.0'
s.subspec 'Core' do |s|
s.ios.deployment_target = '8.0'
s.ios.source_files = 'Sources/**/*.swift'
s.requires_arc = true
s.resource_bundles = {
'com.cosmicmind.material.icons' => ['Sources/**/*.xcassets'],
'com.cosmicmind.material.fonts' => ['Sources/**/*.ttf']
}
s.dependency 'Motion', '~> 3.1.1'
end
end
================================================
FILE: Material.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
961154CC1F32A7B100A78D74 /* ChipBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961154CB1F32A7B100A78D74 /* ChipBar.swift */; };
961527B91F3A509900E8B2AC /* ChipBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961527B81F3A509900E8B2AC /* ChipBarController.swift */; };
9617B07D1DFCA8CF00410F8F /* Application.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961E6BDE1DDA2A95004E6C93 /* Application.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B07E1DFCA8CF00410F8F /* Card.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB75D1CB40DC500C806FE /* Card.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B07F1DFCA8CF00410F8F /* ImageCard.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7621CB40DC500C806FE /* ImageCard.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0801DFCA8CF00410F8F /* PresenterCard.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9631A7C01D95E3AC00CFB109 /* PresenterCard.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9626CB9A1DAD3D1D003E2611 /* HeightPreset.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B08A1DFCA8CF00410F8F /* DisplayStyle.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9626CA961DAB53A8003E2611 /* DisplayStyle.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B08B1DFCA8CF00410F8F /* Screen.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961E6BE11DDA2AF3004E6C93 /* Screen.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B08C1DFCA8CF00410F8F /* SearchBar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7951CB40DC500C806FE /* SearchBar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B08D1DFCA8CF00410F8F /* SearchBarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7961CB40DC500C806FE /* SearchBarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B08E1DFCA8CF00410F8F /* TabBar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB79A1CB40DC500C806FE /* TabBar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B08F1DFCA8CF00410F8F /* Material+NSMutableAttributedString.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961276621DCD8B1800A7D920 /* Material+NSMutableAttributedString.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0901DFCA8CF00410F8F /* Toolbar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB79F1CB40DC500C806FE /* Toolbar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0911DFCA8CF00410F8F /* ToolbarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7A01CB40DC500C806FE /* ToolbarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9618006D1F4D384200CD77A1 /* Material+UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9618006C1F4D384200CD77A1 /* Material+UIViewController.swift */; };
9618006E1F4D38BC00CD77A1 /* ChipBar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961154CB1F32A7B100A78D74 /* ChipBar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9618006F1F4D38BC00CD77A1 /* ChipBarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961527B81F3A509900E8B2AC /* ChipBarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
961800701F4D38BC00CD77A1 /* Material+UIViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9618006C1F4D384200CD77A1 /* Material+UIViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
961E6BDF1DDA2A95004E6C93 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961E6BDE1DDA2A95004E6C93 /* Application.swift */; };
961E6BE21DDA2AF3004E6C93 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961E6BE11DDA2AF3004E6C93 /* Screen.swift */; };
96328B7A1E020A41009A4C90 /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96328B791E020A41009A4C90 /* CollectionViewController.swift */; };
96328B971E05C0BB009A4C90 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96328B961E05C0BB009A4C90 /* TableView.swift */; };
96328B991E05C0CE009A4C90 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96328B981E05C0CE009A4C90 /* TableViewController.swift */; };
96328B9B1E05C24E009A4C90 /* CollectionViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96328B791E020A41009A4C90 /* CollectionViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96328B9E1E05C24E009A4C90 /* TableView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96328B961E05C0BB009A4C90 /* TableView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96328B9F1E05C24E009A4C90 /* TableViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96328B981E05C0CE009A4C90 /* TableViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96334EF61C8B84660083986B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96334EF51C8B84660083986B /* Assets.xcassets */; };
9656895F1F002F16001C656D /* CardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9656895E1F002F16001C656D /* CardCollectionViewCell.swift */; };
965689611F002F4C001C656D /* CardCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965689601F002F4C001C656D /* CardCollectionViewController.swift */; };
965E80CC1DD4C50600D61E4B /* Bar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7981CB40DC500C806FE /* Bar.swift */; };
965E80CD1DD4C50600D61E4B /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7701CB40DC500C806FE /* Button.swift */; };
965E80CE1DD4C50600D61E4B /* FABButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB75F1CB40DC500C806FE /* FABButton.swift */; };
965E80CF1DD4C50600D61E4B /* FlatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7601CB40DC500C806FE /* FlatButton.swift */; };
965E80D01DD4C50600D61E4B /* RaisedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7931CB40DC500C806FE /* RaisedButton.swift */; };
965E80D11DD4C50600D61E4B /* IconButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9658F2161CD6FA4700B902C1 /* IconButton.swift */; };
965E80D21DD4C50600D61E4B /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7761CB40DC500C806FE /* Color.swift */; };
965E80D31DD4C50600D61E4B /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7791CB40DC500C806FE /* Device.swift */; };
965E80D41DD4C50600D61E4B /* Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96230AB71D6A520C00AF47DC /* Divider.swift */; };
965E80D51DD4C50600D61E4B /* Grid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7611CB40DC500C806FE /* Grid.swift */; };
965E80D61DD4C50600D61E4B /* HeightPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9626CB9A1DAD3D1D003E2611 /* HeightPreset.swift */; };
965E80D71DD4C50600D61E4B /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB77D1CB40DC500C806FE /* Icon.swift */; };
965E80D81DD4C50600D61E4B /* Layer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7801CB40DC500C806FE /* Layer.swift */; };
965E80D91DD4C50600D61E4B /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7811CB40DC500C806FE /* Layout.swift */; };
965E80DA1DD4C50600D61E4B /* Border.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB76F1CB40DC500C806FE /* Border.swift */; };
965E80DB1DD4C50600D61E4B /* InterimSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7871CB40DC500C806FE /* InterimSpace.swift */; };
965E80DC1DD4C50600D61E4B /* Depth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7781CB40DC500C806FE /* Depth.swift */; };
965E80DD1DD4C50600D61E4B /* EdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB77A1CB40DC500C806FE /* EdgeInsets.swift */; };
965E80DF1DD4C50600D61E4B /* CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7851CB40DC500C806FE /* CornerRadius.swift */; };
965E80E01DD4C50600D61E4B /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7861CB40DC500C806FE /* Shape.swift */; };
965E80E11DD4C50600D61E4B /* Offset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 968C99461D377849000074FF /* Offset.swift */; };
965E80E21DD4C50600D61E4B /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB78C1CB40DC500C806FE /* View.swift */; };
965E80E41DD4C53300D61E4B /* PulseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7841CB40DC500C806FE /* PulseView.swift */; };
965E80E51DD4C53300D61E4B /* PulseAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7821CB40DC500C806FE /* PulseAnimation.swift */; };
965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E3C3931D397AE90086A024 /* Material+UIView.swift */; };
965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F1DC871D654FDF0025F925 /* Material+CALayer.swift */; };
965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7641CB40DC500C806FE /* Material+String.swift */; };
965E80EA1DD4C55200D61E4B /* Material+UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7651CB40DC500C806FE /* Material+UIFont.swift */; };
965E80EB1DD4C55200D61E4B /* Material+UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB76C1CB40DC500C806FE /* Material+UIImage.swift */; };
965E80EC1DD4C55200D61E4B /* Material+Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C1C8801D42C62800E6608F /* Material+Array.swift */; };
965E80ED1DD4C55200D61E4B /* Material+UIWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 962864591D53FE3E00690B69 /* Material+UIWindow.swift */; };
965E80F71DD4D59500D61E4B /* Card.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB75D1CB40DC500C806FE /* Card.swift */; };
965E80F81DD4D59500D61E4B /* ImageCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7621CB40DC500C806FE /* ImageCard.swift */; };
965E80F91DD4D59500D61E4B /* PresenterCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9631A7C01D95E3AC00CFB109 /* PresenterCard.swift */; };
965E80FB1DD4D59500D61E4B /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7951CB40DC500C806FE /* SearchBar.swift */; };
965E80FC1DD4D59500D61E4B /* SearchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7961CB40DC500C806FE /* SearchBarController.swift */; };
965E80FD1DD4D59500D61E4B /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79F1CB40DC500C806FE /* Toolbar.swift */; };
965E80FE1DD4D59500D61E4B /* ToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7A01CB40DC500C806FE /* ToolbarController.swift */; };
965E80FF1DD4D5C800D61E4B /* BottomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7581CB40DC500C806FE /* BottomNavigationController.swift */; };
965E81031DD4D5C800D61E4B /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7711CB40DC500C806FE /* CollectionView.swift */; };
965E81041DD4D5C800D61E4B /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7721CB40DC500C806FE /* CollectionViewCell.swift */; };
965E81071DD4D5C800D61E4B /* CollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7751CB40DC500C806FE /* CollectionViewLayout.swift */; };
965E81081DD4D5C800D61E4B /* CollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966ECF291CF4C20100BB0BDF /* CollectionReusableView.swift */; };
965E81091DD4D5C800D61E4B /* DataSourceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7771CB40DC500C806FE /* DataSourceItem.swift */; };
965E810A1DD4D5C800D61E4B /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB77B1CB40DC500C806FE /* Font.swift */; };
965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7941CB40DC500C806FE /* RobotoFont.swift */; };
965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9628645E1D540AF300690B69 /* DynamicFontType.swift */; };
965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7901CB40DC500C806FE /* NavigationBar.swift */; };
965E81111DD4D5C800D61E4B /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7911CB40DC500C806FE /* NavigationController.swift */; };
965E81121DD4D5C800D61E4B /* NavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7921CB40DC500C806FE /* NavigationItem.swift */; };
965E81131DD4D5C800D61E4B /* NavigationDrawerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7971CB40DC500C806FE /* NavigationDrawerController.swift */; };
965E81161DD4D5C800D61E4B /* DisplayStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9626CA961DAB53A8003E2611 /* DisplayStyle.swift */; };
965E81171DD4D5C800D61E4B /* TransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7991CB40DC500C806FE /* TransitionController.swift */; };
965E81181DD4D5C800D61E4B /* Snackbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963FBEFC1D669510008F8512 /* Snackbar.swift */; };
965E81191DD4D5C800D61E4B /* SnackbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961EFC571D738FF600E84652 /* SnackbarController.swift */; };
965E811A1DD4D5C800D61E4B /* StatusBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967A48181D0F425A00B8CEB7 /* StatusBarController.swift */; };
965E811B1DD4D5C800D61E4B /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7881CB40DC500C806FE /* Switch.swift */; };
965E811C1DD4D5C800D61E4B /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79A1CB40DC500C806FE /* TabBar.swift */; };
965E811D1DD4D5C800D61E4B /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7891CB40DC500C806FE /* TableViewCell.swift */; };
965E811E1DD4D5C800D61E4B /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79C1CB40DC500C806FE /* TextField.swift */; };
965E811F1DD4D5C800D61E4B /* ErrorTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961F18E71CD93E3E008927C5 /* ErrorTextField.swift */; };
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79D1CB40DC500C806FE /* TextStorage.swift */; };
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79E1CB40DC500C806FE /* TextView.swift */; };
965E81261DD4D7C800D61E4B /* Material+NSMutableAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961276621DCD8B1800A7D920 /* Material+NSMutableAttributedString.swift */; };
966C17731F0439F600D3E83C /* Material+MotionAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C17721F0439F600D3E83C /* Material+MotionAnimation.swift */; };
9685D5AF1F0F04CB00AFEB79 /* CardCollectionViewCell.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9656895E1F002F16001C656D /* CardCollectionViewCell.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9685D5B01F0F04CB00AFEB79 /* CardCollectionViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 965689601F002F4C001C656D /* CardCollectionViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9685D5B11F0F04CB00AFEB79 /* Material+MotionAnimation.swift in Headers */ = {isa = PBXBuildFile; fileRef = 966C17721F0439F600D3E83C /* Material+MotionAnimation.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7BF1D8F2572004741EC /* Divider.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96230AB71D6A520C00AF47DC /* Divider.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C01D8F2572004741EC /* Material+CALayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96F1DC871D654FDF0025F925 /* Material+CALayer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C11D8F2572004741EC /* Material+Array.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96C1C8801D42C62800E6608F /* Material+Array.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C21D8F2572004741EC /* Material+UIWindow.swift in Headers */ = {isa = PBXBuildFile; fileRef = 962864591D53FE3E00690B69 /* Material+UIWindow.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C31D8F2572004741EC /* DynamicFontType.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9628645E1D540AF300690B69 /* DynamicFontType.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7CB1D8F2573004741EC /* Snackbar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963FBEFC1D669510008F8512 /* Snackbar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7CC1D8F2573004741EC /* SnackbarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961EFC571D738FF600E84652 /* SnackbarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96B8D22C20CF82D2008BD149 /* FABMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96A183641E0C6DD400083C30 /* FABMenuController.swift */; };
96B8D22D20CF82D5008BD149 /* FABMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96A183621E0C6CE200083C30 /* FABMenu.swift */; };
96BCB7F31CB40DE900C806FE /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 96BCB7EE1CB40DE900C806FE /* Roboto-Bold.ttf */; };
96BCB7F51CB40DE900C806FE /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 96BCB7EF1CB40DE900C806FE /* Roboto-Light.ttf */; };
96BCB7F71CB40DE900C806FE /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 96BCB7F01CB40DE900C806FE /* Roboto-Medium.ttf */; };
96BCB7F91CB40DE900C806FE /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 96BCB7F11CB40DE900C806FE /* Roboto-Regular.ttf */; };
96BCB7FB1CB40DE900C806FE /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 96BCB7F21CB40DE900C806FE /* Roboto-Thin.ttf */; };
96BCB8141CB4115200C806FE /* PulseAnimation.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7821CB40DC500C806FE /* PulseAnimation.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8151CB4115200C806FE /* FABButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB75F1CB40DC500C806FE /* FABButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8161CB4115200C806FE /* FlatButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7601CB40DC500C806FE /* FlatButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8171CB4115200C806FE /* Button.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7701CB40DC500C806FE /* Button.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8181CB4115200C806FE /* RaisedButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7931CB40DC500C806FE /* RaisedButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB81E1CB4115200C806FE /* DataSourceItem.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7771CB40DC500C806FE /* DataSourceItem.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB81F1CB4115200C806FE /* CollectionView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7711CB40DC500C806FE /* CollectionView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8201CB4115200C806FE /* CollectionViewCell.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7721CB40DC500C806FE /* CollectionViewCell.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8231CB4115200C806FE /* CollectionViewLayout.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7751CB40DC500C806FE /* CollectionViewLayout.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8241CB4115200C806FE /* TableViewCell.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7891CB40DC500C806FE /* TableViewCell.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8251CB4115200C806FE /* Color.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7761CB40DC500C806FE /* Color.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8261CB4115200C806FE /* Device.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7791CB40DC500C806FE /* Device.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8281CB4115200C806FE /* Material+String.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7641CB40DC500C806FE /* Material+String.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8291CB4115200C806FE /* Material+UIFont.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7651CB40DC500C806FE /* Material+UIFont.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8301CB4115200C806FE /* Material+UIImage.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB76C1CB40DC500C806FE /* Material+UIImage.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8311CB4115200C806FE /* Font.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB77B1CB40DC500C806FE /* Font.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8321CB4115200C806FE /* RobotoFont.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7941CB40DC500C806FE /* RobotoFont.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8331CB4115200C806FE /* Icon.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB77D1CB40DC500C806FE /* Icon.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8341CB4115200C806FE /* Layer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7801CB40DC500C806FE /* Layer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8361CB4115200C806FE /* Grid.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7611CB40DC500C806FE /* Grid.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8371CB4115200C806FE /* Layout.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7811CB40DC500C806FE /* Layout.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB83B1CB4115200C806FE /* NavigationDrawerController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7971CB40DC500C806FE /* NavigationDrawerController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB83C1CB4115200C806FE /* Bar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7981CB40DC500C806FE /* Bar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB83D1CB4115200C806FE /* TransitionController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7991CB40DC500C806FE /* TransitionController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8441CB4115200C806FE /* BottomNavigationController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7581CB40DC500C806FE /* BottomNavigationController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8461CB4115200C806FE /* NavigationBar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7901CB40DC500C806FE /* NavigationBar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8471CB4115200C806FE /* NavigationController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7911CB40DC500C806FE /* NavigationController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8481CB4115200C806FE /* NavigationItem.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7921CB40DC500C806FE /* NavigationItem.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB84A1CB4115200C806FE /* TextField.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB79C1CB40DC500C806FE /* TextField.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB84B1CB4115200C806FE /* TextStorage.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB79D1CB40DC500C806FE /* TextStorage.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB84C1CB4115200C806FE /* TextView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB79E1CB40DC500C806FE /* TextView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB84D1CB4115200C806FE /* Border.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB76F1CB40DC500C806FE /* Border.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB84E1CB4115200C806FE /* InterimSpace.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7871CB40DC500C806FE /* InterimSpace.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB84F1CB4115200C806FE /* Depth.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7781CB40DC500C806FE /* Depth.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8501CB4115200C806FE /* EdgeInsets.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB77A1CB40DC500C806FE /* EdgeInsets.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8521CB4115200C806FE /* CornerRadius.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7851CB40DC500C806FE /* CornerRadius.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8531CB4115200C806FE /* Shape.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7861CB40DC500C806FE /* Shape.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8551CB4115200C806FE /* PulseView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7841CB40DC500C806FE /* PulseView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8561CB4115200C806FE /* Switch.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7881CB40DC500C806FE /* Switch.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BCB8571CB4115200C806FE /* View.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB78C1CB40DC500C806FE /* View.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96BFC1541E5E486F0075DE1F /* SpringAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965532281E47E388005C2792 /* SpringAnimation.swift */; };
96BFC16F1E63C10A0075DE1F /* SpringAnimation.swift in Headers */ = {isa = PBXBuildFile; fileRef = 965532281E47E388005C2792 /* SpringAnimation.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96D88C321C1328D800B91418 /* Material.h in Headers */ = {isa = PBXBuildFile; fileRef = 96D88C091C1328D800B91418 /* Material.h */; settings = {ATTRIBUTES = (Public, ); }; };
96E09DC81F2287E50000B121 /* TabsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E09DC71F2287E50000B121 /* TabsController.swift */; };
96E3C3951D3A1CC20086A024 /* IconButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9658F2161CD6FA4700B902C1 /* IconButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E3C3961D3A1CC20086A024 /* CollectionReusableView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 966ECF291CF4C20100BB0BDF /* CollectionReusableView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E3C3971D3A1CC20086A024 /* Material+UIView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E3C3931D397AE90086A024 /* Material+UIView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E3C3991D3A1CC20086A024 /* StatusBarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 967A48181D0F425A00B8CEB7 /* StatusBarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E3C39A1D3A1CC20086A024 /* ErrorTextField.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961F18E71CD93E3E008927C5 /* ErrorTextField.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E3C39C1D3A1CC20086A024 /* Offset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 968C99461D377849000074FF /* Offset.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96F1A5531F24F17A001D8CAF /* TabsController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E09DC71F2287E50000B121 /* TabsController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9D00EBB4216675FB00DBCD69 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D00EBB3216675FB00DBCD69 /* Theme.swift */; };
9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6320D175AC00D0528D /* Material+UIButton.swift */; };
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; };
9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; };
9D494A38217F6B63003D66F1 /* LayoutAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */; };
9D494A3A217F6B70003D66F1 /* LayoutAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */; };
9D494A3C217F6B7D003D66F1 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */; };
9D9089B92118914500605DC9 /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9089B82118914500605DC9 /* Editor.swift */; };
9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DDF2170D7AF000C04DF /* Dialog.swift */; };
9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE12170D7C0000C04DF /* DialogController.swift */; };
9DE25DE42170D7FF000C04DF /* DialogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE32170D7FF000C04DF /* DialogView.swift */; };
9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */; };
9DE84D731FF0252600586C8B /* BaseButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */; };
9DE84D741FF0252600586C8B /* CheckButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */; };
9DF352421FED20C900B2A11B /* BaseIconLayerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF352411FED20C900B2A11B /* BaseIconLayerButton.swift */; };
9DF352441FED20ED00B2A11B /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF352431FED20ED00B2A11B /* RadioButton.swift */; };
9DF352461FED210000B2A11B /* CheckButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF352451FED210000B2A11B /* CheckButton.swift */; };
9DF58CEE20C098C60098968D /* ErrorTextFieldValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF58CED20C098C60098968D /* ErrorTextFieldValidator.swift */; };
9DF74C4E20D15D84003C1D66 /* Material+UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF74C4D20D15D84003C1D66 /* Material+UIColor.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
968BA8351F8D201B0091852E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 968BA8311F8D201B0091852E /* Motion.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 96C98DD11E424AB000B22906;
remoteInfo = "Motion iOS";
};
968BA8371F8D20530091852E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 968BA8311F8D201B0091852E /* Motion.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 96C98DD01E424AB000B22906;
remoteInfo = "Motion iOS";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
961154CB1F32A7B100A78D74 /* ChipBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChipBar.swift; sourceTree = ""; };
961276621DCD8B1800A7D920 /* Material+NSMutableAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+NSMutableAttributedString.swift"; sourceTree = ""; };
961527B81F3A509900E8B2AC /* ChipBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChipBarController.swift; sourceTree = ""; };
9618006C1F4D384200CD77A1 /* Material+UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIViewController.swift"; sourceTree = ""; };
961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; };
961E6BE11DDA2AF3004E6C93 /* Screen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; };
961EFC571D738FF600E84652 /* SnackbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnackbarController.swift; sourceTree = ""; };
961F18E71CD93E3E008927C5 /* ErrorTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTextField.swift; sourceTree = ""; };
96230AB71D6A520C00AF47DC /* Divider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Divider.swift; sourceTree = ""; };
9626CA961DAB53A8003E2611 /* DisplayStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayStyle.swift; sourceTree = ""; };
9626CB9A1DAD3D1D003E2611 /* HeightPreset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeightPreset.swift; sourceTree = ""; };
962864591D53FE3E00690B69 /* Material+UIWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIWindow.swift"; sourceTree = ""; };
9628645E1D540AF300690B69 /* DynamicFontType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicFontType.swift; sourceTree = ""; };
9631A7C01D95E3AC00CFB109 /* PresenterCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenterCard.swift; sourceTree = ""; };
96328B791E020A41009A4C90 /* CollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewController.swift; sourceTree = ""; };
96328B961E05C0BB009A4C90 /* TableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = ""; };
96328B981E05C0CE009A4C90 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; };
96334EF51C8B84660083986B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
963832361B88DFD80015F710 /* Material.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Material.framework; sourceTree = BUILT_PRODUCTS_DIR; };
963FBEFC1D669510008F8512 /* Snackbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Snackbar.swift; sourceTree = ""; };
965532281E47E388005C2792 /* SpringAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringAnimation.swift; sourceTree = ""; };
9656895E1F002F16001C656D /* CardCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardCollectionViewCell.swift; sourceTree = ""; };
965689601F002F4C001C656D /* CardCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardCollectionViewController.swift; sourceTree = ""; };
9658F2161CD6FA4700B902C1 /* IconButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IconButton.swift; sourceTree = ""; };
966C17721F0439F600D3E83C /* Material+MotionAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+MotionAnimation.swift"; sourceTree = ""; };
966ECF291CF4C20100BB0BDF /* CollectionReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionReusableView.swift; sourceTree = ""; };
967A48181D0F425A00B8CEB7 /* StatusBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarController.swift; sourceTree = ""; };
968BA8311F8D201B0091852E /* Motion.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = Motion.xcodeproj; sourceTree = ""; };
968C99461D377849000074FF /* Offset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Offset.swift; sourceTree = ""; };
96A183621E0C6CE200083C30 /* FABMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABMenu.swift; sourceTree = ""; };
96A183641E0C6DD400083C30 /* FABMenuController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABMenuController.swift; sourceTree = ""; };
96BCB7581CB40DC500C806FE /* BottomNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BottomNavigationController.swift; sourceTree = ""; };
96BCB75D1CB40DC500C806FE /* Card.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Card.swift; sourceTree = ""; };
96BCB75F1CB40DC500C806FE /* FABButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABButton.swift; sourceTree = ""; };
96BCB7601CB40DC500C806FE /* FlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatButton.swift; sourceTree = ""; };
96BCB7611CB40DC500C806FE /* Grid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Grid.swift; sourceTree = ""; };
96BCB7621CB40DC500C806FE /* ImageCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCard.swift; sourceTree = ""; };
96BCB7641CB40DC500C806FE /* Material+String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+String.swift"; sourceTree = ""; };
96BCB7651CB40DC500C806FE /* Material+UIFont.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIFont.swift"; sourceTree = ""; };
96BCB76C1CB40DC500C806FE /* Material+UIImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIImage.swift"; sourceTree = ""; };
96BCB76F1CB40DC500C806FE /* Border.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Border.swift; sourceTree = ""; };
96BCB7701CB40DC500C806FE /* Button.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; };
96BCB7711CB40DC500C806FE /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = ""; };
96BCB7721CB40DC500C806FE /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; };
96BCB7751CB40DC500C806FE /* CollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewLayout.swift; sourceTree = ""; };
96BCB7761CB40DC500C806FE /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; };
96BCB7771CB40DC500C806FE /* DataSourceItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSourceItem.swift; sourceTree = ""; };
96BCB7781CB40DC500C806FE /* Depth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Depth.swift; sourceTree = ""; };
96BCB7791CB40DC500C806FE /* Device.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = ""; };
96BCB77A1CB40DC500C806FE /* EdgeInsets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EdgeInsets.swift; sourceTree = ""; };
96BCB77B1CB40DC500C806FE /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; };
96BCB77D1CB40DC500C806FE /* Icon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = ""; };
96BCB7801CB40DC500C806FE /* Layer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layer.swift; sourceTree = ""; };
96BCB7811CB40DC500C806FE /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; };
96BCB7821CB40DC500C806FE /* PulseAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PulseAnimation.swift; sourceTree = ""; };
96BCB7841CB40DC500C806FE /* PulseView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PulseView.swift; sourceTree = ""; };
96BCB7851CB40DC500C806FE /* CornerRadius.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CornerRadius.swift; sourceTree = ""; };
96BCB7861CB40DC500C806FE /* Shape.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shape.swift; sourceTree = ""; };
96BCB7871CB40DC500C806FE /* InterimSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterimSpace.swift; sourceTree = ""; };
96BCB7881CB40DC500C806FE /* Switch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = ""; };
96BCB7891CB40DC500C806FE /* TableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; };
96BCB78C1CB40DC500C806FE /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; };
96BCB7901CB40DC500C806FE /* NavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = ""; };
96BCB7911CB40DC500C806FE /* NavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = ""; };
96BCB7921CB40DC500C806FE /* NavigationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationItem.swift; sourceTree = ""; };
96BCB7931CB40DC500C806FE /* RaisedButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RaisedButton.swift; sourceTree = ""; };
96BCB7941CB40DC500C806FE /* RobotoFont.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RobotoFont.swift; sourceTree = ""; };
96BCB7951CB40DC500C806FE /* SearchBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; };
96BCB7961CB40DC500C806FE /* SearchBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchBarController.swift; sourceTree = ""; };
96BCB7971CB40DC500C806FE /* NavigationDrawerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationDrawerController.swift; sourceTree = ""; };
96BCB7981CB40DC500C806FE /* Bar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bar.swift; sourceTree = ""; };
96BCB7991CB40DC500C806FE /* TransitionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionController.swift; sourceTree = ""; };
96BCB79A1CB40DC500C806FE /* TabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = ""; tabWidth = 2; };
96BCB79C1CB40DC500C806FE /* TextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; };
96BCB79D1CB40DC500C806FE /* TextStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextStorage.swift; sourceTree = ""; };
96BCB79E1CB40DC500C806FE /* TextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; };
96BCB79F1CB40DC500C806FE /* Toolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = ""; };
96BCB7A01CB40DC500C806FE /* ToolbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolbarController.swift; sourceTree = ""; };
96BCB7EE1CB40DE900C806FE /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; };
96BCB7EF1CB40DE900C806FE /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = ""; };
96BCB7F01CB40DE900C806FE /* Roboto-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Medium.ttf"; sourceTree = ""; };
96BCB7F11CB40DE900C806FE /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; };
96BCB7F21CB40DE900C806FE /* Roboto-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Thin.ttf"; sourceTree = ""; };
96C1C8801D42C62800E6608F /* Material+Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+Array.swift"; sourceTree = ""; };
96D88BFC1C1328D800B91418 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
96D88BFD1C1328D800B91418 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; };
96D88C091C1328D800B91418 /* Material.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Material.h; sourceTree = ""; };
96E09DC71F2287E50000B121 /* TabsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsController.swift; sourceTree = ""; };
96E3C3931D397AE90086A024 /* Material+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIView.swift"; sourceTree = ""; };
96F1DC871D654FDF0025F925 /* Material+CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+CALayer.swift"; sourceTree = ""; };
9D00EBB3216675FB00DBCD69 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; };
9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = ""; };
9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = ""; };
9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAttribute.swift; sourceTree = ""; };
9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAnchor.swift; sourceTree = ""; };
9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutConstraint.swift; sourceTree = ""; };
9D9089B82118914500605DC9 /* Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = ""; };
9DE25DDF2170D7AF000C04DF /* Dialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dialog.swift; sourceTree = ""; };
9DE25DE12170D7C0000C04DF /* DialogController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogController.swift; sourceTree = ""; };
9DE25DE32170D7FF000C04DF /* DialogView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialogView.swift; sourceTree = ""; };
9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; };
9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseButtonGroup.swift; sourceTree = ""; };
9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckButtonGroup.swift; sourceTree = ""; };
9DF352411FED20C900B2A11B /* BaseIconLayerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseIconLayerButton.swift; sourceTree = ""; };
9DF352431FED20ED00B2A11B /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; };
9DF352451FED210000B2A11B /* CheckButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckButton.swift; sourceTree = ""; };
9DF58CED20C098C60098968D /* ErrorTextFieldValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorTextFieldValidator.swift; sourceTree = ""; };
9DF74C4D20D15D84003C1D66 /* Material+UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Material+UIColor.swift"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
9602F00C1DA1163000F3FB79 /* Grid */ = {
isa = PBXGroup;
children = (
96BCB7611CB40DC500C806FE /* Grid.swift */,
);
path = Grid;
sourceTree = "";
};
96090B031D9D709E00709CA6 /* Text */ = {
isa = PBXGroup;
children = (
9D9089B82118914500605DC9 /* Editor.swift */,
961F18E71CD93E3E008927C5 /* ErrorTextField.swift */,
9DF58CED20C098C60098968D /* ErrorTextFieldValidator.swift */,
96BCB79C1CB40DC500C806FE /* TextField.swift */,
96BCB79D1CB40DC500C806FE /* TextStorage.swift */,
96BCB79E1CB40DC500C806FE /* TextView.swift */,
);
path = Text;
sourceTree = "";
};
961154CA1F32999000A78D74 /* Chip */ = {
isa = PBXGroup;
children = (
961154CB1F32A7B100A78D74 /* ChipBar.swift */,
961527B81F3A509900E8B2AC /* ChipBarController.swift */,
);
path = Chip;
sourceTree = "";
};
961527531F393C0E00E8B2AC /* Motion */ = {
isa = PBXGroup;
children = (
968BA8311F8D201B0091852E /* Motion.xcodeproj */,
);
path = Motion;
sourceTree = "";
};
961E6BDD1DDA2A7E004E6C93 /* Application */ = {
isa = PBXGroup;
children = (
961E6BDE1DDA2A95004E6C93 /* Application.swift */,
);
path = Application;
sourceTree = "";
};
961E6BE01DDA2ADD004E6C93 /* Screen */ = {
isa = PBXGroup;
children = (
961E6BE11DDA2AF3004E6C93 /* Screen.swift */,
);
path = Screen;
sourceTree = "";
};
961E6BEF1DDA4B04004E6C93 /* NavigationDrawer */ = {
isa = PBXGroup;
children = (
96BCB7971CB40DC500C806FE /* NavigationDrawerController.swift */,
);
path = NavigationDrawer;
sourceTree = "";
};
96230AB61D6A51FD00AF47DC /* Divider */ = {
isa = PBXGroup;
children = (
96230AB71D6A520C00AF47DC /* Divider.swift */,
);
path = Divider;
sourceTree = "";
};
96264BE41D833C8400576F37 /* Bar */ = {
isa = PBXGroup;
children = (
96BCB7981CB40DC500C806FE /* Bar.swift */,
);
path = Bar;
sourceTree = "";
};
9626CA951DAB5370003E2611 /* Transition */ = {
isa = PBXGroup;
children = (
9626CA961DAB53A8003E2611 /* DisplayStyle.swift */,
96BCB7991CB40DC500C806FE /* TransitionController.swift */,
);
path = Transition;
sourceTree = "";
};
9626CBCC1DADA5F1003E2611 /* Height */ = {
isa = PBXGroup;
children = (
9626CB9A1DAD3D1D003E2611 /* HeightPreset.swift */,
);
path = Height;
sourceTree = "";
};
962DDD081D6FBBD0001C307C /* BottomTabBar */ = {
isa = PBXGroup;
children = (
96BCB7581CB40DC500C806FE /* BottomNavigationController.swift */,
);
path = BottomTabBar;
sourceTree = "";
};
9630ACB71F29A26B00B4217D /* Frameworks */ = {
isa = PBXGroup;
children = (
961527531F393C0E00E8B2AC /* Motion */,
);
path = Frameworks;
sourceTree = "";
};
96328B9A1E05C135009A4C90 /* Data */ = {
isa = PBXGroup;
children = (
96BCB7771CB40DC500C806FE /* DataSourceItem.swift */,
);
path = Data;
sourceTree = "";
};
9638322C1B88DFD80015F710 = {
isa = PBXGroup;
children = (
96D88BF41C1328D800B91418 /* Sources */,
963832371B88DFD80015F710 /* Products */,
);
indentWidth = 2;
sourceTree = "";
tabWidth = 2;
usesTabs = 0;
};
963832371B88DFD80015F710 /* Products */ = {
isa = PBXGroup;
children = (
963832361B88DFD80015F710 /* Material.framework */,
);
name = Products;
sourceTree = "";
};
963FBEFB1D6694E8008F8512 /* Snackbar */ = {
isa = PBXGroup;
children = (
963FBEFC1D669510008F8512 /* Snackbar.swift */,
961EFC571D738FF600E84652 /* SnackbarController.swift */,
);
path = Snackbar;
sourceTree = "";
};
963FBF001D66964F008F8512 /* Toolbar */ = {
isa = PBXGroup;
children = (
96BCB79F1CB40DC500C806FE /* Toolbar.swift */,
96BCB7A01CB40DC500C806FE /* ToolbarController.swift */,
);
path = Toolbar;
sourceTree = "";
};
963FBF011D6696AB008F8512 /* Tab */ = {
isa = PBXGroup;
children = (
96BCB79A1CB40DC500C806FE /* TabBar.swift */,
96E09DC71F2287E50000B121 /* TabsController.swift */,
);
path = Tab;
sourceTree = "";
};
963FBF021D6696D0008F8512 /* FABMenu */ = {
isa = PBXGroup;
children = (
96A183621E0C6CE200083C30 /* FABMenu.swift */,
96A183641E0C6DD400083C30 /* FABMenuController.swift */,
);
path = FABMenu;
sourceTree = "";
};
963FBF031D6696EF008F8512 /* SearchBar */ = {
isa = PBXGroup;
children = (
96BCB7951CB40DC500C806FE /* SearchBar.swift */,
96BCB7961CB40DC500C806FE /* SearchBarController.swift */,
);
path = SearchBar;
sourceTree = "";
};
965689641F003476001C656D /* CardCollectionView */ = {
isa = PBXGroup;
children = (
9656895E1F002F16001C656D /* CardCollectionViewCell.swift */,
965689601F002F4C001C656D /* CardCollectionViewController.swift */,
);
path = CardCollectionView;
sourceTree = "";
};
966ECF2B1CF4C21B00BB0BDF /* Table */ = {
isa = PBXGroup;
children = (
96328B961E05C0BB009A4C90 /* TableView.swift */,
96BCB7891CB40DC500C806FE /* TableViewCell.swift */,
96328B981E05C0CE009A4C90 /* TableViewController.swift */,
);
path = Table;
sourceTree = "";
};
967A48171D0F424B00B8CEB7 /* StatusBar */ = {
isa = PBXGroup;
children = (
967A48181D0F425A00B8CEB7 /* StatusBarController.swift */,
);
path = StatusBar;
sourceTree = "";
};
968BA8321F8D201B0091852E /* Products */ = {
isa = PBXGroup;
children = (
968BA8361F8D201B0091852E /* Motion.framework */,
);
name = Products;
sourceTree = "";
};
968C99421D36EC9E000074FF /* Switch */ = {
isa = PBXGroup;
children = (
96BCB7881CB40DC500C806FE /* Switch.swift */,
);
path = Switch;
sourceTree = "";
};
96BCB7571CB40DC500C806FE /* iOS */ = {
isa = PBXGroup;
children = (
96EF418E1E835E850012CA1C /* Animation */,
961E6BDD1DDA2A7E004E6C93 /* Application */,
96264BE41D833C8400576F37 /* Bar */,
962DDD081D6FBBD0001C307C /* BottomTabBar */,
96BCB8031CB40F4B00C806FE /* Button */,
9DE84D6E1FF0250E00586C8B /* ButtonGroup */,
96BCB8021CB40F3B00C806FE /* Card */,
961154CA1F32999000A78D74 /* Chip */,
96BCB8051CB40F9C00C806FE /* Collection */,
96BCB8001CB40F0300C806FE /* Color */,
96328B9A1E05C135009A4C90 /* Data */,
96BCB80B1CB410CC00C806FE /* Device */,
9DE25DDE2170D779000C04DF /* Dialogs */,
96230AB61D6A51FD00AF47DC /* Divider */,
96BCB80A1CB410A100C806FE /* Extension */,
963FBF021D6696D0008F8512 /* FABMenu */,
96BCB8071CB4101C00C806FE /* Font */,
9602F00C1DA1163000F3FB79 /* Grid */,
9626CBCC1DADA5F1003E2611 /* Height */,
96BCB8081CB4105E00C806FE /* Icon */,
96BCB80D1CB410FD00C806FE /* Layer */,
96BCB8041CB40F6C00C806FE /* Layout */,
96BCB8011CB40F1700C806FE /* Navigation */,
961E6BEF1DDA4B04004E6C93 /* NavigationDrawer */,
961E6BE01DDA2ADD004E6C93 /* Screen */,
963FBF031D6696EF008F8512 /* SearchBar */,
963FBEFB1D6694E8008F8512 /* Snackbar */,
967A48171D0F424B00B8CEB7 /* StatusBar */,
968C99421D36EC9E000074FF /* Switch */,
963FBF011D6696AB008F8512 /* Tab */,
966ECF2B1CF4C21B00BB0BDF /* Table */,
96090B031D9D709E00709CA6 /* Text */,
9D00EBB2216675A800DBCD69 /* Theme */,
963FBF001D66964F008F8512 /* Toolbar */,
9626CA951DAB5370003E2611 /* Transition */,
96BCB8061CB40FD000C806FE /* Type */,
96BCB80C1CB410DD00C806FE /* View */,
);
path = iOS;
sourceTree = "";
};
96BCB7EC1CB40DE900C806FE /* Font */ = {
isa = PBXGroup;
children = (
96BCB7ED1CB40DE900C806FE /* Roboto */,
);
path = Font;
sourceTree = "";
};
96BCB7ED1CB40DE900C806FE /* Roboto */ = {
isa = PBXGroup;
children = (
96BCB7EE1CB40DE900C806FE /* Roboto-Bold.ttf */,
96BCB7EF1CB40DE900C806FE /* Roboto-Light.ttf */,
96BCB7F01CB40DE900C806FE /* Roboto-Medium.ttf */,
96BCB7F11CB40DE900C806FE /* Roboto-Regular.ttf */,
96BCB7F21CB40DE900C806FE /* Roboto-Thin.ttf */,
);
path = Roboto;
sourceTree = "";
};
96BCB8001CB40F0300C806FE /* Color */ = {
isa = PBXGroup;
children = (
96BCB7761CB40DC500C806FE /* Color.swift */,
);
path = Color;
sourceTree = "";
};
96BCB8011CB40F1700C806FE /* Navigation */ = {
isa = PBXGroup;
children = (
96BCB7901CB40DC500C806FE /* NavigationBar.swift */,
96BCB7911CB40DC500C806FE /* NavigationController.swift */,
96BCB7921CB40DC500C806FE /* NavigationItem.swift */,
);
path = Navigation;
sourceTree = "";
};
96BCB8021CB40F3B00C806FE /* Card */ = {
isa = PBXGroup;
children = (
96BCB75D1CB40DC500C806FE /* Card.swift */,
96BCB7621CB40DC500C806FE /* ImageCard.swift */,
9631A7C01D95E3AC00CFB109 /* PresenterCard.swift */,
);
path = Card;
sourceTree = "";
};
96BCB8031CB40F4B00C806FE /* Button */ = {
isa = PBXGroup;
children = (
9DF352411FED20C900B2A11B /* BaseIconLayerButton.swift */,
96BCB7701CB40DC500C806FE /* Button.swift */,
9DF352451FED210000B2A11B /* CheckButton.swift */,
96BCB75F1CB40DC500C806FE /* FABButton.swift */,
96BCB7601CB40DC500C806FE /* FlatButton.swift */,
9658F2161CD6FA4700B902C1 /* IconButton.swift */,
9DF352431FED20ED00B2A11B /* RadioButton.swift */,
96BCB7931CB40DC500C806FE /* RaisedButton.swift */,
);
path = Button;
sourceTree = "";
};
96BCB8041CB40F6C00C806FE /* Layout */ = {
isa = PBXGroup;
children = (
96BCB7811CB40DC500C806FE /* Layout.swift */,
9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */,
9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */,
9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */,
);
path = Layout;
sourceTree = "";
};
96BCB8051CB40F9C00C806FE /* Collection */ = {
isa = PBXGroup;
children = (
965689641F003476001C656D /* CardCollectionView */,
966ECF291CF4C20100BB0BDF /* CollectionReusableView.swift */,
96BCB7711CB40DC500C806FE /* CollectionView.swift */,
96BCB7721CB40DC500C806FE /* CollectionViewCell.swift */,
96328B791E020A41009A4C90 /* CollectionViewController.swift */,
96BCB7751CB40DC500C806FE /* CollectionViewLayout.swift */,
);
path = Collection;
sourceTree = "";
};
96BCB8061CB40FD000C806FE /* Type */ = {
isa = PBXGroup;
children = (
96BCB76F1CB40DC500C806FE /* Border.swift */,
96BCB7851CB40DC500C806FE /* CornerRadius.swift */,
96BCB7781CB40DC500C806FE /* Depth.swift */,
96BCB77A1CB40DC500C806FE /* EdgeInsets.swift */,
96BCB7871CB40DC500C806FE /* InterimSpace.swift */,
968C99461D377849000074FF /* Offset.swift */,
96BCB7861CB40DC500C806FE /* Shape.swift */,
);
path = Type;
sourceTree = "";
};
96BCB8071CB4101C00C806FE /* Font */ = {
isa = PBXGroup;
children = (
9628645E1D540AF300690B69 /* DynamicFontType.swift */,
96BCB77B1CB40DC500C806FE /* Font.swift */,
96BCB7941CB40DC500C806FE /* RobotoFont.swift */,
);
path = Font;
sourceTree = "";
};
96BCB8081CB4105E00C806FE /* Icon */ = {
isa = PBXGroup;
children = (
96BCB77D1CB40DC500C806FE /* Icon.swift */,
);
path = Icon;
sourceTree = "";
};
96BCB80A1CB410A100C806FE /* Extension */ = {
isa = PBXGroup;
children = (
96C1C8801D42C62800E6608F /* Material+Array.swift */,
96F1DC871D654FDF0025F925 /* Material+CALayer.swift */,
966C17721F0439F600D3E83C /* Material+MotionAnimation.swift */,
961276621DCD8B1800A7D920 /* Material+NSMutableAttributedString.swift */,
96BCB7641CB40DC500C806FE /* Material+String.swift */,
9D054A6320D175AC00D0528D /* Material+UIButton.swift */,
9DF74C4D20D15D84003C1D66 /* Material+UIColor.swift */,
96BCB7651CB40DC500C806FE /* Material+UIFont.swift */,
96BCB76C1CB40DC500C806FE /* Material+UIImage.swift */,
9D054A6420D175AC00D0528D /* Material+UILabel.swift */,
96E3C3931D397AE90086A024 /* Material+UIView.swift */,
9618006C1F4D384200CD77A1 /* Material+UIViewController.swift */,
962864591D53FE3E00690B69 /* Material+UIWindow.swift */,
);
path = Extension;
sourceTree = "";
};
96BCB80B1CB410CC00C806FE /* Device */ = {
isa = PBXGroup;
children = (
96BCB7791CB40DC500C806FE /* Device.swift */,
);
path = Device;
sourceTree = "";
};
96BCB80C1CB410DD00C806FE /* View */ = {
isa = PBXGroup;
children = (
96BCB7841CB40DC500C806FE /* PulseView.swift */,
96BCB78C1CB40DC500C806FE /* View.swift */,
9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */,
);
path = View;
sourceTree = "";
};
96BCB80D1CB410FD00C806FE /* Layer */ = {
isa = PBXGroup;
children = (
96BCB7801CB40DC500C806FE /* Layer.swift */,
);
path = Layer;
sourceTree = "";
};
96D88BF41C1328D800B91418 /* Sources */ = {
isa = PBXGroup;
children = (
96BCB7EC1CB40DE900C806FE /* Font */,
9630ACB71F29A26B00B4217D /* Frameworks */,
96BCB7571CB40DC500C806FE /* iOS */,
96334EF51C8B84660083986B /* Assets.xcassets */,
96D88BFC1C1328D800B91418 /* Info.plist */,
96D88BFD1C1328D800B91418 /* LICENSE */,
96D88C091C1328D800B91418 /* Material.h */,
);
path = Sources;
sourceTree = "";
};
96EF418E1E835E850012CA1C /* Animation */ = {
isa = PBXGroup;
children = (
96BCB7821CB40DC500C806FE /* PulseAnimation.swift */,
965532281E47E388005C2792 /* SpringAnimation.swift */,
);
path = Animation;
sourceTree = "";
};
9D00EBB2216675A800DBCD69 /* Theme */ = {
isa = PBXGroup;
children = (
9D00EBB3216675FB00DBCD69 /* Theme.swift */,
);
path = Theme;
sourceTree = "";
};
9DE25DDE2170D779000C04DF /* Dialogs */ = {
isa = PBXGroup;
children = (
9DE25DDF2170D7AF000C04DF /* Dialog.swift */,
9DE25DE12170D7C0000C04DF /* DialogController.swift */,
9DE25DE32170D7FF000C04DF /* DialogView.swift */,
);
path = Dialogs;
sourceTree = "";
};
9DE84D6E1FF0250E00586C8B /* ButtonGroup */ = {
isa = PBXGroup;
children = (
9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */,
9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */,
9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */,
);
path = ButtonGroup;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
963832331B88DFD80015F710 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
96D88C321C1328D800B91418 /* Material.h in Headers */,
96BCB8141CB4115200C806FE /* PulseAnimation.swift in Headers */,
96BCB8151CB4115200C806FE /* FABButton.swift in Headers */,
96BCB8161CB4115200C806FE /* FlatButton.swift in Headers */,
96BCB8171CB4115200C806FE /* Button.swift in Headers */,
96BCB8181CB4115200C806FE /* RaisedButton.swift in Headers */,
96BCB81E1CB4115200C806FE /* DataSourceItem.swift in Headers */,
96BCB81F1CB4115200C806FE /* CollectionView.swift in Headers */,
96BCB8201CB4115200C806FE /* CollectionViewCell.swift in Headers */,
96BCB8231CB4115200C806FE /* CollectionViewLayout.swift in Headers */,
96BCB8241CB4115200C806FE /* TableViewCell.swift in Headers */,
96BCB8251CB4115200C806FE /* Color.swift in Headers */,
96BCB8261CB4115200C806FE /* Device.swift in Headers */,
96BCB8281CB4115200C806FE /* Material+String.swift in Headers */,
96BCB8291CB4115200C806FE /* Material+UIFont.swift in Headers */,
96BCB8301CB4115200C806FE /* Material+UIImage.swift in Headers */,
96BCB8311CB4115200C806FE /* Font.swift in Headers */,
96BCB8321CB4115200C806FE /* RobotoFont.swift in Headers */,
96BCB8331CB4115200C806FE /* Icon.swift in Headers */,
96BCB8341CB4115200C806FE /* Layer.swift in Headers */,
96BCB8361CB4115200C806FE /* Grid.swift in Headers */,
96BCB8371CB4115200C806FE /* Layout.swift in Headers */,
96BCB83B1CB4115200C806FE /* NavigationDrawerController.swift in Headers */,
96BCB83C1CB4115200C806FE /* Bar.swift in Headers */,
96BCB83D1CB4115200C806FE /* TransitionController.swift in Headers */,
96BCB8441CB4115200C806FE /* BottomNavigationController.swift in Headers */,
96BCB8461CB4115200C806FE /* NavigationBar.swift in Headers */,
96BCB8471CB4115200C806FE /* NavigationController.swift in Headers */,
96BCB8481CB4115200C806FE /* NavigationItem.swift in Headers */,
96BCB84A1CB4115200C806FE /* TextField.swift in Headers */,
96BCB84B1CB4115200C806FE /* TextStorage.swift in Headers */,
96BCB84C1CB4115200C806FE /* TextView.swift in Headers */,
96BCB84D1CB4115200C806FE /* Border.swift in Headers */,
96BCB84E1CB4115200C806FE /* InterimSpace.swift in Headers */,
96BCB84F1CB4115200C806FE /* Depth.swift in Headers */,
96BCB8501CB4115200C806FE /* EdgeInsets.swift in Headers */,
96BCB8521CB4115200C806FE /* CornerRadius.swift in Headers */,
96BCB8531CB4115200C806FE /* Shape.swift in Headers */,
96BCB8551CB4115200C806FE /* PulseView.swift in Headers */,
96BCB8561CB4115200C806FE /* Switch.swift in Headers */,
96BCB8571CB4115200C806FE /* View.swift in Headers */,
96E3C3951D3A1CC20086A024 /* IconButton.swift in Headers */,
96E3C3961D3A1CC20086A024 /* CollectionReusableView.swift in Headers */,
96E3C3971D3A1CC20086A024 /* Material+UIView.swift in Headers */,
96E3C3991D3A1CC20086A024 /* StatusBarController.swift in Headers */,
96E3C39A1D3A1CC20086A024 /* ErrorTextField.swift in Headers */,
96E3C39C1D3A1CC20086A024 /* Offset.swift in Headers */,
9697F7BF1D8F2572004741EC /* Divider.swift in Headers */,
9697F7C01D8F2572004741EC /* Material+CALayer.swift in Headers */,
9697F7C11D8F2572004741EC /* Material+Array.swift in Headers */,
9697F7C21D8F2572004741EC /* Material+UIWindow.swift in Headers */,
9697F7C31D8F2572004741EC /* DynamicFontType.swift in Headers */,
9697F7CB1D8F2573004741EC /* Snackbar.swift in Headers */,
9697F7CC1D8F2573004741EC /* SnackbarController.swift in Headers */,
9617B07D1DFCA8CF00410F8F /* Application.swift in Headers */,
9617B07E1DFCA8CF00410F8F /* Card.swift in Headers */,
9617B07F1DFCA8CF00410F8F /* ImageCard.swift in Headers */,
9617B0801DFCA8CF00410F8F /* PresenterCard.swift in Headers */,
9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */,
9617B08A1DFCA8CF00410F8F /* DisplayStyle.swift in Headers */,
9617B08B1DFCA8CF00410F8F /* Screen.swift in Headers */,
9617B08C1DFCA8CF00410F8F /* SearchBar.swift in Headers */,
9617B08D1DFCA8CF00410F8F /* SearchBarController.swift in Headers */,
9617B08E1DFCA8CF00410F8F /* TabBar.swift in Headers */,
9617B08F1DFCA8CF00410F8F /* Material+NSMutableAttributedString.swift in Headers */,
9617B0901DFCA8CF00410F8F /* Toolbar.swift in Headers */,
9617B0911DFCA8CF00410F8F /* ToolbarController.swift in Headers */,
96328B9B1E05C24E009A4C90 /* CollectionViewController.swift in Headers */,
96328B9E1E05C24E009A4C90 /* TableView.swift in Headers */,
96328B9F1E05C24E009A4C90 /* TableViewController.swift in Headers */,
96BFC16F1E63C10A0075DE1F /* SpringAnimation.swift in Headers */,
9685D5AF1F0F04CB00AFEB79 /* CardCollectionViewCell.swift in Headers */,
9685D5B01F0F04CB00AFEB79 /* CardCollectionViewController.swift in Headers */,
9685D5B11F0F04CB00AFEB79 /* Material+MotionAnimation.swift in Headers */,
96F1A5531F24F17A001D8CAF /* TabsController.swift in Headers */,
9618006E1F4D38BC00CD77A1 /* ChipBar.swift in Headers */,
9618006F1F4D38BC00CD77A1 /* ChipBarController.swift in Headers */,
961800701F4D38BC00CD77A1 /* Material+UIViewController.swift in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
963832351B88DFD80015F710 /* Material */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9638324C1B88DFD80015F710 /* Build configuration list for PBXNativeTarget "Material" */;
buildPhases = (
963832311B88DFD80015F710 /* Sources */,
963832331B88DFD80015F710 /* Headers */,
963832341B88DFD80015F710 /* Resources */,
);
buildRules = (
);
dependencies = (
968BA8381F8D20530091852E /* PBXTargetDependency */,
);
name = Material;
productName = FocusKit;
productReference = 963832361B88DFD80015F710 /* Material.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
9638322D1B88DFD80015F710 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftMigration = 0710;
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 1100;
ORGANIZATIONNAME = "CosmicMind, Inc.";
TargetAttributes = {
963832351B88DFD80015F710 = {
CreatedOnToolsVersion = 6.4;
DevelopmentTeam = 9Z76XCNLGL;
DevelopmentTeamName = "CosmicMind Inc.";
LastSwiftMigration = 1020;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 963832301B88DFD80015F710 /* Build configuration list for PBXProject "Material" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 9638322C1B88DFD80015F710;
productRefGroup = 963832371B88DFD80015F710 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 968BA8321F8D201B0091852E /* Products */;
ProjectRef = 968BA8311F8D201B0091852E /* Motion.xcodeproj */;
},
);
projectRoot = "";
targets = (
963832351B88DFD80015F710 /* Material */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
968BA8361F8D201B0091852E /* Motion.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = Motion.framework;
remoteRef = 968BA8351F8D201B0091852E /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
963832341B88DFD80015F710 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
96334EF61C8B84660083986B /* Assets.xcassets in Resources */,
96BCB7F71CB40DE900C806FE /* Roboto-Medium.ttf in Resources */,
96BCB7F31CB40DE900C806FE /* Roboto-Bold.ttf in Resources */,
96BCB7FB1CB40DE900C806FE /* Roboto-Thin.ttf in Resources */,
96BCB7F91CB40DE900C806FE /* Roboto-Regular.ttf in Resources */,
96BCB7F51CB40DE900C806FE /* Roboto-Light.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
963832311B88DFD80015F710 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
961E6BE21DDA2AF3004E6C93 /* Screen.swift in Sources */,
9DF352441FED20ED00B2A11B /* RadioButton.swift in Sources */,
965E81261DD4D7C800D61E4B /* Material+NSMutableAttributedString.swift in Sources */,
965E80FF1DD4D5C800D61E4B /* BottomNavigationController.swift in Sources */,
965E81031DD4D5C800D61E4B /* CollectionView.swift in Sources */,
965E81041DD4D5C800D61E4B /* CollectionViewCell.swift in Sources */,
965E81071DD4D5C800D61E4B /* CollectionViewLayout.swift in Sources */,
965E81081DD4D5C800D61E4B /* CollectionReusableView.swift in Sources */,
965E81091DD4D5C800D61E4B /* DataSourceItem.swift in Sources */,
9DF352461FED210000B2A11B /* CheckButton.swift in Sources */,
965E810A1DD4D5C800D61E4B /* Font.swift in Sources */,
965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */,
9D494A3C217F6B7D003D66F1 /* LayoutConstraint.swift in Sources */,
965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */,
965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */,
965E81111DD4D5C800D61E4B /* NavigationController.swift in Sources */,
965E81121DD4D5C800D61E4B /* NavigationItem.swift in Sources */,
965E81131DD4D5C800D61E4B /* NavigationDrawerController.swift in Sources */,
9656895F1F002F16001C656D /* CardCollectionViewCell.swift in Sources */,
965E81161DD4D5C800D61E4B /* DisplayStyle.swift in Sources */,
965E81171DD4D5C800D61E4B /* TransitionController.swift in Sources */,
965E81181DD4D5C800D61E4B /* Snackbar.swift in Sources */,
965E81191DD4D5C800D61E4B /* SnackbarController.swift in Sources */,
9618006D1F4D384200CD77A1 /* Material+UIViewController.swift in Sources */,
9DE84D741FF0252600586C8B /* CheckButtonGroup.swift in Sources */,
965E811A1DD4D5C800D61E4B /* StatusBarController.swift in Sources */,
965E811B1DD4D5C800D61E4B /* Switch.swift in Sources */,
965E811C1DD4D5C800D61E4B /* TabBar.swift in Sources */,
965E811D1DD4D5C800D61E4B /* TableViewCell.swift in Sources */,
9D9089B92118914500605DC9 /* Editor.swift in Sources */,
965E811E1DD4D5C800D61E4B /* TextField.swift in Sources */,
965E811F1DD4D5C800D61E4B /* ErrorTextField.swift in Sources */,
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */,
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */,
965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */,
9D494A38217F6B63003D66F1 /* LayoutAttribute.swift in Sources */,
96B8D22D20CF82D5008BD149 /* FABMenu.swift in Sources */,
965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */,
965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */,
9DE84D731FF0252600586C8B /* BaseButtonGroup.swift in Sources */,
965E80F71DD4D59500D61E4B /* Card.swift in Sources */,
965E80EA1DD4C55200D61E4B /* Material+UIFont.swift in Sources */,
965E80EB1DD4C55200D61E4B /* Material+UIImage.swift in Sources */,
965E80EC1DD4C55200D61E4B /* Material+Array.swift in Sources */,
965E80ED1DD4C55200D61E4B /* Material+UIWindow.swift in Sources */,
961527B91F3A509900E8B2AC /* ChipBarController.swift in Sources */,
965E80E41DD4C53300D61E4B /* PulseView.swift in Sources */,
9DF352421FED20C900B2A11B /* BaseIconLayerButton.swift in Sources */,
966C17731F0439F600D3E83C /* Material+MotionAnimation.swift in Sources */,
965E80E51DD4C53300D61E4B /* PulseAnimation.swift in Sources */,
9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */,
9D00EBB4216675FB00DBCD69 /* Theme.swift in Sources */,
965E80FE1DD4D59500D61E4B /* ToolbarController.swift in Sources */,
9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */,
9DE25DE42170D7FF000C04DF /* DialogView.swift in Sources */,
96328B971E05C0BB009A4C90 /* TableView.swift in Sources */,
965E80F81DD4D59500D61E4B /* ImageCard.swift in Sources */,
96328B991E05C0CE009A4C90 /* TableViewController.swift in Sources */,
9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */,
965E80F91DD4D59500D61E4B /* PresenterCard.swift in Sources */,
96E09DC81F2287E50000B121 /* TabsController.swift in Sources */,
961154CC1F32A7B100A78D74 /* ChipBar.swift in Sources */,
965689611F002F4C001C656D /* CardCollectionViewController.swift in Sources */,
965E80CC1DD4C50600D61E4B /* Bar.swift in Sources */,
965E80CD1DD4C50600D61E4B /* Button.swift in Sources */,
965E80CE1DD4C50600D61E4B /* FABButton.swift in Sources */,
965E80CF1DD4C50600D61E4B /* FlatButton.swift in Sources */,
9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */,
965E80D01DD4C50600D61E4B /* RaisedButton.swift in Sources */,
9DF58CEE20C098C60098968D /* ErrorTextFieldValidator.swift in Sources */,
9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */,
965E80D11DD4C50600D61E4B /* IconButton.swift in Sources */,
965E80D21DD4C50600D61E4B /* Color.swift in Sources */,
96BFC1541E5E486F0075DE1F /* SpringAnimation.swift in Sources */,
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */,
9D494A3A217F6B70003D66F1 /* LayoutAnchor.swift in Sources */,
965E80D31DD4C50600D61E4B /* Device.swift in Sources */,
965E80FD1DD4D59500D61E4B /* Toolbar.swift in Sources */,
965E80D41DD4C50600D61E4B /* Divider.swift in Sources */,
965E80D51DD4C50600D61E4B /* Grid.swift in Sources */,
965E80D61DD4C50600D61E4B /* HeightPreset.swift in Sources */,
961E6BDF1DDA2A95004E6C93 /* Application.swift in Sources */,
965E80D71DD4C50600D61E4B /* Icon.swift in Sources */,
965E80FC1DD4D59500D61E4B /* SearchBarController.swift in Sources */,
965E80D81DD4C50600D61E4B /* Layer.swift in Sources */,
965E80D91DD4C50600D61E4B /* Layout.swift in Sources */,
965E80DA1DD4C50600D61E4B /* Border.swift in Sources */,
965E80DB1DD4C50600D61E4B /* InterimSpace.swift in Sources */,
965E80DC1DD4C50600D61E4B /* Depth.swift in Sources */,
965E80DD1DD4C50600D61E4B /* EdgeInsets.swift in Sources */,
9DF74C4E20D15D84003C1D66 /* Material+UIColor.swift in Sources */,
965E80DF1DD4C50600D61E4B /* CornerRadius.swift in Sources */,
965E80FB1DD4D59500D61E4B /* SearchBar.swift in Sources */,
965E80E01DD4C50600D61E4B /* Shape.swift in Sources */,
965E80E11DD4C50600D61E4B /* Offset.swift in Sources */,
965E80E21DD4C50600D61E4B /* View.swift in Sources */,
96B8D22C20CF82D2008BD149 /* FABMenuController.swift in Sources */,
96328B7A1E020A41009A4C90 /* CollectionViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
968BA8381F8D20530091852E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "Motion iOS";
targetProxy = 968BA8371F8D20530091852E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
9638324A1B88DFD80015F710 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = Material;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
9638324B1B88DFD80015F710 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = Material;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
9638324D1B88DFD80015F710 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_OBJC_UNUSED_IVARS = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/**";
GCC_WARN_UNUSED_VALUE = YES;
INFOPLIST_FILE = Sources/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @rpath/Frameworks";
MARKETING_VERSION = 3.1.8;
PRODUCT_BUNDLE_IDENTIFIER = com.cosmicmind.Material;
PRODUCT_NAME = Material;
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
9638324E1B88DFD80015F710 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_OBJC_UNUSED_IVARS = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/**";
GCC_WARN_UNUSED_VALUE = YES;
INFOPLIST_FILE = Sources/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @rpath/Frameworks";
MARKETING_VERSION = 3.1.8;
PRODUCT_BUNDLE_IDENTIFIER = com.cosmicmind.Material;
PRODUCT_NAME = Material;
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
963832301B88DFD80015F710 /* Build configuration list for PBXProject "Material" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9638324A1B88DFD80015F710 /* Debug */,
9638324B1B88DFD80015F710 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9638324C1B88DFD80015F710 /* Build configuration list for PBXNativeTarget "Material" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9638324D1B88DFD80015F710 /* Debug */,
9638324E1B88DFD80015F710 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 9638322D1B88DFD80015F710 /* Project object */;
}
================================================
FILE: Material.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: Material.xcodeproj/project.xcworkspace/xcshareddata/Material.xcscmblueprint
================================================
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "9CF35BC641478FBD765F7442D4034ED362261E0A",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"9CF35BC641478FBD765F7442D4034ED362261E0A" : 9223372036854775807,
"0F3E254D46E5A5B90D1542854F510B7D145A9F31" : 9223372036854775807
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "0F44C283-777E-4E3F-9E10-FC52C3C270CE",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"9CF35BC641478FBD765F7442D4034ED362261E0A" : "Material\/",
"0F3E254D46E5A5B90D1542854F510B7D145A9F31" : "Material\/Sources\/Frameworks\/Motion\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "Material",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Material.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/CosmicMind\/Motion.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "0F3E254D46E5A5B90D1542854F510B7D145A9F31"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/CosmicMind\/Material.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "9CF35BC641478FBD765F7442D4034ED362261E0A"
}
]
}
================================================
FILE: Material.xcodeproj/xcshareddata/xcschemes/Material.xcscheme
================================================
================================================
FILE: Package.swift
================================================
// swift-tools-version:4.2
import PackageDescription
let package = Package(
name: "Material",
// platforms: [.iOS("8.0")],
products: [
.library(name: "Material", targets: ["Material"])
],
dependencies: [
.package(url: "https://github.com/CosmicMind/Motion.git", .upToNextMajor(from: "3.1.0")),
],
targets: [
.target(
name: "Material",
dependencies: ["Motion"],
path: "Sources",
exclude: ["Frameworks"]
)
]
)
================================================
FILE: README.md
================================================

# Material
Welcome to **Material,** a UI/UX framework for creating beautiful applications. Material's animation system has been completely reworked to take advantage of [Motion](https://github.com/CosmicMind/Motion), a library dedicated to animations and transitions.
[](https://github.com/Carthage/Carthage)
[](https://github.com/JamitLabs/Accio)
[](http://cocoapods.org/pods/Material)
[](https://github.com/CosmicMind/Material/blob/master/LICENSE.md)



[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9D6MURMLLUNQ2)
## Photos Sample
Take a look at a sample [Photos](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Photos) project to get started.

## Sample Projects
Take a look at [Sample Projects](https://github.com/CosmicMind/Samples) to get your projects started.
## Features
- [x] Completely Customizable
- [x] [Motion Animations & Transitions](https://github.com/CosmicMind/Motion)
- [x] Layout Tools for AutoLayout & Grid Systems
- [x] Color Library
- [x] Cards
- [x] FABMenu
- [x] Icons
- [x] TextField
- [x] Snackbar
- [x] Tabs
- [x] Chips
- [x] SearchBar
- [x] NavigationController
- [x] NavigationDrawer
- [x] BottomNavigationBar
- [x] [Sample Projects](https://github.com/CosmicMind/Samples)
- [x] And More...
## Requirements
* iOS 8.0+
* Xcode 8.0+
## Communication
- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/cosmicmind). (Tag 'cosmicmind')
- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/cosmicmind).
- If you **found a bug**, _and can provide steps to reliably reproduce it_, open an issue.
- If you **have a feature request**, open an issue.
- If you **want to contribute**, submit a pull request.
## Installation
> **Embedded frameworks require a minimum deployment target of iOS 8+.**
> - [Download Material](https://github.com/CosmicMind/Material/archive/master.zip)
## CocoaPods
[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
```bash
$ gem install cocoapods
```
To integrate Material's core features into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Material', '~> 3.1.0'
```
Then, run the following command:
```bash
$ pod install
```
## Carthage
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with Homebrew using the following command:
```bash
$ brew update
$ brew install carthage
```
To integrate Material into your Xcode project using Carthage, specify it in your Cartfile:
```bash
github "CosmicMind/Material"
```
Run `carthage update` to build the framework and drag the built `Material.framework` into your Xcode project.
## Change Log
Material is a growing project and will encounter changes throughout its development. It is recommended that the [Change Log](https://github.com/CosmicMind/Material/blob/master/CHANGELOG.md) be reviewed prior to updating versions.
## Icons
Icons is a library of Google and CosmicMind icons that are available for use within your iOS applications.

[Learn More](https://github.com/CosmicMind/Material/blob/master/Sources/iOS/Icon/Icon.swift)
## Colors
Try the Material Colors app to see the wonderful colors available in Material, or use the online version at [MaterialColor.com](http://materialcolor.com).

* [Material Colors iOS App](https://itunes.apple.com/app/x/id1111994400?mt=8)
## TextField
A TextField is an excellent way to improve UX. It allows for a placeholder and additional hint details.

* [TextField Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/TextField)
## Button
A button is used to trigger an action through a touch event. Material comes with a foundational button, and 4 specialized buttons that can be stylized in any way.

* [Button Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Button)
## Switch
A switch is a control component that toggles between on and off states.

* [Switch Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Switch)
## Card
A Card is a flexible component that may be configured in any way you like. It has a Toolbar, Bar, and content area that may utilize any UIView type.

* [Card Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Card)
## ImageCard
An ImageCard is an expansion of the base Card. The Toolbar overlays an image area that sits above the dynamic content area.

* [ImageCard Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/ImageCard)
## PresenterCard
The PresenterCard is a completely new card style. It allows for a primary presentation area that may be any UIView type in addition to the content area, Toolbar, and Bar components. The options for this card are endless.

* [PresenterCard Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/PresenterCard)
## FABMenu
A FABMenu manages a collection of views. A new MenuItem type has been added that manages a title and button to improve UX and visual beauty.

* [FABMenu Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/FABMenuController)
## Toolbar
Toolbars are super flexible and add excellent control to your navigation flow. They manage a set of left and right views with auto aligning title and detail labels.

* [Toolbar Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/ToolbarController)
## SearchBar
A SearchBar is a powerful navigation tool that allows for user's input with an instant visual response. A set of left and right views may be added to expand functionality.

* [SearchBar Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Search)
## Tabs
Tabs is a new component that links a customizable TabBar to a stack of view controllers making a powerful and visually pleasing component to have in any application.

* [Tabs Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/TabsController)
## NavigationController
A NavigationController is a specialized view controller that manages a hierarchy of content efficiently, making it easier for users to move within an application.

* [NavigationController Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/NavigationController)
## NavigationDrawer
A NavigationDrawer slides in from the left or right and contains the navigation destinations for your application.

* [NavigationDrawer Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/NavigationDrawerController)
## Snackbar
A Snackbar is a new component that is very simple in its behavior and very powerful in its message. It can be used application wide, or isolated to specific view controllers.

* [Snackbar Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/SnackbarController)
## Sticker Sheet
To help template your project, checkout Material Sticker Sheet.

* [Material Sticker Sheet](http://www.materialup.com/posts/material-design-sticker-sheets)
## Much More...
So much more inside. Enjoy!
## License
The MIT License (MIT)
Copyright (C) 2019, CosmicMind, Inc. .
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: Sources/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_add_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_add_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_add_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_add_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_arrow_back_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_arrow_back_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_arrow_back_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_arrow_back_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_arrow_downward_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_arrow_downward_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_arrow_downward_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_arrow_downward_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_audio_library_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_audio_library_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_audio_library_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_audio_library_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_audio_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_audio_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_audio_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_audio_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_bell_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_bell_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_bell_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_bell_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_check_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_check_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_check_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_check_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_close_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_close_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_close_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_close_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_image_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_image_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_image_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_image_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_menu_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_menu_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_menu_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_menu_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_microphone_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_microphone_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_microphone_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_microphone_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_more_horiz_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_more_horiz_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_more_horiz_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_more_horiz_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_more_vert_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_more_vert_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_more_vert_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_more_vert_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_movie_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_movie_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_movie_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_movie_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_pause_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_pause_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_pause_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_pause_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_pen_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_pen_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_pen_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_pen_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_photo_camera_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_photo_camera_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_photo_camera_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_photo_camera_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_photo_library_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_photo_library_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_photo_library_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_photo_library_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_play_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_play_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_play_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_play_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_search_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_search_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_search_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_search_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_settings_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_settings_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_settings_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_settings_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_share_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_share_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_share_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_share_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_shuffle_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_shuffle_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_shuffle_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_shuffle_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_skip_backward_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_skip_backward_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_skip_backward_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_skip_backward_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_skip_forward_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_skip_forward_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_skip_forward_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_skip_forward_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_star_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_star_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_star_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_star_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_videocam_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_videocam_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_videocam_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_videocam_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_volume_high_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_volume_high_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_volume_high_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_volume_high_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_volume_medium_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_volume_medium_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_volume_medium_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_volume_medium_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/cm_volume_off_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "cm_volume_off_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cm_volume_off_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cm_volume_off_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_add_circle_outline_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_add_circle_outline_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_add_circle_outline_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_add_circle_outline_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_add_circle_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_add_circle_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_add_circle_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_add_circle_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_add_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_add_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_add_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_add_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_arrow_back_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_arrow_back_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_arrow_back_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_arrow_back_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_arrow_downward_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_arrow_downward_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_arrow_downward_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_arrow_downward_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_audiotrack_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_audiotrack_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_audiotrack_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_audiotrack_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_camera_front_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_camera_front_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_camera_front_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_camera_front_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_camera_rear_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_camera_rear_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_camera_rear_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_camera_rear_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_check_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_check_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_check_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_check_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_close_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_close_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_close_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_close_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_edit_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_edit_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_edit_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_edit_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_email_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_email_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_email_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_email_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_favorite_border_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_favorite_border_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_favorite_border_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_favorite_border_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_favorite_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_favorite_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_favorite_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_favorite_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_flash_auto_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_flash_auto_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_flash_auto_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_flash_auto_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_flash_off_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_flash_off_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_flash_off_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_flash_off_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_flash_on_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_flash_on_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_flash_on_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_flash_on_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_history_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_history_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_history_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_history_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_home_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_home_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_home_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_home_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_image_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_image_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_image_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_image_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_menu_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_menu_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_menu_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_menu_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_more_horiz_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_more_horiz_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_more_horiz_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_more_horiz_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_more_vert_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_more_vert_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_more_vert_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_more_vert_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_movie_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_movie_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_movie_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_movie_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_phone_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_phone_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_phone_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_phone_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_photo_camera_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_photo_camera_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_photo_camera_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_photo_camera_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_photo_library_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_photo_library_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_photo_library_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_photo_library_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_place_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_place_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_place_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_place_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_search_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_search_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_search_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_search_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_settings_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_settings_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_settings_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_settings_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_share_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_share_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_share_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_share_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_star_border_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_star_border_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_star_border_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_star_border_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_star_half_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_star_half_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_star_half_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_star_half_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_star_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_star_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_star_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_star_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_videocam_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_videocam_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_videocam_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_videocam_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_visibility_off_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_visibility_off_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_visibility_off_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_visibility_off_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_visibility_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_visibility_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_visibility_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_visibility_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Assets.xcassets/ic_work_white.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_work_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_work_white@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_work_white@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Sources/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
FMWK
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleSignature
????
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
LSApplicationCategoryType
NSHumanReadableCopyright
Copyright © 2017 CosmicMind, Inc. All rights reserved.
NSPrincipalClass
UIAppFonts
Roboto-Thin
Roboto-Light.ttf
Roboto-Regular.ttf
Roboto-Medium.ttf
Roboto-Bold
================================================
FILE: Sources/LICENSE
================================================
The MIT License (MIT)
Copyright (C) 2019, CosmicMind, Inc. .
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: Sources/Material.h
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import
//! Project version number for Material.
FOUNDATION_EXPORT double MaterialVersionNumber;
//! Project version string for Material.
FOUNDATION_EXPORT const unsigned char MaterialVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import
================================================
FILE: Sources/iOS/Animation/PulseAnimation.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc(PulseAnimation)
public enum PulseAnimation: Int {
case none
case center
case centerWithBacking
case centerRadialBeyondBounds
case radialBeyondBounds
case backing
case point
case pointWithBacking
}
public protocol Pulseable {
/// A reference to the PulseAnimation.
var pulseAnimation: PulseAnimation { get set }
/// A UIColor.
var pulseColor: UIColor { get set }
/// The opcaity value for the pulse animation.
var pulseOpacity: CGFloat { get set }
}
internal protocol PulseableLayer {
/// A reference to the pulse layer.
var pulseLayer: CALayer? { get }
}
public struct Pulse {
/// A UIView that is Pulseable.
fileprivate weak var pulseView: UIView?
/// The layer the pulse layers are added to.
internal weak var pulseLayer: CALayer?
/// Pulse layers.
fileprivate var layers = [CAShapeLayer]()
/// A reference to the PulseAnimation.
public var animation = PulseAnimation.pointWithBacking
/// A UIColor.
public var color = Color.grey.base
/// The opcaity value for the pulse animation.
public var opacity: CGFloat = 0.18
/**
An initializer that takes a given view and pulse layer.
- Parameter pulseView: An optional UIView.
- Parameter pulseLayer: An optional CALayer.
*/
public init(pulseView: UIView?, pulseLayer: CALayer?) {
self.pulseView = pulseView
self.pulseLayer = pulseLayer
}
/**
Triggers the expanding animation.
- Parameter point: A point to pulse from.
*/
public mutating func expand(point: CGPoint) {
guard let view = pulseView else {
return
}
guard let layer = pulseLayer else {
return
}
guard .none != animation else {
return
}
let bLayer = CAShapeLayer()
let pLayer = CAShapeLayer()
bLayer.addSublayer(pLayer)
layer.addSublayer(bLayer)
bLayer.zPosition = 0
pLayer.zPosition = 0
layers.insert(bLayer, at: 0)
layer.masksToBounds = !(.centerRadialBeyondBounds == animation || .radialBeyondBounds == animation)
let w = view.bounds.width
let h = view.bounds.height
Motion.disable { [
n = .center == animation ? min(w, h) : max(w, h),
bounds = layer.bounds,
animation = animation,
color = color,
opacity = opacity
] in
bLayer.frame = bounds
pLayer.frame = CGRect(x: 0, y: 0, width: n, height: n)
switch animation {
case .center, .centerWithBacking, .centerRadialBeyondBounds:
pLayer.position = CGPoint(x: w / 2, y: h / 2)
default:
pLayer.position = point
}
pLayer.cornerRadius = n / 2
pLayer.backgroundColor = color.withAlphaComponent(opacity).cgColor
pLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransform(scaleX: 0, y: 0))
}
bLayer.setValue(false, forKey: "animated")
let t: TimeInterval = .center == animation ? 0.16125 : 0.325
switch animation {
case .centerWithBacking, .backing, .pointWithBacking:
bLayer.animate(.background(color: color.withAlphaComponent(opacity / 2)), .duration(t))
default:break
}
switch animation {
case .center, .centerWithBacking, .centerRadialBeyondBounds, .radialBeyondBounds, .point, .pointWithBacking:
pLayer.animate(.scale(1), .duration(t))
default:break
}
Motion.delay(t) {
bLayer.setValue(true, forKey: "animated")
}
}
/// Triggers the contracting animation.
public mutating func contract() {
guard let bLayer = layers.popLast() else {
return
}
guard let animated = bLayer.value(forKey: "animated") as? Bool else {
return
}
Motion.delay(animated ? 0 : 0.15) { [animation = animation, color = color] in
guard let pLayer = bLayer.sublayers?.first as? CAShapeLayer else {
return
}
let t: TimeInterval = 0.325
switch animation {
case .centerWithBacking, .backing, .pointWithBacking:
bLayer.animate(.background(color: color.withAlphaComponent(0)), .duration(t))
default:break
}
switch animation {
case .center, .centerWithBacking, .centerRadialBeyondBounds, .radialBeyondBounds, .point, .pointWithBacking:
pLayer.animate(.background(color: color.withAlphaComponent(0)))
default:break
}
Motion.delay(t) {
pLayer.removeFromSuperlayer()
bLayer.removeFromSuperlayer()
}
}
}
}
================================================
FILE: Sources/iOS/Animation/SpringAnimation.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(SpringDirection)
public enum SpringDirection: Int {
case up
case down
case left
case right
}
open class SpringAnimation {
/// A SpringDirection value.
open var springDirection = SpringDirection.up
/// A Boolean that indicates if the menu is open or not.
open var isOpened = false
/// Enables the animations for the Menu.
open var isEnabled = true
/// A preset wrapper around interimSpace.
open var interimSpacePreset = InterimSpacePreset.none {
didSet {
interimSpace = InterimSpacePresetToValue(preset: interimSpacePreset)
}
}
/// The space between views.
open var interimSpace: InterimSpace = 0 {
didSet {
reload()
}
}
/// An Array of UIViews.
open var views = [UIView]() {
didSet {
reload()
}
}
/// An Optional base view size.
open var baseSize = CGSize(width: 48, height: 48) {
didSet {
reload()
}
}
/// Size of views.
open var itemSize = CGSize(width: 48, height: 48) {
didSet {
reload()
}
}
/// Reload the view layout.
open func reload() {
isOpened = false
for i in 0.. Void)? = nil, completion: ((UIView) -> Void)? = nil) {
guard isEnabled else {
return
}
disable()
switch springDirection {
case .up:
expandUp(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
case .down:
expandDown(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
case .left:
expandLeft(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
case .right:
expandRight(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
}
/**
Contracts the Spring component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func contract(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIView.AnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
guard isEnabled else {
return
}
disable()
switch springDirection {
case .up:
contractUp(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
case .down:
contractDown(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
case .left:
contractLeft(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
case .right:
contractRight(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
}
}
extension SpringAnimation {
/**
Handles the animation open completion.
- Parameter view: A UIView.
- Parameter completion: A completion handler.
*/
fileprivate func handleOpenCompletion(view: UIView, completion: ((UIView) -> Void)?) {
enable(view: view)
if view == views.last {
isOpened = true
}
completion?(view)
}
/**
Handles the animation contract completion.
- Parameter view: A UIView.
- Parameter completion: A completion handler.
*/
fileprivate func handleCloseCompletion(view: UIView, completion: ((UIView) -> Void)?) {
view.isHidden = true
enable(view: view)
if view == views.last {
isOpened = false
}
completion?(view)
}
}
extension SpringAnimation {
/**
Open the Menu component with animation options in the Up direction.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func expandUp(duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIView.AnimationOptions, animations: ((UIView) -> Void)?, completion: ((UIView) -> Void)?) {
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
guard let first = views.first else {
return
}
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
guard let first = views.first else {
return
}
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
for i in 0.. Void)?, completion: ((UIView) -> Void)?) {
guard let first = views.first else {
return
}
let w = baseSize.width
for i in 0...
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public struct Application {
/// A reference to UIApplication.shared application which doesn't trigger linker errors when Material is included in an extension
/// Note that this will crash if actually called from within an extension
public static var shared: UIApplication {
let sharedSelector = NSSelectorFromString("sharedApplication")
guard UIApplication.responds(to: sharedSelector) else {
fatalError("[Material: Extensions cannot access Application]")
}
let shared = UIApplication.perform(sharedSelector)
return shared?.takeUnretainedValue() as! UIApplication
}
/// An optional reference to the main UIWindow.
public static var keyWindow: UIWindow? {
return Application.shared.keyWindow
}
/// An optional reference to the top most view controller.
public static var rootViewController: UIViewController? {
return keyWindow?.rootViewController
}
/// A boolean indicating if the device is in Landscape mode.
public static var isLandscape: Bool {
return Application.shared.statusBarOrientation.isLandscape
}
/// A boolean indicating if the device is in Portrait mode.
public static var isPortrait: Bool {
return !isLandscape
}
/// The current UIInterfaceOrientation value.
public static var orientation: UIInterfaceOrientation {
return Application.shared.statusBarOrientation
}
/// Retrieves the device status bar style.
public static var statusBarStyle: UIStatusBarStyle {
get {
return Application.shared.statusBarStyle
}
set(value) {
Application.shared.statusBarStyle = value
}
}
/// Retrieves the device status bar hidden state.
public static var isStatusBarHidden: Bool {
get {
return Application.shared.isStatusBarHidden
}
set(value) {
Application.shared.isStatusBarHidden = value
}
}
/**
A boolean that indicates based on iPhone rules if the
status bar should be shown.
*/
public static var shouldStatusBarBeHidden: Bool {
return isLandscape && .phone == Device.userInterfaceIdiom
}
/// A reference to the user interface layout direction.
public static var userInterfaceLayoutDirection: UIUserInterfaceLayoutDirection {
return Application.shared.userInterfaceLayoutDirection
}
}
================================================
FILE: Sources/iOS/Bar/Bar.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(ContentViewAlignment)
public enum ContentViewAlignment: Int {
case full
case center
}
open class Bar: View {
/// Will layout the view.
open var willLayout: Bool {
return 0 < bounds.width && 0 < bounds.height && nil != superview && !grid.isDeferred
}
open override var intrinsicContentSize: CGSize {
return bounds.size
}
/// Should center the contentView.
open var contentViewAlignment = ContentViewAlignment.full {
didSet {
layoutSubviews()
}
}
/// A preset wrapper around contentEdgeInsets.
open var contentEdgeInsetsPreset: EdgeInsetsPreset {
get {
return grid.contentEdgeInsetsPreset
}
set(value) {
grid.contentEdgeInsetsPreset = value
}
}
/// A reference to EdgeInsets.
@IBInspectable
open var contentEdgeInsets: EdgeInsets {
get {
return grid.contentEdgeInsets
}
set(value) {
grid.contentEdgeInsets = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return grid.interimSpacePreset
}
set(value) {
grid.interimSpacePreset = value
}
}
/// A wrapper around grid.interimSpace.
@IBInspectable
open var interimSpace: InterimSpace {
get {
return grid.interimSpace
}
set(value) {
grid.interimSpace = value
}
}
/// Grid cell factor.
@IBInspectable
open var gridFactor: CGFloat = 12 {
didSet {
assert(0 < gridFactor, "[Material Error: gridFactor must be greater than 0.]")
layoutSubviews()
}
}
/// ContentView that holds the any desired subviews.
public let contentView = UIView()
/// Left side UIViews.
open var leftViews = [UIView]() {
didSet {
oldValue.forEach {
$0.removeFromSuperview()
}
layoutSubviews()
}
}
/// Right side UIViews.
open var rightViews = [UIView]() {
didSet {
oldValue.forEach {
$0.removeFromSuperview()
}
layoutSubviews()
}
}
/// Center UIViews.
open var centerViews: [UIView] {
get {
return contentView.grid.views
}
set(value) {
contentView.grid.views = value
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
/**
An initializer that initializes the object with a CGRect object.
If AutoLayout is used, it is better to initilize the instance
using the init() initializer.
- Parameter frame: A CGRect instance.
*/
public override init(frame: CGRect) {
super.init(frame: frame)
}
/**
A convenience initializer with parameter settings.
- Parameter leftViews: An Array of UIViews that go on the left side.
- Parameter rightViews: An Array of UIViews that go on the right side.
- Parameter centerViews: An Array of UIViews that go in the center.
*/
public convenience init(leftViews: [UIView]? = nil, rightViews: [UIView]? = nil, centerViews: [UIView]? = nil) {
self.init()
self.leftViews = leftViews ?? []
self.rightViews = rightViews ?? []
self.centerViews = centerViews ?? []
}
open override func layoutSubviews() {
super.layoutSubviews()
guard willLayout else {
return
}
var lc = 0
var rc = 0
grid.begin()
grid.views.removeAll()
for v in leftViews {
if let b = v as? UIButton {
b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero
}
v.frame.size.width = v.intrinsicContentSize.width
v.sizeToFit()
v.grid.columns = Int(ceil(v.bounds.width / gridFactor)) + 2
lc += v.grid.columns
grid.views.append(v)
}
grid.views.append(contentView)
for v in rightViews {
if let b = v as? UIButton {
b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero
}
v.frame.size.width = v.intrinsicContentSize.width
v.sizeToFit()
v.grid.columns = Int(ceil(v.bounds.width / gridFactor)) + 2
rc += v.grid.columns
grid.views.append(v)
}
contentView.grid.begin()
contentView.grid.offset.columns = 0
var l: CGFloat = 0
var r: CGFloat = 0
if .center == contentViewAlignment {
if leftViews.count < rightViews.count {
r = CGFloat(rightViews.count) * interimSpace
l = r
} else {
l = CGFloat(leftViews.count) * interimSpace
r = l
}
}
let p = bounds.width - l - r - contentEdgeInsets.left - contentEdgeInsets.right
let columns = Int(ceil(p / gridFactor))
if .center == contentViewAlignment {
if lc < rc {
contentView.grid.columns = columns - 2 * rc
contentView.grid.offset.columns = rc - lc
} else {
contentView.grid.columns = columns - 2 * lc
rightViews.first?.grid.offset.columns = lc - rc
}
} else {
contentView.grid.columns = columns - lc - rc
}
grid.axis.columns = columns
grid.commit()
contentView.grid.commit()
layoutDivider()
}
open override func prepare() {
super.prepare()
heightPreset = .normal
autoresizingMask = .flexibleWidth
interimSpacePreset = .interimSpace3
contentEdgeInsetsPreset = .square1
prepareContentView()
}
}
extension Bar {
/// Prepares the contentView.
fileprivate func prepareContentView() {
contentView.contentScaleFactor = Screen.scale
}
}
================================================
FILE: Sources/iOS/BottomTabBar/BottomNavigationController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
extension UIViewController {
/**
A convenience property that provides access to the BottomNavigationController.
This is the recommended method of accessing the BottomNavigationController
through child UIViewControllers.
*/
public var bottomNavigationController: BottomNavigationController? {
return traverseViewControllerHierarchyForClassType()
}
}
private class MaterialTabBar: UITabBar {
override func sizeThatFits(_ size: CGSize) -> CGSize {
var v = super.sizeThatFits(size)
let offset = v.height - HeightPreset.normal.rawValue
v.height = heightPreset.rawValue + offset
return v
}
}
open class BottomNavigationController: UITabBarController, Themeable {
/// A Boolean that controls if the swipe feature is enabled.
open var isSwipeEnabled = true {
didSet {
guard isSwipeEnabled else {
removeSwipeGesture()
return
}
prepareSwipeGesture()
}
}
/**
A UIPanGestureRecognizer property internally used for the interactive
swipe.
*/
public private(set) var interactiveSwipeGesture: UIPanGestureRecognizer?
/**
A private integer for storing index of current view controller
during interactive transition.
*/
private var currentIndex = -1
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setTabBarClass()
}
/**
An initializer that initializes the object with an Optional nib and bundle.
- Parameter nibNameOrNil: An Optional String for the nib.
- Parameter bundle: An Optional NSBundle where the nib is located.
*/
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setTabBarClass()
}
/// An initializer that accepts no parameters.
public init() {
super.init(nibName: nil, bundle: nil)
setTabBarClass()
}
/**
An initializer that initializes the object an Array of UIViewControllers.
- Parameter viewControllers: An Array of UIViewControllers.
*/
public init(viewControllers: [UIViewController]) {
super.init(nibName: nil, bundle: nil)
setTabBarClass()
self.viewControllers = viewControllers
}
open override func viewDidLoad() {
super.viewDidLoad()
prepare()
}
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
layoutSubviews()
}
/**
To execute in the order of the layout chain, override this
method. `layoutSubviews` should be called immediately, unless you
have a certain need.
*/
open func layoutSubviews() {
tabBar.layoutDivider()
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open func prepare() {
view.clipsToBounds = true
view.backgroundColor = .white
view.contentScaleFactor = Screen.scale
prepareTabBar()
isSwipeEnabled = true
isMotionEnabled = true
applyCurrentTheme()
}
/**
Applies the given theme.
- Parameter theme: A Theme.
*/
open func apply(theme: Theme) {
tabBar.tintColor = theme.secondary
tabBar.barTintColor = theme.background
tabBar.dividerColor = theme.onSurface.withAlphaComponent(0.12)
if #available(iOS 10.0, *) {
tabBar.unselectedItemTintColor = theme.onSurface.withAlphaComponent(0.54)
}
}
}
private extension BottomNavigationController {
/**
A target method contolling interactive swipe transition based on
gesture recognizer.
- Parameter _ gesture: A UIPanGestureRecognizer.
*/
@objc
func handleTransitionPan(_ gesture: UIPanGestureRecognizer) {
guard selectedIndex != NSNotFound else {
return
}
let translationX = gesture.translation(in: nil).x
let velocityX = gesture.velocity(in: nil).x
switch gesture.state {
case .began, .changed:
let isSlidingLeft = currentIndex == -1 ? velocityX < 0 : translationX < 0
if currentIndex == -1 {
currentIndex = selectedIndex
}
let nextIndex = currentIndex + (isSlidingLeft ? 1 : -1)
if selectedIndex != nextIndex {
/// 5 point threshold
guard abs(translationX) > 5 else {
return
}
if currentIndex != selectedIndex {
MotionTransition.shared.cancel(isAnimated: false)
}
guard canSelect(at: nextIndex) else {
return
}
selectedIndex = nextIndex
MotionTransition.shared.setCompletionCallbackForNextTransition { [weak self] isFinishing in
guard let `self` = self, isFinishing else {
return
}
self.delegate?.tabBarController?(self, didSelect: self.viewControllers![nextIndex])
}
} else {
let progress = abs(translationX / view.bounds.width)
MotionTransition.shared.update(Double(progress))
}
default:
let progress = (translationX + velocityX) / view.bounds.width
let isUserHandDirectionLeft = progress < 0
let isTargetHandDirectionLeft = selectedIndex > currentIndex
if isUserHandDirectionLeft == isTargetHandDirectionLeft && abs(progress) > 0.5 {
MotionTransition.shared.finish()
} else {
MotionTransition.shared.cancel()
}
currentIndex = -1
}
}
/// Prepares interactiveSwipeGesture.
func prepareSwipeGesture() {
guard nil == interactiveSwipeGesture else {
return
}
interactiveSwipeGesture = UIPanGestureRecognizer(target: self, action: #selector(handleTransitionPan))
view.addGestureRecognizer(interactiveSwipeGesture!)
}
/// Removes interactiveSwipeGesture.
func removeSwipeGesture() {
guard let v = interactiveSwipeGesture else {
return
}
view.removeGestureRecognizer(v)
interactiveSwipeGesture = nil
}
}
private extension BottomNavigationController {
/// Sets tabBar class to MaterialTabBar.
func setTabBarClass() {
guard object_getClass(tabBar) === UITabBar.self else {
return
}
object_setClass(tabBar, MaterialTabBar.self)
}
}
private extension BottomNavigationController {
/**
Checks if the view controller at a given index can be selected.
- Parameter at index: An Int.
*/
func canSelect(at index: Int) -> Bool {
guard index != selectedIndex else {
return false
}
let lastTabIndex = (tabBar.items?.count ?? 1) - 1
guard (0...lastTabIndex).contains(index) else {
return false
}
guard !(index == lastTabIndex && tabBar.items?.last == moreNavigationController.tabBarItem) else {
return false
}
let vc = viewControllers![index]
guard delegate?.tabBarController?(self, shouldSelect: vc) != false else {
return false
}
return true
}
}
private extension BottomNavigationController {
/// Prepares the tabBar.
func prepareTabBar() {
tabBar.isTranslucent = false
tabBar.heightPreset = .normal
tabBar.dividerColor = Color.grey.lighten2
tabBar.dividerAlignment = .top
let image = UIImage()
tabBar.shadowImage = image
tabBar.backgroundImage = image
tabBar.backgroundColor = .white
}
}
================================================
FILE: Sources/iOS/Button/BaseIconLayerButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
/// Implements common logic for CheckButton and RadioButton
open class BaseIconLayerButton: Button {
class var iconLayer: BaseIconLayer { fatalError("Has to be implemented by subclasses") }
lazy var iconLayer: BaseIconLayer = type(of: self).iconLayer
/// A Boolean value indicating whether the button is in the selected state
///
/// Use `setSelected(_:, animated:)` if the state change needs to be animated
open override var isSelected: Bool {
didSet {
iconLayer.setSelected(isSelected, animated: false)
updatePulseColor()
}
}
/// A Boolean value indicating whether the control is enabled.
open override var isEnabled: Bool {
didSet {
iconLayer.isEnabled = isEnabled
}
}
/// Sets the color of the icon to use for the specified state.
///
/// - Parameters:
/// - color: The color of the icon to use for the specified state.
/// - state: The state that uses the specified color. Supports only (.normal, .selected, .disabled)
open func setIconColor(_ color: UIColor, for state: UIControl.State) {
switch state {
case .normal:
iconLayer.normalColor = color
case .selected:
iconLayer.selectedColor = color
case .disabled:
iconLayer.disabledColor = color
default:
fatalError("unsupported state")
}
}
/// Returns the icon color used for a state.
///
/// - Parameter state: The state that uses the icon color. Supports only (.normal, .selected, .disabled)
/// - Returns: The color of the title for the specified state.
open func iconColor(for state: UIControl.State) -> UIColor {
switch state {
case .normal:
return iconLayer.normalColor
case .selected:
return iconLayer.selectedColor
case .disabled:
return iconLayer.disabledColor
default:
fatalError("unsupported state")
}
}
/// A Boolean value indicating whether the button is being animated
open var isAnimating: Bool { return iconLayer.isAnimating }
/// Sets the `selected` state of the button, optionally animating the transition.
///
/// - Parameters:
/// - isSelected: A Boolean value indicating new `selected` state
/// - animated: true if the state change should be animated, otherwise false.
open func setSelected(_ isSelected: Bool, animated: Bool) {
guard !isAnimating else { return }
iconLayer.setSelected(isSelected, animated: animated)
self.isSelected = isSelected
}
open override func prepare() {
super.prepare()
layer.addSublayer(iconLayer)
iconLayer.prepare()
contentHorizontalAlignment = .left // default was .center
reloadImage()
}
open override func touchesBegan(_ touches: Set, with event: UIEvent?) {
// pulse.animation set to .none so that when we call `super.touchesBegan`
// pulse will not expand as there is a `guard` against .none case
pulse.animation = .none
super.touchesBegan(touches, with: event)
pulse.animation = .point
// expand pulse from the center of iconLayer/visualLayer (`point` is relative to self.view/self.layer)
pulse.expand(point: iconLayer.frame.center)
}
open override func layoutSubviews() {
super.layoutSubviews()
// positioning iconLayer
let insets = iconEdgeInsets
iconLayer.frame.size = CGSize(width: iconSize, height: iconSize)
iconLayer.frame.origin = CGPoint(x: imageView!.frame.minX + insets.left, y: imageView!.frame.minY + insets.top)
// visualLayer is the layer where pulse layer is expanding.
// So we position it at the center of iconLayer, and make it
// small circle, so that the expansion of pulse layer is clipped off
let w = iconSize + insets.left + insets.right
let h = iconSize + insets.top + insets.bottom
let pulseSize = min(w, h)
visualLayer.bounds.size = CGSize(width: pulseSize, height: pulseSize)
visualLayer.frame.center = iconLayer.frame.center
visualLayer.cornerRadius = pulseSize / 2
}
/// Size of the icon
///
/// This property affects `intrinsicContentSize` and `sizeThatFits(_:)`
/// Use `iconEdgeInsets` to set margins.
open var iconSize: CGFloat = 18 {
didSet {
reloadImage()
}
}
/// The *outset* margins for the rectangle around the button’s icon.
///
/// You can specify a different value for each of the four margins (top, left, bottom, right)
/// This property affects `intrinsicContentSize` and `sizeThatFits(_:)` and position of the icon
/// within the rectangle.
///
/// You can use `iconSize` and this property, or `titleEdgeInsets` and `contentEdgeInsets` to position
/// the icon however you want.
/// For negative values, behavior is undefined. Default is `8.0` for all four margins
open var iconEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) {
didSet {
reloadImage()
}
}
open override func apply(theme: Theme) {
super.apply(theme: theme)
setIconColor(theme.secondary, for: .selected)
setIconColor(theme.onSurface.withAlphaComponent(0.38), for: .normal)
titleColor = theme.onSurface.withAlphaComponent(0.60)
selectedPulseColor = theme.secondary
normalPulseColor = theme.onSurface
updatePulseColor()
}
/// This might be considered as a hackish way, but it's just manipulation
/// UIButton considers size of the `currentImage` to determine `intrinsicContentSize`
/// and `sizeThatFits(_:)`, and to position `titleLabel`.
/// So, we make use of this property (by setting transparent image) to make room for our icon
/// without making much effort (like playing with `titleEdgeInsets` and `contentEdgeInsets`)
/// Size of the image equals to `iconSize` plus corresponsing `iconEdgeInsets` values
private func reloadImage() {
let insets = iconEdgeInsets
let w = iconSize + insets.left + insets.right
let h = iconSize + insets.top + insets.bottom
UIGraphicsBeginImageContext(CGSize(width: w, height: h))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.image = image
}
/// Pulse color for selected state.
open var selectedPulseColor = Color.white
/// Pulse color for normal state.
open var normalPulseColor = Color.white
}
private extension BaseIconLayerButton {
func updatePulseColor() {
pulseColor = isSelected ? selectedPulseColor : normalPulseColor
}
}
// MARK: - BaseIconLayer
internal class BaseIconLayer: CALayer {
var selectedColor = Color.blue.base
var normalColor = Color.lightGray
var disabledColor = Color.gray
func prepareForFirstAnimation() {}
func firstAnimation() {}
func prepareForSecondAnimation() {}
func secondAnimation() {}
private(set) var isAnimating = false
private(set) var isSelected = false
var isEnabled = true {
didSet {
selectedColor = { selectedColor }()
normalColor = { normalColor }()
disabledColor = { disabledColor }()
}
}
func prepare() {
normalColor = { normalColor }() // calling didSet
selectedColor = { selectedColor }() // calling didSet
}
func setSelected(_ isSelected: Bool, animated: Bool) {
guard self.isSelected != isSelected, !isAnimating else { return }
self.isSelected = isSelected
if animated {
animate()
} else {
Motion.disable {
prepareForFirstAnimation()
firstAnimation()
prepareForSecondAnimation()
secondAnimation()
}
}
}
private func animate() {
guard !isAnimating else { return }
prepareForFirstAnimation()
Motion.animate(duration: Constants.partialDuration, timingFunction: .easeInOut, animations: {
self.isAnimating = true
self.firstAnimation()
}, completion: {
Motion.disable {
self.prepareForSecondAnimation()
}
Motion.delay(Constants.partialDuration * Constants.delayFactor) {
Motion.animate(duration: Constants.partialDuration, timingFunction: .easeInOut, animations: {
self.secondAnimation()
}, completion: { self.isAnimating = false })
}
})
}
var sideLength: CGFloat { return frame.height }
struct Constants {
static let totalDuration = 0.5
static let delayFactor = 0.33
static let partialDuration = totalDuration / (1.0 + delayFactor + 1.0)
}
}
// MARK: - Helper extension
private extension CGRect {
var center: CGPoint {
get {
return CGPoint(x: minX + width / 2 , y: minY + height / 2)
}
set {
origin = CGPoint(x: newValue.x - width / 2, y: newValue.y - height / 2)
}
}
}
internal extension CALayer {
/// Animates the propery of CALayer from current value to the specified value
/// and does not reset to the initial value after the animation finishes
///
/// - Parameters:
/// - keyPath: Keypath to the animatable property of the layer
/// - to: Final value of the property
/// - dur: Duration of the animation in seconds. Defaults to 0, which results in taking the duration from enclosing CATransaction, or .25 seconds
func animate(_ keyPath: String, to: CGFloat, dur: TimeInterval = 0) {
let animation = CABasicAnimation(keyPath: keyPath)
animation.timingFunction = .easeIn
animation.fromValue = self.value(forKeyPath: keyPath) // from current value
animation.duration = dur
setValue(to, forKeyPath: keyPath)
self.add(animation, forKey: nil)
}
}
internal extension CATransform3D {
static var identity: CATransform3D {
return CATransform3DIdentity
}
}
================================================
FILE: Sources/iOS/Button/Button.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
open class Button: UIButton, Pulseable, PulseableLayer, Themeable {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
allows the dropshadow effect on the backing layer, while clipping
the image to a desired shape within the visualLayer.
*/
public let visualLayer = CAShapeLayer()
/// A Pulse reference.
internal var pulse: Pulse!
/// A reference to the pulse layer.
internal var pulseLayer: CALayer? {
return pulse.pulseLayer
}
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
set(value) {
pulse.animation = value
}
}
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
return pulse.color
}
set(value) {
pulse.color = value
}
}
/// Pulse opacity.
@IBInspectable
open var pulseOpacity: CGFloat {
get {
return pulse.opacity
}
set(value) {
pulse.opacity = value
}
}
/// A property that accesses the backing layer's background
@IBInspectable
open override var backgroundColor: UIColor? {
didSet {
layer.backgroundColor = backgroundColor?.cgColor
}
}
/// A preset property for updated contentEdgeInsets.
open var contentEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
contentEdgeInsets = EdgeInsetsPresetToValue(preset: contentEdgeInsetsPreset)
}
}
/// Sets the normal and highlighted image for the button.
@IBInspectable
open var image: UIImage? {
didSet {
setImage(image, for: .normal)
setImage(image, for: .selected)
setImage(image, for: .highlighted)
setImage(image, for: .disabled)
if #available(iOS 9, *) {
setImage(image, for: .application)
setImage(image, for: .focused)
setImage(image, for: .reserved)
}
}
}
/// Sets the normal and highlighted title for the button.
@IBInspectable
open var title: String? {
didSet {
setTitle(title, for: .normal)
setTitle(title, for: .selected)
setTitle(title, for: .highlighted)
setTitle(title, for: .disabled)
if #available(iOS 9, *) {
setTitle(title, for: .application)
setTitle(title, for: .focused)
setTitle(title, for: .reserved)
}
guard nil != title else {
return
}
guard nil == titleColor else {
return
}
titleColor = Color.blue.base
}
}
/// Sets the normal and highlighted titleColor for the button.
@IBInspectable
open var titleColor: UIColor? {
didSet {
setTitleColor(titleColor, for: .normal)
setTitleColor(titleColor, for: .highlighted)
setTitleColor(titleColor, for: .disabled)
if nil == selectedTitleColor {
setTitleColor(titleColor, for: .selected)
}
if #available(iOS 9, *) {
setTitleColor(titleColor, for: .application)
setTitleColor(titleColor, for: .focused)
setTitleColor(titleColor, for: .reserved)
}
}
}
/// Sets the selected titleColor for the button.
@IBInspectable
open var selectedTitleColor: UIColor? {
didSet {
setTitleColor(selectedTitleColor, for: .selected)
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/**
An initializer that initializes the object with a CGRect object.
If AutoLayout is used, it is better to initilize the instance
using the init() initializer.
- Parameter frame: A CGRect instance.
*/
public override init(frame: CGRect) {
super.init(frame: frame)
/// Set these here to avoid overriding storyboard values
tintColor = Color.blue.base
titleLabel?.font = Theme.font.regular(with: fontSize)
prepare()
}
/**
A convenience initializer that acceps an image and tint
- Parameter image: A UIImage.
- Parameter tintColor: A UIColor.
*/
public init(image: UIImage?, tintColor: UIColor? = nil) {
super.init(frame: .zero)
prepare()
prepare(with: image, tintColor: tintColor)
}
/**
A convenience initializer that acceps a title and title
- Parameter title: A String.
- Parameter titleColor: A UIColor.
*/
public init(title: String?, titleColor: UIColor? = nil) {
super.init(frame: .zero)
prepare()
prepare(with: title, titleColor: titleColor)
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutShape()
layoutVisualLayer()
layoutShadowPath()
}
/**
Triggers the pulse animation.
- Parameter point: A Optional point to pulse from, otherwise pulses
from the center.
*/
open func pulse(point: CGPoint? = nil) {
pulse.expand(point: point ?? center)
Motion.delay(0.35) { [weak self] in
self?.pulse.contract()
}
}
/**
A delegation method that is executed when the view has began a
touch event.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesBegan(_ touches: Set, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
pulse.expand(point: layer.convert(touches.first!.location(in: self), from: layer))
}
/**
A delegation method that is executed when the view touch event has
ended.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesEnded(_ touches: Set, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
pulse.contract()
}
/**
A delegation method that is executed when the view touch event has
been cancelled.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
pulse.contract()
}
open func bringImageViewToFront() {
guard let v = imageView else {
return
}
bringSubviewToFront(v)
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open func prepare() {
contentScaleFactor = Screen.scale
prepareVisualLayer()
preparePulse()
applyCurrentTheme()
}
/**
Applies the given theme.
- Parameter theme: A Theme.
*/
open func apply(theme: Theme) { }
}
extension Button {
/// Prepares the visualLayer property.
fileprivate func prepareVisualLayer() {
visualLayer.zPosition = 0
visualLayer.masksToBounds = true
layer.addSublayer(visualLayer)
}
/// Prepares the pulse motion.
fileprivate func preparePulse() {
pulse = Pulse(pulseView: self, pulseLayer: visualLayer)
}
/**
Prepares the Button with an image and tint
- Parameter image: A UIImage.
- Parameter tintColor: A UI
*/
fileprivate func prepare(with image: UIImage?, tintColor: UIColor?) {
self.image = image
self.tintColor = tintColor ?? self.tintColor
}
/**
Prepares the Button with a title and title
- Parameter title: A String.
- Parameter titleColor: A UI
*/
fileprivate func prepare(with title: String?, titleColor: UIColor?) {
self.title = title
self.titleColor = titleColor ?? self.titleColor
}
}
extension Button {
/// Manages the layout for the visualLayer property.
fileprivate func layoutVisualLayer() {
visualLayer.frame = bounds
visualLayer.cornerRadius = layer.cornerRadius
}
}
================================================
FILE: Sources/iOS/Button/CheckButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class CheckButton: BaseIconLayerButton {
class override var iconLayer: BaseIconLayer { return CheckBoxLayer() }
/// Color of the checkmark (✓)
open var checkmarkColor: UIColor {
get {
return (iconLayer as! CheckBoxLayer).checkmarkColor
}
set {
(iconLayer as! CheckBoxLayer).checkmarkColor = newValue
}
}
open override func prepare() {
super.prepare()
addTarget(self, action: #selector(didTap), for: .touchUpInside)
}
@objc
private func didTap() {
guard !isAnimating else { return }
setSelected(!isSelected, animated: true)
}
open override func apply(theme: Theme) {
super.apply(theme: theme)
checkmarkColor = theme.onSecondary
}
}
internal class CheckBoxLayer: BaseIconLayer {
var checkmarkColor: UIColor = .white {
didSet {
checkMarkLeftLayer.strokeColor = checkmarkColor.cgColor
checkMarkRightLayer.strokeColor = checkmarkColor.cgColor
}
}
let borderLayer = CALayer()
let checkMarkLeftLayer = CAShapeLayer()
let checkMarkRightLayer = CAShapeLayer()
let checkMarkLayer = CALayer()
override var selectedColor: UIColor {
didSet {
guard isSelected, isEnabled else { return }
borderLayer.borderColor = selectedColor.cgColor
borderLayer.backgroundColor = selectedColor.cgColor
}
}
override var normalColor: UIColor {
didSet {
guard !isSelected, isEnabled else { return }
borderLayer.borderColor = normalColor.cgColor
}
}
override var disabledColor: UIColor {
didSet {
guard !isEnabled else { return }
borderLayer.borderColor = disabledColor.cgColor
if isSelected { borderLayer.backgroundColor = disabledColor.cgColor }
}
}
open override func prepare() {
super.prepare()
addSublayer(borderLayer)
addSublayer(checkMarkLayer)
checkMarkLayer.addSublayer(checkMarkLeftLayer)
checkMarkLayer.addSublayer(checkMarkRightLayer)
checkMarkLeftLayer.lineCap = CAShapeLayerLineCap.square
checkMarkRightLayer.lineCap = CAShapeLayerLineCap.square
checkMarkLeftLayer.strokeEnd = 0
checkMarkRightLayer.strokeEnd = 0
checkmarkColor = { checkmarkColor }() // calling didSet
}
override func prepareForFirstAnimation() {
borderLayer.borderColor = (isEnabled ? (isSelected ? selectedColor : normalColor) : disabledColor).cgColor
if isSelected {
borderLayer.borderWidth = borderLayerNormalBorderWidth
} else {
borderLayer.borderWidth = 0
borderLayer.backgroundColor = (isEnabled ? normalColor : disabledColor).cgColor
checkMarkLeftLayer.strokeEnd = 1
checkMarkRightLayer.strokeEnd = 1
}
checkMarkLayer.transform = .identity
}
override func firstAnimation() {
borderLayer.transform = borderLayerScaleToShrink
checkMarkLayer.transform = borderLayerScaleToShrink
if isSelected {
borderLayer.animate(#keyPath(CALayer.borderWidth), to: borderLayerFullBorderWidth)
} else {
checkMarkLeftLayer.animate(#keyPath(CAShapeLayer.strokeEnd), to: 0)
checkMarkRightLayer.animate(#keyPath(CAShapeLayer.strokeEnd), to: 0)
checkMarkLayer.transform = CATransform3DMakeTranslation(sideLength / 2 - checkMarkStartPoint.x, -(checkMarkStartPoint.y - sideLength / 2), 0)
}
}
override func prepareForSecondAnimation() {
borderLayer.backgroundColor = (isSelected ? (isEnabled ? selectedColor : disabledColor) : .clear).cgColor
if isSelected {
borderLayer.borderWidth = borderLayerNormalBorderWidth
checkMarkLeftLayer.strokeEnd = 0.0001
checkMarkRightLayer.strokeEnd = 0.0001
checkMarkLayer.opacity = 0
checkMarkLayer.animate(#keyPath(CALayer.opacity), to: 1, dur: Constants.totalDuration * 0.1)
} else {
borderLayer.borderWidth = borderLayerCenterDotBorderWidth
}
}
override func secondAnimation() {
borderLayer.transform = .identity
checkMarkLayer.transform = .identity
if isSelected {
checkMarkLeftLayer.animate(#keyPath(CAShapeLayer.strokeEnd), to: 1)
checkMarkRightLayer.animate(#keyPath(CAShapeLayer.strokeEnd), to: 1)
} else {
borderLayer.animate(#keyPath(CALayer.borderWidth), to: borderLayerNormalBorderWidth)
}
}
override func layoutSublayers() {
super.layoutSublayers()
guard !isAnimating else { return }
let s = CGSize(width: sideLength, height: sideLength)
borderLayer.frame.size = s
checkMarkLayer.frame.size = s
checkMarkLeftLayer.frame.size = s
checkMarkRightLayer.frame.size = s
checkMarkLeftLayer.path = checkMarkPathLeft.cgPath
checkMarkRightLayer.path = checkMarkPathRigth.cgPath
checkMarkLeftLayer.lineWidth = lineWidth
checkMarkRightLayer.lineWidth = lineWidth
borderLayer.borderWidth = borderLayerNormalBorderWidth
borderLayer.cornerRadius = borderLayerCornerRadius
}
}
private extension CheckBoxLayer {
var borderLayerFullBorderWidth: CGFloat { return sideLength / 2 * 1.1 } //without multipling 1.1 a weird plus sign (+) appears sometimes.
var borderLayerCenterDotBorderWidth: CGFloat { return sideLength / 2 * 0.87 }
var borderLayerNormalBorderWidth: CGFloat { return sideLength * 0.1 }
var borderLayerCornerRadius: CGFloat { return sideLength * 0.1 }
var borderLayerScalePercentageToShrink: CGFloat { return 0.9 }
var borderLayerScaleToShrink: CATransform3D {
return CATransform3DMakeScale(borderLayerScalePercentageToShrink, borderLayerScalePercentageToShrink, 1)
}
var checkMarkStartPoint: CGPoint {
return CGPoint(x: sideLength * 14 / 36, y: sideLength * 25 / 36)
}
var checkMarkRightEndPoint: CGPoint {
return CGPoint(x: sideLength - (sideLength * 6 / 36), y: sideLength * 9 / 36)
}
var checkMarkLeftEndPoint: CGPoint {
return CGPoint(x: sideLength * 6 / 36, y: sideLength * 18 / 36)
}
var checkMarkPathRigth: UIBezierPath {
let path = UIBezierPath()
path.move(to: checkMarkStartPoint)
path.addLine(to: checkMarkRightEndPoint)
return path
}
var checkMarkPathLeft: UIBezierPath {
let path = UIBezierPath()
path.move(to: checkMarkStartPoint)
path.addLine(to: checkMarkLeftEndPoint)
return path
}
var lineWidth: CGFloat { return sideLength * 0.1 }
}
================================================
FILE: Sources/iOS/Button/FABButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class FABButton: Button {
open override func prepare() {
super.prepare()
depthPreset = .depth1
shapePreset = .circle
pulseAnimation = .centerWithBacking
}
open override func apply(theme: Theme) {
super.apply(theme: theme)
backgroundColor = theme.secondary
titleColor = theme.onSecondary
tintColor = theme.onSecondary
pulseColor = theme.onSecondary
}
}
================================================
FILE: Sources/iOS/Button/FlatButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class FlatButton: Button {
open override func prepare() {
super.prepare()
cornerRadiusPreset = .cornerRadius1
}
open override func apply(theme: Theme) {
super.apply(theme: theme)
backgroundColor = .clear
titleColor = theme.secondary
tintColor = theme.secondary
pulseColor = theme.secondary
}
}
================================================
FILE: Sources/iOS/Button/IconButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public enum IconButtonThemingStyle {
/// Theming when background content is in background color.
case onBackground
/// Theming when background content is in primary color.
case onPrimary
}
open class IconButton: Button {
/// A reference to IconButtonThemingStyle.
open var themingStyle = IconButtonThemingStyle.onBackground
open override func prepare() {
super.prepare()
pulseAnimation = .center
}
open override func apply(theme: Theme) {
super.apply(theme: theme)
switch themingStyle {
case .onBackground:
tintColor = theme.secondary
pulseColor = theme.secondary
case .onPrimary:
tintColor = theme.onPrimary
pulseColor = theme.onPrimary
}
}
}
================================================
FILE: Sources/iOS/Button/RadioButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class RadioButton: BaseIconLayerButton {
class override var iconLayer: BaseIconLayer { return RadioBoxLayer() }
open override func prepare() {
super.prepare()
addTarget(self, action: #selector(didTap), for: .touchUpInside)
}
@objc
private func didTap() {
setSelected(true, animated: true)
}
}
internal class RadioBoxLayer: BaseIconLayer {
private let centerDot = CALayer()
private let outerCircle = CALayer()
override var selectedColor: UIColor {
didSet {
guard isSelected, isEnabled else { return }
outerCircle.borderColor = selectedColor.cgColor
centerDot.backgroundColor = selectedColor.cgColor
}
}
override var normalColor: UIColor {
didSet {
guard !isSelected, isEnabled else { return }
outerCircle.borderColor = normalColor.cgColor
}
}
override var disabledColor: UIColor {
didSet {
guard !isEnabled else { return }
outerCircle.borderColor = disabledColor.cgColor
if isSelected { centerDot.backgroundColor = disabledColor.cgColor }
}
}
override func prepare() {
super.prepare()
addSublayer(centerDot)
addSublayer(outerCircle)
}
override func prepareForFirstAnimation() {
outerCircle.borderColor = (isEnabled ? (isSelected ? selectedColor : normalColor) : disabledColor).cgColor
if !isSelected {
centerDot.backgroundColor = (isEnabled ? normalColor : disabledColor).cgColor
}
outerCircle.borderWidth = outerCircleBorderWidth
}
override func firstAnimation() {
outerCircle.transform = outerCircleScaleToShrink
let to = isSelected ? sideLength / 2.0 : outerCircleBorderWidth * percentageOfOuterCircleWidthToStart
outerCircle.animate(#keyPath(CALayer.borderWidth), to: to)
if !isSelected {
centerDot.transform = centerDotScaleForMeeting
}
}
override func prepareForSecondAnimation() {
centerDot.transform = isSelected ? centerDotScaleForMeeting : .identity
centerDot.backgroundColor = (isSelected ? (isEnabled ? selectedColor : disabledColor) : .clear).cgColor
outerCircle.borderWidth = isSelected ? outerCircleBorderWidth * percentageOfOuterCircleWidthToStart : outerCircleFullBorderWidth
}
override func secondAnimation() {
outerCircle.transform = .identity
outerCircle.animate(#keyPath(CALayer.borderWidth), to: outerCircleBorderWidth)
if isSelected {
centerDot.transform = .identity
}
}
override func layoutSublayers() {
super.layoutSublayers()
guard !isAnimating else { return }
centerDot.frame = CGRect(x: centerDotDiameter / 2.0, y: centerDotDiameter / 2.0, width: centerDotDiameter, height: centerDotDiameter)
outerCircle.frame.size = CGSize(width: sideLength, height: sideLength)
centerDot.cornerRadius = centerDot.bounds.width / 2
outerCircle.cornerRadius = sideLength / 2
outerCircle.borderWidth = outerCircleBorderWidth
}
}
private extension RadioBoxLayer {
var percentageOfOuterCircleSizeToShrinkTo: CGFloat { return 0.9 }
var percentageOfOuterCircleWidthToStart: CGFloat { return 1 }
var outerCircleScaleToShrink: CATransform3D {
let s = percentageOfOuterCircleSizeToShrinkTo
return CATransform3DMakeScale(s, s, 1)
}
var centerDotScaleForMeeting: CATransform3D {
let s = ((sideLength - 2 * percentageOfOuterCircleWidthToStart * outerCircleBorderWidth) * percentageOfOuterCircleSizeToShrinkTo) / centerDotDiameter
return CATransform3DMakeScale(s, s, 1)
}
var outerCircleFullBorderWidth: CGFloat {
return (self.sideLength / 2.0) * 1.1 //without multipling 1.1 a weird plus sign (+) appears sometimes.
}
var centerDotDiameter: CGFloat { return sideLength / 2.0 }
var outerCircleBorderWidth: CGFloat { return sideLength * 0.11 }
}
================================================
FILE: Sources/iOS/Button/RaisedButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class RaisedButton: Button {
open override func prepare() {
super.prepare()
depthPreset = .depth1
cornerRadiusPreset = .cornerRadius1
}
open override func apply(theme: Theme) {
super.apply(theme: theme)
backgroundColor = theme.secondary
titleColor = theme.onSecondary
pulseColor = theme.onSecondary
tintColor = theme.onSecondary
}
}
================================================
FILE: Sources/iOS/ButtonGroup/BaseButtonGroup.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class BaseButtonGroup: View {
/// Holds reference to buttons within the group.
open var buttons: [T] = [] {
didSet {
oldValue.forEach {
$0.removeFromSuperview()
$0.removeTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
}
prepareButtons()
grid.views = buttons
grid.axis.rows = buttons.count
}
}
/// Initializes group with the provided buttons.
///
/// - Parameter buttons: Array of buttons.
public convenience init(buttons: [T]) {
self.init(frame: .zero)
defer { self.buttons = buttons } // defer allows didSet to be called
}
open override func prepare() {
super.prepare()
grid.axis.direction = .vertical
grid.axis.columns = 1
}
open override var intrinsicContentSize: CGSize { return sizeThatFits(bounds.size) }
open override func sizeThatFits(_ size: CGSize) -> CGSize {
let size = CGSize(width: size.width == 0 ? .greatestFiniteMagnitude : size.width, height: size.height == 0 ? .greatestFiniteMagnitude : size.height)
let availableW = size.width - grid.contentEdgeInsets.left - grid.contentEdgeInsets.right - grid.layoutEdgeInsets.left - grid.layoutEdgeInsets.right
let maxW = buttons.reduce(0) { max($0, $1.sizeThatFits(.init(width: availableW, height: .greatestFiniteMagnitude)).width) }
let h = buttons.reduce(0) { $0 + $1.sizeThatFits(.init(width: maxW, height: .greatestFiniteMagnitude)).height }
+ grid.contentEdgeInsets.top + grid.contentEdgeInsets.bottom
+ grid.layoutEdgeInsets.top + grid.layoutEdgeInsets.bottom
+ CGFloat(buttons.count - 1) * grid.interimSpace
return CGSize(width: maxW + grid.contentEdgeInsets.left + grid.contentEdgeInsets.right + grid.layoutEdgeInsets.left + grid.layoutEdgeInsets.right, height: min(h, size.height))
}
open override func layoutSubviews() {
super.layoutSubviews()
grid.reload()
}
open func didTap(button: T, at index: Int) { }
@objc
private func didTap(_ sender: Button) {
guard let sender = sender as? T,
let index = buttons.firstIndex(of: sender)
else { return }
didTap(button: sender, at: index)
}
}
private extension BaseButtonGroup {
func prepareButtons() {
buttons.forEach {
addSubview($0)
$0.removeTarget(nil, action: nil, for: .allEvents)
$0.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
}
}
}
================================================
FILE: Sources/iOS/ButtonGroup/CheckButtonGroup.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/// Lays out provided check buttons within itself.
///
/// Unlike RadioButtonGroup, checking one check button that belongs to a check group *does not* unchecks any previously checked
/// check button within the same group. Intially, all of the check buttons are unchecked.
///
/// The buttons are laid out by `Grid` system, so that changing properites of grid instance
/// (e.g interimSpace) are reflected.
open class CheckButtonGroup: BaseButtonGroup {
/// Initializes CheckButtonGroup with an array of check buttons each having
/// title equal to corresponding string in the `titles` parameter.
///
/// - Parameter titles: An array of title strings
public convenience init(titles: [String]) {
let buttons = titles.map { CheckButton(title: $0) }
self.init(buttons: buttons)
}
/// Returns all selected check buttons within the group
/// or empty array if none is seleceted.
open var selecetedButtons: [CheckButton] {
return buttons.filter { $0.isSelected }
}
/// Returns indexes of all selected check buttons within the group
/// or empty array if none is seleceted.
open var selectedIndices: [Int] {
return selecetedButtons.map { buttons.firstIndex(of: $0)! }
}
open override func didTap(button: CheckButton, at index: Int) {
button.setSelected(!button.isSelected, animated: true)
}
}
================================================
FILE: Sources/iOS/ButtonGroup/RadioButtonGroup.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/// Lays out provided radio buttons within itself.
///
/// Checking one radio button that belongs to a radio group unchecks any previously checked
/// radio button within the same group. Intially, all of the radio buttons are unchecked.
///
/// The buttons are laid out by `Grid` system, so that changing properites of grid instance
/// (e.g interimSpace) are reflected.
open class RadioButtonGroup: BaseButtonGroup {
/// Initializes RadioButtonGroup with an array of radio buttons each having
/// title equal to corresponding string in the `titles` parameter.
///
/// - Parameter titles: An array of title strings.
public convenience init(titles: [String]) {
let buttons = titles.map { RadioButton(title: $0) }
self.init(buttons: buttons)
}
/// Returns selected radio button within the group.
/// If none is selected (e.g in initial state), nil is returned.
open var selectedButton: RadioButton? {
return buttons.first { $0.isSelected }
}
/// Returns index of selected radio button within the group.
/// If none is selected (e.g in initial state), -1 is returned.
open var selectedIndex: Int {
guard let b = selectedButton else { return -1 }
return buttons.firstIndex(of: b)!
}
open override func didTap(button: RadioButton, at index: Int) {
let isAnimating = buttons.reduce(false) { $0 || $1.isAnimating }
guard !isAnimating else { return }
buttons.forEach { $0.setSelected($0 == button, animated: true) }
}
}
================================================
FILE: Sources/iOS/Card/Card.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class Card: PulseView {
/// A container view for subviews.
public let container = UIView()
@IBInspectable
open override var cornerRadiusPreset: CornerRadiusPreset {
didSet {
container.cornerRadiusPreset = cornerRadiusPreset
}
}
@IBInspectable
open var cornerRadius: CGFloat {
get {
return container.layer.cornerRadius
}
set(value) {
container.layer.cornerRadius = value
}
}
open override var shapePreset: ShapePreset {
didSet {
container.shapePreset = shapePreset
}
}
@IBInspectable
open override var backgroundColor: UIColor? {
didSet {
container.backgroundColor = backgroundColor
}
}
/// A reference to the toolbar.
@IBInspectable
open var toolbar: Toolbar? {
didSet {
oldValue?.removeFromSuperview()
if let v = toolbar {
container.addSubview(v)
}
layoutSubviews()
}
}
/// A preset wrapper around toolbarEdgeInsets.
open var toolbarEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
toolbarEdgeInsets = EdgeInsetsPresetToValue(preset: toolbarEdgeInsetsPreset)
}
}
/// A reference to toolbarEdgeInsets.
@IBInspectable
open var toolbarEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// A reference to the contentView.
@IBInspectable
open var contentView: UIView? {
didSet {
oldValue?.removeFromSuperview()
if let v = contentView {
v.clipsToBounds = true
container.addSubview(v)
}
layoutSubviews()
}
}
/// A preset wrapper around contentViewEdgeInsets.
open var contentViewEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
contentViewEdgeInsets = EdgeInsetsPresetToValue(preset: contentViewEdgeInsetsPreset)
}
}
/// A reference to contentViewEdgeInsets.
@IBInspectable
open var contentViewEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// A reference to the bottomBar.
@IBInspectable
open var bottomBar: Bar? {
didSet {
oldValue?.removeFromSuperview()
if let v = bottomBar {
container.addSubview(v)
}
layoutSubviews()
}
}
/// A preset wrapper around bottomBarEdgeInsets.
open var bottomBarEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
bottomBarEdgeInsets = EdgeInsetsPresetToValue(preset: bottomBarEdgeInsetsPreset)
}
}
/// A reference to bottomBarEdgeInsets.
@IBInspectable
open var bottomBarEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/**
An initializer that accepts a NSCoder.
- Parameter coder aDecoder: A NSCoder.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
/**
An initializer that accepts a CGRect.
- Parameter frame: A CGRect.
*/
public override init(frame: CGRect) {
super.init(frame: frame)
}
/**
A convenience initiazlier.
- Parameter toolbar: An optional Toolbar.
- Parameter contentView: An optional UIView.
- Parameter bottomBar: An optional Bar.
*/
public convenience init?(toolbar: Toolbar?, contentView: UIView?, bottomBar: Bar?) {
self.init(frame: .zero)
prepareProperties(toolbar: toolbar, contentView: contentView, bottomBar: bottomBar)
}
open override func layoutSubviews() {
super.layoutSubviews()
container.frame.size.width = bounds.width
reload()
}
/// Reloads the layout.
open func reload() {
var h: CGFloat = 0
if let v = toolbar {
h = prepare(view: v, with: toolbarEdgeInsets, from: h)
}
if let v = contentView {
h = prepare(view: v, with: contentViewEdgeInsets, from: h)
}
if let v = bottomBar {
h = prepare(view: v, with: bottomBarEdgeInsets, from: h)
}
container.frame.size.height = h
bounds.size.height = h
}
open override func prepare() {
super.prepare()
pulseAnimation = .none
cornerRadiusPreset = .cornerRadius1
prepareContainer()
}
/**
Prepare the view size from a given top position.
- Parameter view: A UIView.
- Parameter edge insets: An EdgeInsets.
- Parameter from top: A CGFloat.
- Returns: A CGFloat.
*/
@discardableResult
open func prepare(view: UIView, with insets: EdgeInsets, from top: CGFloat) -> CGFloat {
let y = insets.top + top
view.frame.origin.y = y
view.frame.origin.x = insets.left
let w = container.bounds.width - insets.left - insets.right
var h = view.bounds.height
if 0 == h || nil != view as? UILabel {
(view as? UILabel)?.sizeToFit()
h = view.sizeThatFits(CGSize(width: w, height: .greatestFiniteMagnitude)).height
}
view.frame.size.width = w
view.frame.size.height = h
return y + h + insets.bottom
}
/**
A preparation method that sets the base UI elements.
- Parameter toolbar: An optional Toolbar.
- Parameter contentView: An optional UIView.
- Parameter bottomBar: An optional Bar.
*/
internal func prepareProperties(toolbar: Toolbar?, contentView: UIView?, bottomBar: Bar?) {
self.toolbar = toolbar
self.contentView = contentView
self.bottomBar = bottomBar
}
}
extension Card {
/// Prepares the container.
fileprivate func prepareContainer() {
container.clipsToBounds = true
addSubview(container)
}
}
================================================
FILE: Sources/iOS/Card/ImageCard.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class ImageCard: Card {
/**
A Display value to indicate whether or not to
display the imageView to the full view
bounds.
*/
open var displayStyle = DisplayStyle.partial {
didSet {
layoutSubviews()
}
}
/// A preset wrapper around imageViewEdgeInsets.
open var imageViewEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
imageViewEdgeInsets = EdgeInsetsPresetToValue(preset: imageViewEdgeInsetsPreset)
}
}
/// A reference to imageViewEdgeInsets.
@IBInspectable
open var imageViewEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// A reference to the imageView.
@IBInspectable
open var imageView: UIImageView? {
didSet {
oldValue?.removeFromSuperview()
if let v = imageView {
container.addSubview(v)
}
layoutSubviews()
}
}
/// An ImageCardToolbarAlignment value.
open var toolbarAlignment = ToolbarAlignment.bottom {
didSet {
layoutSubviews()
}
}
/// Reloads the view.
open override func reload() {
var h: CGFloat = 0
if let v = imageView {
h = prepare(view: v, with: imageViewEdgeInsets, from: h)
container.sendSubviewToBack(v)
}
if let v = toolbar {
prepare(view: v, with: toolbarEdgeInsets, from: h)
v.frame.origin.y = .top == toolbarAlignment ? toolbarEdgeInsets.top : h - v.bounds.height - toolbarEdgeInsets.bottom
container.bringSubviewToFront(v)
}
if let v = contentView {
h = prepare(view: v, with: contentViewEdgeInsets, from: h)
}
if let v = bottomBar {
h = prepare(view: v, with: bottomBarEdgeInsets, from: h)
}
container.frame.size.height = h
bounds.size.height = h
}
}
================================================
FILE: Sources/iOS/Card/PresenterCard.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class PresenterCard: Card {
/// A preset wrapper around presenterViewEdgeInsets.
open var presenterViewEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
presenterViewEdgeInsets = EdgeInsetsPresetToValue(preset: presenterViewEdgeInsetsPreset)
}
}
/// A reference to presenterViewEdgeInsets.
@IBInspectable
open var presenterViewEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// A reference to the presenterView.
@IBInspectable
open var presenterView: UIView? {
didSet {
oldValue?.removeFromSuperview()
if let v = presenterView {
v.clipsToBounds = true
container.addSubview(v)
}
layoutSubviews()
}
}
open override func reload() {
var h: CGFloat = 0
if let v = toolbar {
h = prepare(view: v, with: toolbarEdgeInsets, from: h)
}
if let v = presenterView {
h = prepare(view: v, with: presenterViewEdgeInsets, from: h)
}
if let v = contentView {
h = prepare(view: v, with: contentViewEdgeInsets, from: h)
}
if let v = bottomBar {
h = prepare(view: v, with: bottomBarEdgeInsets, from: h)
}
container.frame.size.height = h
bounds.size.height = h
}
}
================================================
FILE: Sources/iOS/Chip/ChipBar.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc(ChipItemStyle)
public enum ChipItemStyle: Int {
case pill
}
open class ChipItem: FlatButton {
/// Configures the visual display of the chip.
var chipItemStyle: ChipItemStyle {
get {
return associatedInstance.chipItemStyle
}
set(value) {
associatedInstance.chipItemStyle = value
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutChipItemStyle()
}
open override func prepare() {
super.prepare()
pulseAnimation = .none
}
}
fileprivate extension ChipItem {
/// Lays out the chipItem based on its style.
func layoutChipItemStyle() {
if .pill == chipItemStyle {
layer.cornerRadius = bounds.height / 2
}
}
}
fileprivate struct AssociatedInstance {
/// A ChipItemStyle value.
var chipItemStyle: ChipItemStyle
}
/// A memory reference to the ChipItemStyle instance for ChipItem extensions.
fileprivate var ChipKey: UInt8 = 0
fileprivate extension ChipItem {
/// AssociatedInstance reference.
var associatedInstance: AssociatedInstance {
get {
return AssociatedObject.get(base: self, key: &ChipKey) {
return AssociatedInstance(chipItemStyle: .pill)
}
}
set(value) {
AssociatedObject.set(base: self, key: &ChipKey, value: value)
}
}
}
@objc(ChipBarDelegate)
public protocol ChipBarDelegate {
/**
A delegation method that is executed when the chipItem will trigger the
animation to the next chip.
- Parameter chipBar: A ChipBar.
- Parameter chipItem: A ChipItem.
*/
@objc
optional func chipBar(chipBar: ChipBar, willSelect chipItem: ChipItem)
/**
A delegation method that is executed when the chipItem did complete the
animation to the next chip.
- Parameter chipBar: A ChipBar.
- Parameter chipItem: A ChipItem.
*/
@objc
optional func chipBar(chipBar: ChipBar, didSelect chipItem: ChipItem)
}
@objc(ChipBarStyle)
public enum ChipBarStyle: Int {
case auto
case nonScrollable
case scrollable
}
open class ChipBar: Bar {
/// The total width of the chipItems.
fileprivate var chipItemsTotalWidth: CGFloat {
var w: CGFloat = 0
let q = 2 * chipItemsInterimSpace
let p = q + chipItemsInterimSpace
for v in chipItems {
let x = v.sizeThatFits(CGSize(width: .greatestFiniteMagnitude, height: scrollView.bounds.height)).width
w += x
w += p
}
w -= chipItemsInterimSpace
return w
}
/// An enum that determines the chip bar style.
open var chipBarStyle = ChipBarStyle.auto {
didSet {
layoutSubviews()
}
}
/// A reference to the scroll view when the chip bar style is scrollable.
public let scrollView = UIScrollView()
/// Enables and disables bouncing when swiping.
open var isScrollBounceEnabled: Bool {
get {
return scrollView.bounces
}
set(value) {
scrollView.bounces = value
}
}
/// A delegation reference.
open weak var delegate: ChipBarDelegate?
/// The currently selected chipItem.
open fileprivate(set) var selectedChipItem: ChipItem?
/// A preset wrapper around chipItems contentEdgeInsets.
open var chipItemsContentEdgeInsetsPreset: EdgeInsetsPreset {
get {
return contentView.grid.contentEdgeInsetsPreset
}
set(value) {
contentView.grid.contentEdgeInsetsPreset = value
}
}
/// A reference to EdgeInsets.
@IBInspectable
open var chipItemsContentEdgeInsets: EdgeInsets {
get {
return contentView.grid.contentEdgeInsets
}
set(value) {
contentView.grid.contentEdgeInsets = value
}
}
/// A preset wrapper around chipItems interimSpace.
open var chipItemsInterimSpacePreset: InterimSpacePreset {
get {
return contentView.grid.interimSpacePreset
}
set(value) {
contentView.grid.interimSpacePreset = value
}
}
/// A wrapper around chipItems interimSpace.
@IBInspectable
open var chipItemsInterimSpace: InterimSpace {
get {
return contentView.grid.interimSpace
}
set(value) {
contentView.grid.interimSpace = value
}
}
/// Buttons.
open var chipItems = [ChipItem]() {
didSet {
oldValue.forEach {
$0.removeFromSuperview()
}
prepareChipItems()
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
guard willLayout else {
return
}
layoutScrollView()
updateScrollView()
}
open override func prepare() {
super.prepare()
interimSpacePreset = .interimSpace3
contentEdgeInsetsPreset = .square1
chipItemsInterimSpacePreset = .interimSpace4
chipItemsContentEdgeInsetsPreset = .square2
chipItemsContentEdgeInsets.left = 0
chipItemsContentEdgeInsets.right = 0
prepareContentView()
prepareScrollView()
prepareDivider()
}
}
fileprivate extension ChipBar {
/// Prepares the divider.
func prepareDivider() {
dividerColor = Color.grey.lighten2
}
/// Prepares the chipItems.
func prepareChipItems() {
for v in chipItems {
v.grid.columns = 0
v.layer.cornerRadius = 0
v.contentEdgeInsets = .zero
v.removeTarget(self, action: #selector(handle(chipItem:)), for: .touchUpInside)
v.addTarget(self, action: #selector(handle(chipItem:)), for: .touchUpInside)
}
}
/// Prepares the contentView.
func prepareContentView() {
contentView.layer.zPosition = 6000
}
/// Prepares the scroll view.
func prepareScrollView() {
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
centerViews = [scrollView]
}
}
fileprivate extension ChipBar {
/// Layout the scrollView.
func layoutScrollView() {
contentView.grid.reload()
if .scrollable == chipBarStyle || (.auto == chipBarStyle && chipItemsTotalWidth > scrollView.bounds.width) {
var w: CGFloat = 0
let q = 2 * chipItemsInterimSpace
let p = q + chipItemsInterimSpace
for v in chipItems {
let x = v.sizeThatFits(CGSize(width: .greatestFiniteMagnitude, height: scrollView.bounds.height)).width
v.frame.size.height = scrollView.bounds.height
v.frame.size.width = x + q
v.frame.origin.x = w
w += x
w += p
if scrollView != v.superview {
scrollView.addSubview(v)
}
}
w -= chipItemsInterimSpace
scrollView.contentSize = CGSize(width: w, height: scrollView.bounds.height)
} else {
scrollView.grid.begin()
scrollView.grid.views = chipItems
scrollView.grid.axis.columns = chipItems.count
scrollView.grid.contentEdgeInsets = chipItemsContentEdgeInsets
scrollView.grid.interimSpace = chipItemsInterimSpace
scrollView.grid.commit()
scrollView.contentSize = scrollView.frame.size
}
}
}
fileprivate extension ChipBar {
/// Handles the chipItem touch event.
@objc
func handle(chipItem: ChipItem) {
animate(to: chipItem, isTriggeredByUserInteraction: true)
}
}
extension ChipBar {
/**
Selects a given index from the chipItems array.
- Parameter at index: An Int.
- Paramater completion: An optional completion block.
*/
open func select(at index: Int, completion: ((ChipItem) -> Void)? = nil) {
guard -1 < index, index < chipItems.count else {
return
}
animate(to: chipItems[index], isTriggeredByUserInteraction: false, completion: completion)
}
/**
Animates to a given chipItem.
- Parameter to chipItem: A ChipItem.
- Parameter completion: An optional completion block.
*/
open func animate(to chipItem: ChipItem, completion: ((ChipItem) -> Void)? = nil) {
animate(to: chipItem, isTriggeredByUserInteraction: false, completion: completion)
}
}
fileprivate extension ChipBar {
/**
Animates to a given chipItem.
- Parameter to chipItem: A ChipItem.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the
state was changed by a user interaction, true if yes, false otherwise.
- Parameter completion: An optional completion block.
*/
func animate(to chipItem: ChipItem, isTriggeredByUserInteraction: Bool, completion: ((ChipItem) -> Void)? = nil) {
if isTriggeredByUserInteraction {
delegate?.chipBar?(chipBar: self, willSelect: chipItem)
}
selectedChipItem = chipItem
updateScrollView()
}
}
fileprivate extension ChipBar {
/// Updates the scrollView.
func updateScrollView() {
guard let v = selectedChipItem else {
return
}
if !scrollView.bounds.contains(v.frame) {
let contentOffsetX = (v.frame.origin.x < scrollView.bounds.minX) ? v.frame.origin.x : v.frame.maxX - scrollView.bounds.width
let normalizedOffsetX = min(max(contentOffsetX, 0), scrollView.contentSize.width - scrollView.bounds.width)
scrollView.setContentOffset(CGPoint(x: normalizedOffsetX, y: 0), animated: true)
}
}
}
================================================
FILE: Sources/iOS/Chip/ChipBarController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
fileprivate var ChipItemKey: UInt8 = 0
@objc(ChipBarAlignment)
public enum ChipBarAlignment: Int {
case top
case bottom
case hidden
}
extension UIViewController {
/**
A convenience property that provides access to the ChipBarController.
This is the recommended method of accessing the ChipBarController
through child UIViewControllers.
*/
public var chipBarController: ChipBarController? {
return traverseViewControllerHierarchyForClassType()
}
}
open class ChipBarController: TransitionController {
/**
A Display value to indicate whether or not to
display the rootViewController to the full view
bounds, or up to the toolbar height.
*/
open var displayStyle = DisplayStyle.partial {
didSet {
layoutSubviews()
}
}
/// The ChipBar used to switch between view controllers.
@IBInspectable
public let chipBar = ChipBar()
/// The chipBar alignment.
open var chipBarAlignment = ChipBarAlignment.bottom {
didSet {
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutChipBar()
layoutContainer()
layoutRootViewController()
}
open override func prepare() {
super.prepare()
prepareChipBar()
}
}
fileprivate extension ChipBarController {
/// Prepares the ChipBar.
func prepareChipBar() {
chipBar.depthPreset = .depth1
view.addSubview(chipBar)
}
}
fileprivate extension ChipBarController {
/// Layout the container.
func layoutContainer() {
chipBar.frame.size.width = view.bounds.width
switch displayStyle {
case .partial:
let p = chipBar.bounds.height
let y = view.bounds.height - p
switch chipBarAlignment {
case .top:
container.frame.origin.y = p
container.frame.size.height = y
case .bottom:
container.frame.origin.y = 0
container.frame.size.height = y
case .hidden:
container.frame.origin.y = 0
container.frame.size.height = view.bounds.height
}
container.frame.size.width = view.bounds.width
case .full:
container.frame = view.bounds
}
}
/// Layout the chipBar.
func layoutChipBar() {
chipBar.frame.size.width = view.bounds.width
switch chipBarAlignment {
case .top:
chipBar.isHidden = false
chipBar.frame.origin.y = 0
case .bottom:
chipBar.isHidden = false
chipBar.frame.origin.y = view.bounds.height - chipBar.bounds.height
case .hidden:
chipBar.isHidden = true
}
}
/// Layout the rootViewController.
func layoutRootViewController() {
rootViewController.view.frame = container.bounds
}
}
================================================
FILE: Sources/iOS/Collection/CardCollectionView/CardCollectionViewCell.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class CardCollectionViewCell: CollectionViewCell {
/// An optional reference to the card being displayed in the cell.
open var card: Card? {
didSet {
oldValue?.removeFromSuperview()
guard let v = card else {
return
}
contentView.addSubview(v)
}
}
open override func prepare() {
super.prepare()
pulseAnimation = .none
}
}
================================================
FILE: Sources/iOS/Collection/CardCollectionView/CardCollectionViewController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
extension UIViewController {
/**
A convenience property that provides access to the CardCollectionViewController.
This is the recommended method of accessing the CardCollectionViewController
through child UIViewControllers.
*/
public var cardCollectionViewController: CardCollectionViewController? {
return traverseViewControllerHierarchyForClassType()
}
}
open class CardCollectionViewController: ViewController {
/// A reference to a Reminder.
public let collectionView = CollectionView()
open var dataSourceItems = [DataSourceItem]()
/// An index of IndexPath to DataSourceItem.
open var dataSourceItemsIndexPaths = [IndexPath: Any]()
open override func prepare() {
super.prepare()
prepareCollectionView()
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutCollectionView()
}
}
extension CardCollectionViewController {
/// Prepares the collectionView.
fileprivate func prepareCollectionView() {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(CardCollectionViewCell.self, forCellWithReuseIdentifier: "CardCollectionViewCell")
view.addSubview(collectionView)
layoutCollectionView()
}
}
extension CardCollectionViewController {
/// Sets the frame for the collectionView.
fileprivate func layoutCollectionView() {
collectionView.frame = view.bounds
}
}
extension CardCollectionViewController: CollectionViewDelegate {}
extension CardCollectionViewController: CollectionViewDataSource {
@objc
open func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
@objc
open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataSourceItems.count
}
@objc
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCollectionViewCell", for: indexPath) as! CardCollectionViewCell
guard let card = dataSourceItems[indexPath.item].data as? Card else {
return cell
}
dataSourceItemsIndexPaths[indexPath] = card
card.frame = cell.bounds
cell.card = card
return cell
}
}
================================================
FILE: Sources/iOS/Collection/CollectionReusableView.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc(CollectionReusableView)
open class CollectionReusableView: UICollectionReusableView, Pulseable, PulseableLayer {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
allows the dropshadow effect on the backing layer, while clipping
the image to a desired shape within the visualLayer.
*/
public let visualLayer = CAShapeLayer()
/// A Pulse reference.
internal var pulse: Pulse!
/// A reference to the pulse layer.
internal var pulseLayer: CALayer? {
return pulse.pulseLayer
}
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
set(value) {
pulse.animation = value
}
}
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
return pulse.color
}
set(value) {
pulse.color = value
}
}
/// Pulse opacity.
@IBInspectable
open var pulseOpacity: CGFloat {
get {
return pulse.opacity
}
set(value) {
pulse.opacity = value
}
}
/**
A property that manages an image for the visualLayer's contents
property. Images should not be set to the backing layer's contents
property to avoid conflicts when using clipsToBounds.
*/
@IBInspectable
open var image: UIImage? {
didSet {
visualLayer.contents = image?.cgImage
}
}
/**
Allows a relative subrectangle within the range of 0 to 1 to be
specified for the visualLayer's contents property. This allows
much greater flexibility than the contentsGravity property in
terms of how the image is cropped and stretched.
*/
@IBInspectable
open var contentsRect: CGRect {
get {
return visualLayer.contentsRect
}
set(value) {
visualLayer.contentsRect = value
}
}
/**
A CGRect that defines a stretchable region inside the visualLayer
with a fixed border around the edge.
*/
@IBInspectable
open var contentsCenter: CGRect {
get {
return visualLayer.contentsCenter
}
set(value) {
visualLayer.contentsCenter = value
}
}
/**
A floating point value that defines a ratio between the pixel
dimensions of the visualLayer's contents property and the size
of the view. By default, this value is set to the Screen.scale.
*/
@IBInspectable
open var contentsScale: CGFloat {
get {
return visualLayer.contentsScale
}
set(value) {
visualLayer.contentsScale = value
}
}
/// Determines how content should be aligned within the visualLayer's bounds.
@IBInspectable
open var contentsGravity: CALayerContentsGravity {
get {
return visualLayer.contentsGravity
}
set(value) {
visualLayer.contentsGravity = value
}
}
/// A preset wrapper around contentEdgeInsets.
open var contentEdgeInsetsPreset: EdgeInsetsPreset {
get {
return grid.contentEdgeInsetsPreset
}
set(value) {
grid.contentEdgeInsetsPreset = value
}
}
/// A reference to EdgeInsets.
@IBInspectable
open var contentEdgeInsets: UIEdgeInsets {
get {
return grid.contentEdgeInsets
}
set(value) {
grid.contentEdgeInsets = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return grid.interimSpacePreset
}
set(value) {
grid.interimSpacePreset = value
}
}
/// A wrapper around grid.interimSpace.
@IBInspectable
open var interimSpace: InterimSpace {
get {
return grid.interimSpace
}
set(value) {
grid.interimSpace = value
}
}
/// A property that accesses the backing layer's background
@IBInspectable
open override var backgroundColor: UIColor? {
didSet {
layer.backgroundColor = backgroundColor?.cgColor
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/**
An initializer that initializes the object with a CGRect object.
If AutoLayout is used, it is better to initilize the instance
using the init() initializer.
- Parameter frame: A CGRect instance.
*/
public override init(frame: CGRect) {
super.init(frame: frame)
prepare()
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutShape()
layoutVisualLayer()
layoutShadowPath()
}
/**
Triggers the pulse animation.
- Parameter point: A Optional point to pulse from, otherwise pulses
from the center.
*/
open func pulse(point: CGPoint? = nil) {
pulse.expand(point: point ?? center)
Motion.delay(0.35) { [weak self] in
self?.pulse.contract()
}
}
/**
A delegation method that is executed when the view has began a
touch event.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesBegan(_ touches: Set, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
pulse.expand(point: layer.convert(touches.first!.location(in: self), from: layer))
}
/**
A delegation method that is executed when the view touch event has
ended.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesEnded(_ touches: Set, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
pulse.contract()
}
/**
A delegation method that is executed when the view touch event has
been cancelled.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
pulse.contract()
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open func prepare() {
contentsGravity = .resizeAspectFill
contentScaleFactor = Screen.scale
prepareVisualLayer()
preparePulse()
}
}
extension CollectionReusableView {
/// Prepares the pulse motion.
fileprivate func preparePulse() {
pulse = Pulse(pulseView: self, pulseLayer: visualLayer)
pulseAnimation = .none
}
/// Prepares the visualLayer property.
fileprivate func prepareVisualLayer() {
visualLayer.zPosition = 0
visualLayer.masksToBounds = true
layer.addSublayer(visualLayer)
}
}
extension CollectionReusableView {
/// Manages the layout for the visualLayer property.
fileprivate func layoutVisualLayer() {
visualLayer.frame = bounds
visualLayer.cornerRadius = layer.cornerRadius
}
}
================================================
FILE: Sources/iOS/Collection/CollectionView.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class CollectionView: UICollectionView {
/// A preset wrapper around contentEdgeInsets.
open var contentEdgeInsetsPreset: EdgeInsetsPreset {
get {
return (collectionViewLayout as? CollectionViewLayout)!.contentEdgeInsetsPreset
}
set(value) {
(collectionViewLayout as? CollectionViewLayout)!.contentEdgeInsetsPreset = value
}
}
open var contentEdgeInsets: EdgeInsets {
get {
return (collectionViewLayout as? CollectionViewLayout)!.contentEdgeInsets
}
set(value) {
(collectionViewLayout as? CollectionViewLayout)!.contentEdgeInsets = value
}
}
/// Scroll direction.
open var scrollDirection: UICollectionView.ScrollDirection {
get {
return (collectionViewLayout as? CollectionViewLayout)!.scrollDirection
}
set(value) {
(collectionViewLayout as? CollectionViewLayout)!.scrollDirection = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return (collectionViewLayout as? CollectionViewLayout)!.interimSpacePreset
}
set(value) {
(collectionViewLayout as? CollectionViewLayout)!.interimSpacePreset = value
}
}
/// Spacing between items.
@IBInspectable
open var interimSpace: InterimSpace {
get {
return (collectionViewLayout as? CollectionViewLayout)!.interimSpace
}
set(value) {
(collectionViewLayout as? CollectionViewLayout)!.interimSpace = value
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/**
An initializer that initializes the object.
- Parameter frame: A CGRect defining the view's frame.
- Parameter collectionViewLayout: A UICollectionViewLayout reference.
*/
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
prepare()
}
/**
An initializer that initializes the object.
- Parameter collectionViewLayout: A UICollectionViewLayout reference.
*/
public init(collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: .zero, collectionViewLayout: layout)
prepare()
}
/**
An initializer that initializes the object.
- Parameter frame: A CGRect defining the view's frame.
*/
public init(frame: CGRect) {
let layout = CollectionViewLayout()
super.init(frame: frame, collectionViewLayout: layout)
prepare()
}
/// A convenience initializer that initializes the object.
public init() {
let layout = CollectionViewLayout()
super.init(frame: .zero, collectionViewLayout: layout)
prepare()
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open func prepare() {
backgroundColor = .white
contentScaleFactor = Screen.scale
register(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
}
}
================================================
FILE: Sources/iOS/Collection/CollectionViewCell.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc(CollectionViewCell)
open class CollectionViewCell: UICollectionViewCell, Pulseable, PulseableLayer {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
allows the dropshadow effect on the backing layer, while clipping
the image to a desired shape within the visualLayer.
*/
public let visualLayer = CAShapeLayer()
/// A Pulse reference.
internal var pulse: Pulse!
/// A reference to the pulse layer.
internal var pulseLayer: CALayer? {
return pulse.pulseLayer
}
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
set(value) {
pulse.animation = value
}
}
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
return pulse.color
}
set(value) {
pulse.color = value
}
}
/// Pulse opacity.
@IBInspectable
open var pulseOpacity: CGFloat {
get {
return pulse.opacity
}
set(value) {
pulse.opacity = value
}
}
/**
A property that manages an image for the visualLayer's contents
property. Images should not be set to the backing layer's contents
property to avoid conflicts when using clipsToBounds.
*/
@IBInspectable
open var image: UIImage? {
didSet {
visualLayer.contents = image?.cgImage
}
}
/**
Allows a relative subrectangle within the range of 0 to 1 to be
specified for the visualLayer's contents property. This allows
much greater flexibility than the contentsGravity property in
terms of how the image is cropped and stretched.
*/
@IBInspectable
open var contentsRect: CGRect {
get {
return visualLayer.contentsRect
}
set(value) {
visualLayer.contentsRect = value
}
}
/**
A CGRect that defines a stretchable region inside the visualLayer
with a fixed border around the edge.
*/
@IBInspectable
open var contentsCenter: CGRect {
get {
return visualLayer.contentsCenter
}
set(value) {
visualLayer.contentsCenter = value
}
}
/**
A floating point value that defines a ratio between the pixel
dimensions of the visualLayer's contents property and the size
of the view. By default, this value is set to the Screen.scale.
*/
@IBInspectable
open var contentsScale: CGFloat {
get {
return visualLayer.contentsScale
}
set(value) {
visualLayer.contentsScale = value
}
}
/// Determines how content should be aligned within the visualLayer's bounds.
@IBInspectable
open var contentsGravity: CALayerContentsGravity {
get {
return visualLayer.contentsGravity
}
set(value) {
visualLayer.contentsGravity = value
}
}
/// A property that accesses the backing layer's background
@IBInspectable
open override var backgroundColor: UIColor? {
didSet {
layer.backgroundColor = backgroundColor?.cgColor
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/**
An initializer that initializes the object with a CGRect object.
If AutoLayout is used, it is better to initilize the instance
using the init() initializer.
- Parameter frame: A CGRect instance.
*/
public override init(frame: CGRect) {
super.init(frame: frame)
prepare()
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutShape()
layoutVisualLayer()
layoutShadowPath()
}
/**
Triggers the pulse animation.
- Parameter point: A Optional point to pulse from, otherwise pulses
from the center.
*/
open func pulse(point: CGPoint? = nil) {
pulse.expand(point: point ?? center)
Motion.delay(0.35) { [weak self] in
self?.pulse.contract()
}
}
/**
A delegation method that is executed when the view has began a
touch event.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesBegan(_ touches: Set, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
pulse.expand(point: layer.convert(touches.first!.location(in: self), from: layer))
}
/**
A delegation method that is executed when the view touch event has
ended.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesEnded(_ touches: Set, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
pulse.contract()
}
/**
A delegation method that is executed when the view touch event has
been cancelled.
- Parameter touches: A set of UITouch objects.
- Parameter event: A UIEvent object.
*/
open override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
pulse.contract()
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open func prepare() {
contentsGravity = .resizeAspectFill
contentScaleFactor = Screen.scale
backgroundColor = .white
prepareVisualLayer()
preparePulse()
}
}
fileprivate extension CollectionViewCell {
/// Prepares the pulse motion.
func preparePulse() {
pulse = Pulse(pulseView: self, pulseLayer: visualLayer)
}
/// Prepares the visualLayer property.
func prepareVisualLayer() {
visualLayer.zPosition = 0
visualLayer.masksToBounds = true
layer.addSublayer(visualLayer)
}
}
fileprivate extension CollectionViewCell {
/// Manages the layout for the visualLayer property.
func layoutVisualLayer() {
visualLayer.frame = bounds
visualLayer.cornerRadius = layer.cornerRadius
}
}
================================================
FILE: Sources/iOS/Collection/CollectionViewController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public protocol CollectionViewDelegate: UICollectionViewDelegate {}
public protocol CollectionViewDataSource: UICollectionViewDataSource {
/**
Retrieves the data source items for the collectionView.
- Returns: An Array of DataSourceItem objects.
*/
var dataSourceItems: [DataSourceItem] { get }
}
extension UIViewController {
/**
A convenience property that provides access to the CollectionViewController.
This is the recommended method of accessing the CollectionViewController
through child UIViewControllers.
*/
public var collectionViewController: CollectionViewController? {
return traverseViewControllerHierarchyForClassType()
}
}
open class CollectionViewController: ViewController {
/// A reference to a Reminder.
public let collectionView = CollectionView()
open var dataSourceItems = [DataSourceItem]()
open override func prepare() {
super.prepare()
prepareCollectionView()
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutCollectionView()
}
}
extension CollectionViewController {
/// Prepares the collectionView.
fileprivate func prepareCollectionView() {
collectionView.delegate = self
collectionView.dataSource = self
view.layout(collectionView).edges()
}
}
extension CollectionViewController {
/// Sets the frame for the collectionView.
fileprivate func layoutCollectionView() {
collectionView.frame = view.bounds
}
}
extension CollectionViewController: CollectionViewDelegate {}
extension CollectionViewController: CollectionViewDataSource {
@objc
open func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
@objc
open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataSourceItems.count
}
@objc
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)
}
}
================================================
FILE: Sources/iOS/Collection/CollectionViewLayout.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
open class CollectionViewLayout: UICollectionViewLayout {
/// Used to calculate the dimensions of the cells.
public var offset = CGPoint.zero
/// The size of items.
public var itemSize = CGSize.zero
/// A preset wrapper around contentEdgeInsets.
public var contentEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
contentEdgeInsets = EdgeInsetsPresetToValue(preset: contentEdgeInsetsPreset)
}
}
/// A wrapper around grid.contentEdgeInsets.
public var contentEdgeInsets = EdgeInsets.zero
/// Size of the content.
public fileprivate(set) var contentSize = CGSize.zero
/// Layout attribute items.
public fileprivate(set) lazy var layoutItems = [(UICollectionViewLayoutAttributes, NSIndexPath)]()
/// Cell data source items.
public fileprivate(set) var dataSourceItems: [DataSourceItem]?
/// Scroll direction.
public var scrollDirection = UICollectionView.ScrollDirection.vertical
/// A preset wrapper around interimSpace.
public var interimSpacePreset = InterimSpacePreset.none {
didSet {
interimSpace = InterimSpacePresetToValue(preset: interimSpacePreset)
}
}
/// Spacing between items.
public var interimSpace: InterimSpace = 0
open override var collectionViewContentSize: CGSize {
return contentSize
}
}
extension CollectionViewLayout {
/**
Retrieves the index paths for the items within the passed in CGRect.
- Parameter rect: A CGRect that acts as the bounds to find the items within.
- Returns: An Array of NSIndexPath objects.
*/
public func indexPathsOfItems(in rect: CGRect) -> [NSIndexPath] {
var paths = [NSIndexPath]()
for (attribute, indexPath) in layoutItems {
guard rect.intersects(attribute.frame) else {
continue
}
paths.append(indexPath)
}
return paths
}
}
extension CollectionViewLayout {
/**
Prepares the layout for the given data source items.
- Parameter for dataSourceItems: An Array of DataSourceItems.
*/
fileprivate func prepareLayout(for dataSourceItems: [DataSourceItem]) {
self.dataSourceItems = dataSourceItems
layoutItems.removeAll()
offset.x = contentEdgeInsets.left
offset.y = contentEdgeInsets.top
for i in 0.. UICollectionViewLayoutAttributes? {
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let dataSourceItem = dataSourceItems![indexPath.item]
if 0 < itemSize.width && 0 < itemSize.height {
attributes.frame = CGRect(x: offset.x, y: offset.y, width: itemSize.width - contentEdgeInsets.left - contentEdgeInsets.right, height: itemSize.height - contentEdgeInsets.top - contentEdgeInsets.bottom)
} else if .vertical == scrollDirection {
if let h = dataSourceItem.height {
attributes.frame = CGRect(x: contentEdgeInsets.left, y: offset.y, width: collectionView!.bounds.width - contentEdgeInsets.left - contentEdgeInsets.right, height: h)
} else if let v = dataSourceItem.data as? UIView, 0 < v.bounds.height {
v.updateConstraintsIfNeeded()
v.updateConstraints()
v.setNeedsLayout()
v.layoutIfNeeded()
attributes.frame = CGRect(x: contentEdgeInsets.left, y: offset.y, width: collectionView!.bounds.width - contentEdgeInsets.left - contentEdgeInsets.right, height: v.bounds.height)
} else {
attributes.frame = CGRect(x: contentEdgeInsets.left, y: offset.y, width: collectionView!.bounds.width - contentEdgeInsets.left - contentEdgeInsets.right, height: collectionView!.bounds.height)
}
} else {
if let w = dataSourceItem.width {
attributes.frame = CGRect(x: offset.x, y: contentEdgeInsets.top, width: w, height: collectionView!.bounds.height - contentEdgeInsets.top - contentEdgeInsets.bottom)
} else if let v = dataSourceItem.data as? UIView, 0 < v.bounds.width {
v.updateConstraintsIfNeeded()
v.updateConstraints()
v.setNeedsLayout()
v.layoutIfNeeded()
attributes.frame = CGRect(x: offset.x, y: contentEdgeInsets.top, width: v.bounds.width, height: collectionView!.bounds.height - contentEdgeInsets.top - contentEdgeInsets.bottom)
} else {
attributes.frame = CGRect(x: offset.x, y: contentEdgeInsets.top, width: collectionView!.bounds.width, height: collectionView!.bounds.height - contentEdgeInsets.top - contentEdgeInsets.bottom)
}
}
return attributes
}
open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for (attribute, _) in layoutItems {
if rect.intersects(attribute.frame) {
layoutAttributes.append(attribute)
}
}
return layoutAttributes
}
open override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return .vertical == scrollDirection ? newBounds.width != collectionView!.bounds.width : newBounds.height != collectionView!.bounds.height
}
open override func prepare() {
guard let dataSource = collectionView?.dataSource as? CollectionViewDataSource else {
return
}
prepareLayout(for: dataSource.dataSourceItems)
}
open override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
return proposedContentOffset
}
}
================================================
FILE: Sources/iOS/Color/Color.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(ColorPalette)
public protocol ColorPalette {
/// Material color code: 50
static var lighten5: UIColor { get }
/// Material color code: 100
static var lighten4: UIColor { get }
/// Material color code: 200
static var lighten3: UIColor { get }
/// Material color code: 300
static var lighten2: UIColor { get }
/// Material color code: 400
static var lighten1: UIColor { get }
/// Material color code: 500
static var base: UIColor { get }
/// Material color code: 600
static var darken1: UIColor { get }
/// Material color code: 700
static var darken2: UIColor { get }
/// Material color code: 800
static var darken3: UIColor { get }
/// Material color code: 900
static var darken4: UIColor { get }
/// Material color code: A100
@objc
optional static var accent1: UIColor { get }
/// Material color code: A200
@objc
optional static var accent2: UIColor { get }
/// Material color code: A400
@objc
optional static var accent3: UIColor { get }
/// Material color code: A700
@objc
optional static var accent4: UIColor { get }
}
open class Color: UIColor {
// dark text
open class darkText {
public static let primary = Color.black.withAlphaComponent(0.87)
public static let secondary = Color.black.withAlphaComponent(0.54)
public static let others = Color.black.withAlphaComponent(0.38)
public static let dividers = Color.black.withAlphaComponent(0.12)
}
// light text
open class lightText {
public static let primary = Color.white
public static let secondary = Color.white.withAlphaComponent(0.7)
public static let others = Color.white.withAlphaComponent(0.5)
public static let dividers = Color.white.withAlphaComponent(0.12)
}
// red
open class red: ColorPalette {
public static let lighten5 = UIColor(red: 255/255, green: 235/255, blue: 238/255, alpha: 1)
public static let lighten4 = UIColor(red: 255/255, green: 205/255, blue: 210/255, alpha: 1)
public static let lighten3 = UIColor(red: 239/255, green: 154/255, blue: 154/255, alpha: 1)
public static let lighten2 = UIColor(red: 229/255, green: 115/255, blue: 115/255, alpha: 1)
public static let lighten1 = UIColor(red: 229/255, green: 83/255, blue: 80/255, alpha: 1)
public static let base = UIColor(red: 244/255, green: 67/255, blue: 54/255, alpha: 1)
public static let darken1 = UIColor(red: 229/255, green: 57/255, blue: 53/255, alpha: 1)
public static let darken2 = UIColor(red: 211/255, green: 47/255, blue: 47/255, alpha: 1)
public static let darken3 = UIColor(red: 198/255, green: 40/255, blue: 40/255, alpha: 1)
public static let darken4 = UIColor(red: 183/255, green: 28/255, blue: 28/255, alpha: 1)
public static let accent1 = UIColor(red: 255/255, green: 138/255, blue: 128/255, alpha: 1)
public static let accent2 = UIColor(red: 255/255, green: 82/255, blue: 82/255, alpha: 1)
public static let accent3 = UIColor(red: 255/255, green: 23/255, blue: 68/255, alpha: 1)
public static let accent4 = UIColor(red: 213/255, green: 0/255, blue: 0/255, alpha: 1)
}
// pink
open class pink: ColorPalette {
public static let lighten5 = UIColor(red: 252/255, green: 228/255, blue: 236/255, alpha: 1)
public static let lighten4 = UIColor(red: 248/255, green: 187/255, blue: 208/255, alpha: 1)
public static let lighten3 = UIColor(red: 244/255, green: 143/255, blue: 177/255, alpha: 1)
public static let lighten2 = UIColor(red: 240/255, green: 98/255, blue: 146/255, alpha: 1)
public static let lighten1 = UIColor(red: 236/255, green: 64/255, blue: 122/255, alpha: 1)
public static let base = UIColor(red: 233/255, green: 30/255, blue: 99/255, alpha: 1)
public static let darken1 = UIColor(red: 216/255, green: 27/255, blue: 96/255, alpha: 1)
public static let darken2 = UIColor(red: 194/255, green: 24/255, blue: 91/255, alpha: 1)
public static let darken3 = UIColor(red: 173/255, green: 20/255, blue: 87/255, alpha: 1)
public static let darken4 = UIColor(red: 136/255, green: 14/255, blue: 79/255, alpha: 1)
public static let accent1 = UIColor(red: 255/255, green: 128/255, blue: 171/255, alpha: 1)
public static let accent2 = UIColor(red: 255/255, green: 64/255, blue: 129/255, alpha: 1)
public static let accent3 = UIColor(red: 245/255, green: 0/255, blue: 87/255, alpha: 1)
public static let accent4 = UIColor(red: 197/255, green: 17/255, blue: 98/255, alpha: 1)
}
// purple
open class purple: ColorPalette {
public static let lighten5 = UIColor(red: 243/255, green: 229/255, blue: 245/255, alpha: 1)
public static let lighten4 = UIColor(red: 225/255, green: 190/255, blue: 231/255, alpha: 1)
public static let lighten3 = UIColor(red: 206/255, green: 147/255, blue: 216/255, alpha: 1)
public static let lighten2 = UIColor(red: 186/255, green: 104/255, blue: 200/255, alpha: 1)
public static let lighten1 = UIColor(red: 171/255, green: 71/255, blue: 188/255, alpha: 1)
public static let base = UIColor(red: 156/255, green: 39/255, blue: 176/255, alpha: 1)
public static let darken1 = UIColor(red: 142/255, green: 36/255, blue: 170/255, alpha: 1)
public static let darken2 = UIColor(red: 123/255, green: 31/255, blue: 162/255, alpha: 1)
public static let darken3 = UIColor(red: 106/255, green: 27/255, blue: 154/255, alpha: 1)
public static let darken4 = UIColor(red: 74/255, green: 20/255, blue: 140/255, alpha: 1)
public static let accent1 = UIColor(red: 234/255, green: 128/255, blue: 252/255, alpha: 1)
public static let accent2 = UIColor(red: 224/255, green: 64/255, blue: 251/255, alpha: 1)
public static let accent3 = UIColor(red: 213/255, green: 0/255, blue: 249/255, alpha: 1)
public static let accent4 = UIColor(red: 170/255, green: 0/255, blue: 255/255, alpha: 1)
}
// deepPurple
open class deepPurple: ColorPalette {
public static let lighten5 = UIColor(red: 237/255, green: 231/255, blue: 246/255, alpha: 1)
public static let lighten4 = UIColor(red: 209/255, green: 196/255, blue: 233/255, alpha: 1)
public static let lighten3 = UIColor(red: 179/255, green: 157/255, blue: 219/255, alpha: 1)
public static let lighten2 = UIColor(red: 149/255, green: 117/255, blue: 205/255, alpha: 1)
public static let lighten1 = UIColor(red: 126/255, green: 87/255, blue: 194/255, alpha: 1)
public static let base = UIColor(red: 103/255, green: 58/255, blue: 183/255, alpha: 1)
public static let darken1 = UIColor(red: 94/255, green: 53/255, blue: 177/255, alpha: 1)
public static let darken2 = UIColor(red: 81/255, green: 45/255, blue: 168/255, alpha: 1)
public static let darken3 = UIColor(red: 69/255, green: 39/255, blue: 160/255, alpha: 1)
public static let darken4 = UIColor(red: 49/255, green: 27/255, blue: 146/255, alpha: 1)
public static let accent1 = UIColor(red: 179/255, green: 136/255, blue: 255/255, alpha: 1)
public static let accent2 = UIColor(red: 124/255, green: 77/255, blue: 255/255, alpha: 1)
public static let accent3 = UIColor(red: 101/255, green: 31/255, blue: 255/255, alpha: 1)
public static let accent4 = UIColor(red: 98/255, green: 0/255, blue: 234/255, alpha: 1)
}
// indigo
open class indigo: ColorPalette {
public static let lighten5 = UIColor(red: 232/255, green: 234/255, blue: 246/255, alpha: 1)
public static let lighten4 = UIColor(red: 197/255, green: 202/255, blue: 233/255, alpha: 1)
public static let lighten3 = UIColor(red: 159/255, green: 168/255, blue: 218/255, alpha: 1)
public static let lighten2 = UIColor(red: 121/255, green: 134/255, blue: 203/255, alpha: 1)
public static let lighten1 = UIColor(red: 92/255, green: 107/255, blue: 192/255, alpha: 1)
public static let base = UIColor(red: 63/255, green: 81/255, blue: 181/255, alpha: 1)
public static let darken1 = UIColor(red: 57/255, green: 73/255, blue: 171/255, alpha: 1)
public static let darken2 = UIColor(red: 48/255, green: 63/255, blue: 159/255, alpha: 1)
public static let darken3 = UIColor(red: 40/255, green: 53/255, blue: 147/255, alpha: 1)
public static let darken4 = UIColor(red: 26/255, green: 35/255, blue: 126/255, alpha: 1)
public static let accent1 = UIColor(red: 140/255, green: 158/255, blue: 255/255, alpha: 1)
public static let accent2 = UIColor(red: 83/255, green: 109/255, blue: 254/255, alpha: 1)
public static let accent3 = UIColor(red: 61/255, green: 90/255, blue: 254/255, alpha: 1)
public static let accent4 = UIColor(red: 48/255, green: 79/255, blue: 254/255, alpha: 1)
}
// blue
open class blue: ColorPalette {
public static let lighten5 = UIColor(red: 227/255, green: 242/255, blue: 253/255, alpha: 1)
public static let lighten4 = UIColor(red: 187/255, green: 222/255, blue: 251/255, alpha: 1)
public static let lighten3 = UIColor(red: 144/255, green: 202/255, blue: 249/255, alpha: 1)
public static let lighten2 = UIColor(red: 100/255, green: 181/255, blue: 246/255, alpha: 1)
public static let lighten1 = UIColor(red: 66/255, green: 165/255, blue: 245/255, alpha: 1)
public static let base = UIColor(red: 33/255, green: 150/255, blue: 243/255, alpha: 1)
public static let darken1 = UIColor(red: 30/255, green: 136/255, blue: 229/255, alpha: 1)
public static let darken2 = UIColor(red: 25/255, green: 118/255, blue: 210/255, alpha: 1)
public static let darken3 = UIColor(red: 21/255, green: 101/255, blue: 192/255, alpha: 1)
public static let darken4 = UIColor(red: 13/255, green: 71/255, blue: 161/255, alpha: 1)
public static let accent1 = UIColor(red: 130/255, green: 177/255, blue: 255/255, alpha: 1)
public static let accent2 = UIColor(red: 68/255, green: 138/255, blue: 255/255, alpha: 1)
public static let accent3 = UIColor(red: 41/255, green: 121/255, blue: 255/255, alpha: 1)
public static let accent4 = UIColor(red: 41/255, green: 98/255, blue: 255/255, alpha: 1)
}
// light blue
open class lightBlue: ColorPalette {
public static let lighten5 = UIColor(red: 225/255, green: 245/255, blue: 254/255, alpha: 1)
public static let lighten4 = UIColor(red: 179/255, green: 229/255, blue: 252/255, alpha: 1)
public static let lighten3 = UIColor(red: 129/255, green: 212/255, blue: 250/255, alpha: 1)
public static let lighten2 = UIColor(red: 79/255, green: 195/255, blue: 247/255, alpha: 1)
public static let lighten1 = UIColor(red: 41/255, green: 182/255, blue: 246/255, alpha: 1)
public static let base = UIColor(red: 3/255, green: 169/255, blue: 244/255, alpha: 1)
public static let darken1 = UIColor(red: 3/255, green: 155/255, blue: 229/255, alpha: 1)
public static let darken2 = UIColor(red: 2/255, green: 136/255, blue: 209/255, alpha: 1)
public static let darken3 = UIColor(red: 2/255, green: 119/255, blue: 189/255, alpha: 1)
public static let darken4 = UIColor(red: 1/255, green: 87/255, blue: 155/255, alpha: 1)
public static let accent1 = UIColor(red: 128/255, green: 216/255, blue: 255/255, alpha: 1)
public static let accent2 = UIColor(red: 64/255, green: 196/255, blue: 255/255, alpha: 1)
public static let accent3 = UIColor(red: 0/255, green: 176/255, blue: 255/255, alpha: 1)
public static let accent4 = UIColor(red: 0/255, green: 145/255, blue: 234/255, alpha: 1)
}
// cyan
open class cyan: ColorPalette {
public static let lighten5 = UIColor(red: 224/255, green: 247/255, blue: 250/255, alpha: 1)
public static let lighten4 = UIColor(red: 178/255, green: 235/255, blue: 242/255, alpha: 1)
public static let lighten3 = UIColor(red: 128/255, green: 222/255, blue: 234/255, alpha: 1)
public static let lighten2 = UIColor(red: 77/255, green: 208/255, blue: 225/255, alpha: 1)
public static let lighten1 = UIColor(red: 38/255, green: 198/255, blue: 218/255, alpha: 1)
public static let base = UIColor(red: 0/255, green: 188/255, blue: 212/255, alpha: 1)
public static let darken1 = UIColor(red: 0/255, green: 172/255, blue: 193/255, alpha: 1)
public static let darken2 = UIColor(red: 0/255, green: 151/255, blue: 167/255, alpha: 1)
public static let darken3 = UIColor(red: 0/255, green: 131/255, blue: 143/255, alpha: 1)
public static let darken4 = UIColor(red: 0/255, green: 96/255, blue: 100/255, alpha: 1)
public static let accent1 = UIColor(red: 132/255, green: 255/255, blue: 255/255, alpha: 1)
public static let accent2 = UIColor(red: 24/255, green: 255/255, blue: 255/255, alpha: 1)
public static let accent3 = UIColor(red: 0/255, green: 229/255, blue: 255/255, alpha: 1)
public static let accent4 = UIColor(red: 0/255, green: 184/255, blue: 212/255, alpha: 1)
}
// teal
open class teal: ColorPalette {
public static let lighten5 = UIColor(red: 224/255, green: 242/255, blue: 241/255, alpha: 1)
public static let lighten4 = UIColor(red: 178/255, green: 223/255, blue: 219/255, alpha: 1)
public static let lighten3 = UIColor(red: 128/255, green: 203/255, blue: 196/255, alpha: 1)
public static let lighten2 = UIColor(red: 77/255, green: 182/255, blue: 172/255, alpha: 1)
public static let lighten1 = UIColor(red: 38/255, green: 166/255, blue: 154/255, alpha: 1)
public static let base = UIColor(red: 0/255, green: 150/255, blue: 136/255, alpha: 1)
public static let darken1 = UIColor(red: 0/255, green: 137/255, blue: 123/255, alpha: 1)
public static let darken2 = UIColor(red: 0/255, green: 121/255, blue: 107/255, alpha: 1)
public static let darken3 = UIColor(red: 0/255, green: 105/255, blue: 92/255, alpha: 1)
public static let darken4 = UIColor(red: 0/255, green: 77/255, blue: 64/255, alpha: 1)
public static let accent1 = UIColor(red: 167/255, green: 255/255, blue: 235/255, alpha: 1)
public static let accent2 = UIColor(red: 100/255, green: 255/255, blue: 218/255, alpha: 1)
public static let accent3 = UIColor(red: 29/255, green: 233/255, blue: 182/255, alpha: 1)
public static let accent4 = UIColor(red: 0/255, green: 191/255, blue: 165/255, alpha: 1)
}
// green
open class green: ColorPalette {
public static let lighten5 = UIColor(red: 232/255, green: 245/255, blue: 233/255, alpha: 1)
public static let lighten4 = UIColor(red: 200/255, green: 230/255, blue: 201/255, alpha: 1)
public static let lighten3 = UIColor(red: 165/255, green: 214/255, blue: 167/255, alpha: 1)
public static let lighten2 = UIColor(red: 129/255, green: 199/255, blue: 132/255, alpha: 1)
public static let lighten1 = UIColor(red: 102/255, green: 187/255, blue: 106/255, alpha: 1)
public static let base = UIColor(red: 76/255, green: 175/255, blue: 80/255, alpha: 1)
public static let darken1 = UIColor(red: 67/255, green: 160/255, blue: 71/255, alpha: 1)
public static let darken2 = UIColor(red: 56/255, green: 142/255, blue: 60/255, alpha: 1)
public static let darken3 = UIColor(red: 46/255, green: 125/255, blue: 50/255, alpha: 1)
public static let darken4 = UIColor(red: 27/255, green: 94/255, blue: 32/255, alpha: 1)
public static let accent1 = UIColor(red: 185/255, green: 246/255, blue: 202/255, alpha: 1)
public static let accent2 = UIColor(red: 105/255, green: 240/255, blue: 174/255, alpha: 1)
public static let accent3 = UIColor(red: 0/255, green: 230/255, blue: 118/255, alpha: 1)
public static let accent4 = UIColor(red: 0/255, green: 200/255, blue: 83/255, alpha: 1)
}
// light green
open class lightGreen: ColorPalette {
public static let lighten5 = UIColor(red: 241/255, green: 248/255, blue: 233/255, alpha: 1)
public static let lighten4 = UIColor(red: 220/255, green: 237/255, blue: 200/255, alpha: 1)
public static let lighten3 = UIColor(red: 197/255, green: 225/255, blue: 165/255, alpha: 1)
public static let lighten2 = UIColor(red: 174/255, green: 213/255, blue: 129/255, alpha: 1)
public static let lighten1 = UIColor(red: 156/255, green: 204/255, blue: 101/255, alpha: 1)
public static let base = UIColor(red: 139/255, green: 195/255, blue: 74/255, alpha: 1)
public static let darken1 = UIColor(red: 124/255, green: 179/255, blue: 66/255, alpha: 1)
public static let darken2 = UIColor(red: 104/255, green: 159/255, blue: 56/255, alpha: 1)
public static let darken3 = UIColor(red: 85/255, green: 139/255, blue: 47/255, alpha: 1)
public static let darken4 = UIColor(red: 51/255, green: 105/255, blue: 30/255, alpha: 1)
public static let accent1 = UIColor(red: 204/255, green: 255/255, blue: 144/255, alpha: 1)
public static let accent2 = UIColor(red: 178/255, green: 255/255, blue: 89/255, alpha: 1)
public static let accent3 = UIColor(red: 118/255, green: 255/255, blue: 3/255, alpha: 1)
public static let accent4 = UIColor(red: 100/255, green: 221/255, blue: 23/255, alpha: 1)
}
// lime
open class lime: ColorPalette {
public static let lighten5 = UIColor(red: 249/255, green: 251/255, blue: 231/255, alpha: 1)
public static let lighten4 = UIColor(red: 240/255, green: 244/255, blue: 195/255, alpha: 1)
public static let lighten3 = UIColor(red: 230/255, green: 238/255, blue: 156/255, alpha: 1)
public static let lighten2 = UIColor(red: 220/255, green: 231/255, blue: 117/255, alpha: 1)
public static let lighten1 = UIColor(red: 212/255, green: 225/255, blue: 87/255, alpha: 1)
public static let base = UIColor(red: 205/255, green: 220/255, blue: 57/255, alpha: 1)
public static let darken1 = UIColor(red: 192/255, green: 202/255, blue: 51/255, alpha: 1)
public static let darken2 = UIColor(red: 175/255, green: 180/255, blue: 43/255, alpha: 1)
public static let darken3 = UIColor(red: 158/255, green: 157/255, blue: 36/255, alpha: 1)
public static let darken4 = UIColor(red: 130/255, green: 119/255, blue: 23/255, alpha: 1)
public static let accent1 = UIColor(red: 244/255, green: 255/255, blue: 129/255, alpha: 1)
public static let accent2 = UIColor(red: 238/255, green: 255/255, blue: 65/255, alpha: 1)
public static let accent3 = UIColor(red: 198/255, green: 255/255, blue: 0/255, alpha: 1)
public static let accent4 = UIColor(red: 174/255, green: 234/255, blue: 0/255, alpha: 1)
}
// yellow
open class yellow: ColorPalette {
public static let lighten5 = UIColor(red: 255/255, green: 253/255, blue: 231/255, alpha: 1)
public static let lighten4 = UIColor(red: 255/255, green: 249/255, blue: 196/255, alpha: 1)
public static let lighten3 = UIColor(red: 255/255, green: 245/255, blue: 157/255, alpha: 1)
public static let lighten2 = UIColor(red: 255/255, green: 241/255, blue: 118/255, alpha: 1)
public static let lighten1 = UIColor(red: 255/255, green: 238/255, blue: 88/255, alpha: 1)
public static let base = UIColor(red: 255/255, green: 235/255, blue: 59/255, alpha: 1)
public static let darken1 = UIColor(red: 253/255, green: 216/255, blue: 53/255, alpha: 1)
public static let darken2 = UIColor(red: 251/255, green: 192/255, blue: 45/255, alpha: 1)
public static let darken3 = UIColor(red: 249/255, green: 168/255, blue: 37/255, alpha: 1)
public static let darken4 = UIColor(red: 245/255, green: 127/255, blue: 23/255, alpha: 1)
public static let accent1 = UIColor(red: 255/255, green: 255/255, blue: 141/255, alpha: 1)
public static let accent2 = UIColor(red: 255/255, green: 255/255, blue: 0/255, alpha: 1)
public static let accent3 = UIColor(red: 255/255, green: 234/255, blue: 0/255, alpha: 1)
public static let accent4 = UIColor(red: 255/255, green: 214/255, blue: 0/255, alpha: 1)
}
// amber
open class amber: ColorPalette {
public static let lighten5 = UIColor(red: 255/255, green: 248/255, blue: 225/255, alpha: 1)
public static let lighten4 = UIColor(red: 255/255, green: 236/255, blue: 179/255, alpha: 1)
public static let lighten3 = UIColor(red: 255/255, green: 224/255, blue: 130/255, alpha: 1)
public static let lighten2 = UIColor(red: 255/255, green: 213/255, blue: 79/255, alpha: 1)
public static let lighten1 = UIColor(red: 255/255, green: 202/255, blue: 40/255, alpha: 1)
public static let base = UIColor(red: 255/255, green: 193/255, blue: 7/255, alpha: 1)
public static let darken1 = UIColor(red: 255/255, green: 179/255, blue: 0/255, alpha: 1)
public static let darken2 = UIColor(red: 255/255, green: 160/255, blue: 0/255, alpha: 1)
public static let darken3 = UIColor(red: 255/255, green: 143/255, blue: 0/255, alpha: 1)
public static let darken4 = UIColor(red: 255/255, green: 111/255, blue: 0/255, alpha: 1)
public static let accent1 = UIColor(red: 255/255, green: 229/255, blue: 127/255, alpha: 1)
public static let accent2 = UIColor(red: 255/255, green: 215/255, blue: 64/255, alpha: 1)
public static let accent3 = UIColor(red: 255/255, green: 196/255, blue: 0/255, alpha: 1)
public static let accent4 = UIColor(red: 255/255, green: 171/255, blue: 0/255, alpha: 1)
}
// orange
open class orange: ColorPalette {
public static let lighten5 = UIColor(red: 255/255, green: 243/255, blue: 224/255, alpha: 1)
public static let lighten4 = UIColor(red: 255/255, green: 224/255, blue: 178/255, alpha: 1)
public static let lighten3 = UIColor(red: 255/255, green: 204/255, blue: 128/255, alpha: 1)
public static let lighten2 = UIColor(red: 255/255, green: 183/255, blue: 77/255, alpha: 1)
public static let lighten1 = UIColor(red: 255/255, green: 167/255, blue: 38/255, alpha: 1)
public static let base = UIColor(red: 255/255, green: 152/255, blue: 0/255, alpha: 1)
public static let darken1 = UIColor(red: 251/255, green: 140/255, blue: 0/255, alpha: 1)
public static let darken2 = UIColor(red: 245/255, green: 124/255, blue: 0/255, alpha: 1)
public static let darken3 = UIColor(red: 239/255, green: 108/255, blue: 0/255, alpha: 1)
public static let darken4 = UIColor(red: 230/255, green: 81/255, blue: 0/255, alpha: 1)
public static let accent1 = UIColor(red: 255/255, green: 209/255, blue: 128/255, alpha: 1)
public static let accent2 = UIColor(red: 255/255, green: 171/255, blue: 64/255, alpha: 1)
public static let accent3 = UIColor(red: 255/255, green: 145/255, blue: 0/255, alpha: 1)
public static let accent4 = UIColor(red: 255/255, green: 109/255, blue: 0/255, alpha: 1)
}
// deep orange
open class deepOrange: ColorPalette {
public static let lighten5 = UIColor(red: 251/255, green: 233/255, blue: 231/255, alpha: 1)
public static let lighten4 = UIColor(red: 255/255, green: 204/255, blue: 188/255, alpha: 1)
public static let lighten3 = UIColor(red: 255/255, green: 171/255, blue: 145/255, alpha: 1)
public static let lighten2 = UIColor(red: 255/255, green: 138/255, blue: 101/255, alpha: 1)
public static let lighten1 = UIColor(red: 255/255, green: 112/255, blue: 67/255, alpha: 1)
public static let base = UIColor(red: 255/255, green: 87/255, blue: 34/255, alpha: 1)
public static let darken1 = UIColor(red: 244/255, green: 81/255, blue: 30/255, alpha: 1)
public static let darken2 = UIColor(red: 230/255, green: 74/255, blue: 25/255, alpha: 1)
public static let darken3 = UIColor(red: 216/255, green: 67/255, blue: 21/255, alpha: 1)
public static let darken4 = UIColor(red: 191/255, green: 54/255, blue: 12/255, alpha: 1)
public static let accent1 = UIColor(red: 255/255, green: 158/255, blue: 128/255, alpha: 1)
public static let accent2 = UIColor(red: 255/255, green: 110/255, blue: 64/255, alpha: 1)
public static let accent3 = UIColor(red: 255/255, green: 61/255, blue: 0/255, alpha: 1)
public static let accent4 = UIColor(red: 221/255, green: 44/255, blue: 0/255, alpha: 1)
}
// brown
open class brown: ColorPalette {
public static let lighten5 = UIColor(red: 239/255, green: 235/255, blue: 233/255, alpha: 1)
public static let lighten4 = UIColor(red: 215/255, green: 204/255, blue: 200/255, alpha: 1)
public static let lighten3 = UIColor(red: 188/255, green: 170/255, blue: 164/255, alpha: 1)
public static let lighten2 = UIColor(red: 161/255, green: 136/255, blue: 127/255, alpha: 1)
public static let lighten1 = UIColor(red: 141/255, green: 110/255, blue: 99/255, alpha: 1)
public static let base = UIColor(red: 121/255, green: 85/255, blue: 72/255, alpha: 1)
public static let darken1 = UIColor(red: 109/255, green: 76/255, blue: 65/255, alpha: 1)
public static let darken2 = UIColor(red: 93/255, green: 64/255, blue: 55/255, alpha: 1)
public static let darken3 = UIColor(red: 78/255, green: 52/255, blue: 46/255, alpha: 1)
public static let darken4 = UIColor(red: 62/255, green: 39/255, blue: 35/255, alpha: 1)
}
// grey
open class grey: ColorPalette {
public static let lighten5 = UIColor(red: 250/255, green: 250/255, blue: 250/255, alpha: 1)
public static let lighten4 = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
public static let lighten3 = UIColor(red: 238/255, green: 238/255, blue: 238/255, alpha: 1)
public static let lighten2 = UIColor(red: 224/255, green: 224/255, blue: 224/255, alpha: 1)
public static let lighten1 = UIColor(red: 189/255, green: 189/255, blue: 189/255, alpha: 1)
public static let base = UIColor(red: 158/255, green: 158/255, blue: 158/255, alpha: 1)
public static let darken1 = UIColor(red: 117/255, green: 117/255, blue: 117/255, alpha: 1)
public static let darken2 = UIColor(red: 97/255, green: 97/255, blue: 97/255, alpha: 1)
public static let darken3 = UIColor(red: 66/255, green: 66/255, blue: 66/255, alpha: 1)
public static let darken4 = UIColor(red: 33/255, green: 33/255, blue: 33/255, alpha: 1)
}
// blue grey
open class blueGrey: ColorPalette {
public static let lighten5 = UIColor(red: 236/255, green: 239/255, blue: 241/255, alpha: 1)
public static let lighten4 = UIColor(red: 207/255, green: 216/255, blue: 220/255, alpha: 1)
public static let lighten3 = UIColor(red: 176/255, green: 190/255, blue: 197/255, alpha: 1)
public static let lighten2 = UIColor(red: 144/255, green: 164/255, blue: 174/255, alpha: 1)
public static let lighten1 = UIColor(red: 120/255, green: 144/255, blue: 156/255, alpha: 1)
public static let base = UIColor(red: 96/255, green: 125/255, blue: 139/255, alpha: 1)
public static let darken1 = UIColor(red: 84/255, green: 110/255, blue: 122/255, alpha: 1)
public static let darken2 = UIColor(red: 69/255, green: 90/255, blue: 100/255, alpha: 1)
public static let darken3 = UIColor(red: 55/255, green: 71/255, blue: 79/255, alpha: 1)
public static let darken4 = UIColor(red: 38/255, green: 50/255, blue: 56/255, alpha: 1)
}
}
================================================
FILE: Sources/iOS/Data/DataSourceItem.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public struct DataSourceItem {
/// Stores an the data for the item.
public var data: Any?
/// Width for horizontal scroll direction.
public var width: CGFloat?
/// Height for vertical scroll direction.
public var height: CGFloat?
/**
Initializer.
- Parameter data: A reference to an Any that is associated
with a width or height.
- Parameter width: The width for the horizontal scroll direction.
- Parameter height: The height for the vertical scroll direction.
*/
public init(data: Any? = nil, width: CGFloat? = nil, height: CGFloat? = nil) {
self.data = data
self.width = width
self.height = height
}
}
================================================
FILE: Sources/iOS/Device/Device.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(DeviceModel)
public enum DeviceModel: Int {
case iPodTouch5
case iPodTouch6
case iPhone4
case iPhone4s
case iPhone5
case iPhone5c
case iPhone5s
case iPhone6
case iPhone6Plus
case iPhone6s
case iPhone6sPlus
case iPhone7
case iPhone7Plus
case iPhone8
case iPhone8Plus
case iPhoneX
case iPhoneXS
case iPhoneXSMax
case iPhoneXR
case iPhoneSE
case iPad2
case iPad3
case iPad4
case iPadAir
case iPadAir2
case iPadMini
case iPadMini2
case iPadMini3
case iPadMini4
case iPadPro
case iPadProLarge
case iPad5
case iPadPro2
case iPadProLarge2
case iPad6
case iPadPro3 //iPad Pro (11-inch)
case iPadProLarge3 //iPad Pro (12.9-inch) (3rd generation)
case appleTv
case appleTv4k
case homePod
case simulator
case unknown
}
public struct Device {
/// Gets the Device identifier String.
public static var identifier: String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { (identifier, element) in
guard let value = element.value as? Int8, value != 0 else {
return identifier
}
return identifier + String(UnicodeScalar(UInt8(value)))
}
return identifier
}
/// Gets the model name for the device.
public static var model: DeviceModel {
switch identifier {
case "iPod5,1": return .iPodTouch5
case "iPod7,1": return .iPodTouch6
case "iPhone4,1": return .iPhone4s
case "iPhone5,1", "iPhone5,2": return .iPhone5
case "iPhone5,3", "iPhone5,4": return .iPhone5c
case "iPhone6,1", "iPhone6,2": return .iPhone5s
case "iPhone7,2": return .iPhone6
case "iPhone7,1": return .iPhone6Plus
case "iPhone8,1": return .iPhone6s
case "iPhone8,2": return .iPhone6sPlus
case "iPhone8,3", "iPhone8,4": return .iPhoneSE
case "iPhone9,1", "iPhone9,3": return .iPhone7
case "iPhone9,2", "iPhone9,4": return .iPhone7Plus
case "iPhone10,1", "iPhone10,4": return .iPhone8
case "iPhone10,2", "iPhone10,5": return .iPhone8Plus
case "iPhone10,3","iPhone10,6": return .iPhoneX
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return .iPad2
case "iPad3,1", "iPad3,2", "iPad3,3": return .iPad3
case "iPad3,4", "iPad3,5", "iPad3,6": return .iPad4
case "iPad4,1", "iPad4,2", "iPad4,3": return .iPadAir
case "iPad5,3", "iPad5,4": return .iPadAir2
case "iPad2,5", "iPad2,6", "iPad2,7": return .iPadMini
case "iPad4,4", "iPad4,5", "iPad4,6": return .iPadMini2
case "iPad4,7", "iPad4,8", "iPad4,9": return .iPadMini3
case "iPad5,1", "iPad5,2": return .iPadMini4
case "iPad6,3", "iPad6,4": return .iPadPro
case "iPad6,7", "iPad6,8": return .iPadProLarge
case "iPad6,11", "iPad6,12": return .iPad5
case "iPad7,3", "iPad7,4": return .iPadPro2
case "iPad7,1", "iPad7,2": return .iPadProLarge2
case "iPad7,5", "iPad7,6": return .iPad6
case "i386", "x86_64": return .simulator
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return .iPhone4
case "iPhone11,2": return .iPhoneXS
case "iPhone11,4", "iPhone11,6": return .iPhoneXSMax
case "iPhone11,8": return .iPhoneXR
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return .iPadPro3
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return .iPadProLarge3
case "AppleTV5,3": return .appleTv
case "AppleTV6,2": return .appleTv4k
case "AudioAccessory1,1": return .homePod
default: return .unknown
}
}
/// Retrieves the current device type.
public static var userInterfaceIdiom: UIUserInterfaceIdiom {
return UIDevice.current.userInterfaceIdiom
}
}
public func ==(lhs: DeviceModel, rhs: DeviceModel) -> Bool {
return lhs.rawValue == rhs.rawValue
}
public func !=(lhs: DeviceModel, rhs: DeviceModel) -> Bool {
return lhs.rawValue != rhs.rawValue
}
================================================
FILE: Sources/iOS/Dialogs/Dialog.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc
public protocol DialogDelegate {
/**
A delegation method that is executed when the Dialog is cancelled through tapping background.
- Parameter _ dialog: A Dialog.
*/
@objc
optional func dialogDidCancel(_ dialog: Dialog)
/**
A delegation method that is executed when the Dialog will appear.
- Parameter _ dialog: A Dialog.
*/
@objc
optional func dialogWillAppear(_ dialog: Dialog)
/**
A delegation method that is executed when the Dialog did disappear.
- Parameter _ dialog: A Dialog.
*/
@objc
optional func dialogDidDisappear(_ dialog: Dialog)
/**
A delegation method that is executed to determine if the Dialog should be dismissed.
- Parameter _ dialog: A Dialog.
- Parameter shouldDismiss button: The tapped button. nil if dialog is being
cancelled through tapping background.
- Returns: A Boolean.
*/
@objc
optional func dialog(_ dialog: Dialog, shouldDismiss button: Button?) -> Bool
/**
A delegation method that is executed when the positive button of Dialog is tapped.
- Parameter _ dialog: A Dialog.
- Parameter didTapPositive button: A Button.
*/
@objc
optional func dialog(_ dialog: Dialog, didTapPositive button: Button)
/**
A delegation method that is executed when the negative button of Dialog is tapped.
- Parameter _ dialog: A Dialog.
- Parameter didTapNegative button: A Button.
*/
@objc
optional func dialog(_ dialog: Dialog, didTapNegative button: Button)
/**
A delegation method that is executed when the neutral button of Dialog is tapped.
- Parameter _ dialog: A Dialog.
- Parameter didTapNeutral button: A Button.
*/
@objc
optional func dialog(_ dialog: Dialog, didTapNeutral button: Button)
}
/// A builder for DialogController.
open class Dialog: NSObject {
/// A reference to dialog controller.
public let controller = DialogController()
/// A weak reference to DialogDelegate.
open weak var delegate: DialogDelegate?
/// An empty initializer.
public override init() {
super.init()
/// Set callbacks for delegate.
shouldDismiss(handler: nil)
.positive(nil, handler: nil)
.negative(nil, handler: nil)
.neutral(nil, handler: nil)
.isCancelable(controller.isCancelable, handler: nil)
.willAppear(handler: nil)
.didDisappear(handler: nil)
}
/**
Sets title of the dialog.
- Parameter _ text: A string.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func title(_ text: String?) -> Dialog {
dialogView.titleLabel.text = text
return self
}
/**
Sets details of the dialog.
- Parameter _ text: A string.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func details(_ text: String?) -> Dialog {
dialogView.detailsLabel.text = text
return self
}
/**
Sets title and handler for positive button of dialog.
- Parameter _ title: A string.
- Parameter handler: A closure handling tap.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func positive(_ title: String?, handler: (() -> Void)?) -> Dialog {
dialogView.positiveButton.title = title
controller.didTapPositiveButtonHandler = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.delegate?.dialog?(strongSelf, didTapPositive: strongSelf.controller.dialogView.positiveButton)
handler?()
}
return self
}
/**
Sets title and handler for negative button of dialog.
- Parameter _ title: A string.
- Parameter handler: A closure handling tap.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func negative(_ title: String?, handler: (() -> Void)?) -> Dialog {
dialogView.negativeButton.title = title
controller.didTapNegativeButtonHandler = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.delegate?.dialog?(strongSelf, didTapNegative: strongSelf.controller.dialogView.negativeButton)
handler?()
}
return self
}
/**
Sets title and handler for neutral button of dialog.
- Parameter _ title: A string.
- Parameter handler: A closure handling tap.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func neutral(_ title: String?, handler: (() -> Void)?) -> Dialog {
dialogView.neutralButton.title = title
controller.didTapNeutralButtonHandler = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.delegate?.dialog?(strongSelf, didTapNeutral: strongSelf.controller.dialogView.neutralButton)
handler?()
}
return self
}
/**
Sets cancelability of dialog and handler for when it's cancelled.
- Parameter _ value: A Bool.
- Parameter handler: A closure handling cancellation.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func isCancelable(_ value: Bool, handler: (() -> Void)? = nil) -> Dialog {
controller.isCancelable = value
controller.didCancelHandler = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.delegate?.dialogDidCancel?(strongSelf)
handler?()
}
return self
}
/**
Sets should-dismiss handler of dialog which takes dialogView and tapped
button and returns a boolean indicating if dialog should be dismissed.
- Parameter handler: A closure handling if dialog can be dismissed.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func shouldDismiss(handler: ((DialogView, Button?) -> Bool)?) -> Dialog {
controller.shouldDismissHandler = { [weak self] dialogView, button in
guard let strongSelf = self else { return true }
let d = strongSelf.delegate?.dialog?(strongSelf, shouldDismiss: button) ?? true
let h = handler?(dialogView, button) ?? true
return d && h
}
return self
}
/**
Sets handler for when view controller will appear.
- Parameter handler: A closure handling the event.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func willAppear(handler: (() -> Void)?) -> Dialog {
controller.willAppear = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.delegate?.dialogWillAppear?(strongSelf)
handler?()
}
return self
}
/**
Sets handler for when view controller did disappear.
- Parameter handler: A closure handling the event.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func didDisappear(handler: (() -> Void)?) -> Dialog {
controller.didDisappear = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.delegate?.dialogDidDisappear?(strongSelf)
handler?()
strongSelf.controller.dialog = nil
}
return self
}
/**
Sets dialog delegate.
- Parameter delegate: A DialogDelegate.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func delegate(_ delegate: DialogDelegate) -> Dialog {
self.delegate = delegate
return self
}
/**
Presents dialog modally from given viewController.
- Parameter _ viewController: A UIViewController.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func show(_ viewController: UIViewController) -> Dialog {
controller.dialog = self
viewController.present(controller, animated: true, completion: nil)
return self
}
}
private extension Dialog {
/// Returns dialogView of controller.
var dialogView: DialogView {
return controller.dialogView
}
}
/// A memory reference to companion Dialog instance.
private var DialogKey: UInt8 = 0
private extension DialogController {
/**
A Dialog instance attached to the dialog controller.
This is used to keep Dialog alive throughout the lifespan
of the controller.
*/
var dialog: Dialog? {
get {
return AssociatedObject.get(base: self, key: &DialogKey) {
return nil
}
}
set(value) {
AssociatedObject.set(base: self, key: &DialogKey, value: value)
}
}
}
================================================
FILE: Sources/iOS/Dialogs/DialogController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
/// A UIViewController managing DialogView.
open class DialogController: UIViewController {
/// A reference to dialogView.
public let dialogView = T()
/// A boolean indicating cancelability of dialog when user taps on background.
open var isCancelable = false
/// A reference to did-cancel handler.
open var didCancelHandler: (() -> Void)?
/**
A reference to should-dismiss handler which takes dialogView
and tapped button and returns Boolean indicating if dialog should be dismissed.
*/
open var shouldDismissHandler: ((T, Button?) -> Bool)?
/// A reference to handler for when positiveButton is tapped.
open var didTapPositiveButtonHandler: (() -> Void)?
/// A reference to handler for when negativeButton is tapped.
open var didTapNegativeButtonHandler: (() -> Void)?
/// A reference to handler for when neutralButton is tapped.
open var didTapNeutralButtonHandler: (() -> Void)?
/// A reference to handler for when controller will appear.
open var willAppear: (() -> Void)?
/// A reference to handler for when controller did disappear.
open var didDisappear: (() -> Void)?
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
prepare()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/// Prepares controller for presentation.
open func prepare() {
isMotionEnabled = true
motionTransitionType = .fade
modalPresentationStyle = .overFullScreen
}
open override func viewDidLoad() {
super.viewDidLoad()
prepareView()
prepareDialogView()
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
willAppear?()
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
didDisappear?()
}
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
dialogView.maxSize = CGSize(width: Screen.width * 0.8, height: Screen.height * 0.9)
}
/**
Dismisses dialog.
- Parameter isAnimated: A boolean.
*/
open func dismiss(isAnimated: Bool = true) {
dismiss(isTriggeredByUserInteraction: false, isAnimated: isAnimated)
}
/// Handler for when background scrim is tapped.
@objc
private func didTapBackgroundView() {
guard isCancelable else {
return
}
dismiss(isTriggeredByUserInteraction: true, isAnimated: true)
}
/// Handler for when one of 3 dialog buttons is tapped.
@objc
private func didTapButton(_ sender: Button) {
switch sender {
case dialogView.positiveButton:
didTapPositiveButtonHandler?()
case dialogView.negativeButton:
didTapNegativeButtonHandler?()
case dialogView.neutralButton:
didTapNeutralButtonHandler?()
default:
break
}
dismiss(isTriggeredByUserInteraction: true, isAnimated: true, using: sender)
}
}
private extension DialogController {
/// Prepares view.
func prepareView() {
let v = UIControl()
v.backgroundColor = Color.black.withAlphaComponent(0.33)
v.addTarget(self, action: #selector(didTapBackgroundView), for: .touchUpInside)
view = v
}
/// Prepares dialogView.
func prepareDialogView() {
view.layout(dialogView).center()
dialogView.buttonArea.subviews.forEach {
($0 as? Button)?.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
}
}
}
private extension DialogController {
/**
Dismisses dialog.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the action is
triggered by a user interaction
- Parameter isAnimated: A boolean indicating if the dismissal should be animated.
- Parameter using button: A button triggering the dismissal.
*/
func dismiss(isTriggeredByUserInteraction: Bool, isAnimated: Bool, using button: Button? = nil) {
if isTriggeredByUserInteraction {
guard shouldDismissHandler?(dialogView, button) ?? true else {
return
}
}
presentingViewController?.dismiss(animated: isAnimated, completion: nil)
guard isTriggeredByUserInteraction, nil == button else {
return
}
didCancelHandler?()
}
}
================================================
FILE: Sources/iOS/Dialogs/DialogView.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
private struct Constants {
struct titleArea {
static let insets = UIEdgeInsets(top: 24, left: 24, bottom: 20, right: 24)
}
struct contentArea {
static let insets = UIEdgeInsets(top: 0, left: 24, bottom: 24, right: 24)
static let insetsNoTitle = UIEdgeInsets(top: 20, left: 24, bottom: 24, right: 24)
}
struct buttonArea {
static let insets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
static let insetsStacked = UIEdgeInsets(top: 6, left: 8, bottom: 14, right: 8)
}
struct button {
static let insets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
static let minWidth: CGFloat = 64
static let height: CGFloat = 36
static let interimStacked: CGFloat = 12
static let interim: CGFloat = 8
}
}
private class DialogScrollView: UIScrollView {
/// A weak reference to DialogView.
weak var dialogView: DialogView?
override func layoutSubviews() {
super.layoutSubviews()
dialogView?.layoutDividers()
}
}
open class DialogView: View, Themeable {
/// A container view for title area.
public let titleArea = UIView()
/// A container view for button area.
public let buttonArea = UIView()
/// A container view for content area.
public let contentArea = UIView()
/// A scroll view containing contentArea.
public let scrollView: UIScrollView = DialogScrollView()
/// A UILabel.
public let titleLabel = UILabel()
/// A UILabel.
public let detailsLabel = UILabel()
/// A Button.
public let neutralButton = FlatButton()
/// A Button.
public let positiveButton = FlatButton()
/// A Button.
public let negativeButton = FlatButton()
/// Maximum size of the dialog.
open var maxSize = CGSize(width: 200, height: 300) {
didSet {
invalidateIntrinsicContentSize()
}
}
open override func prepare() {
super.prepare()
depthPreset = .depth5
cornerRadiusPreset = .cornerRadius2
prepareTitleArea()
prepareTitleLabel()
prepareScrollView()
prepareContentArea()
prepareDetailsLabel()
prepareButtonArea()
prepareButtons()
applyCurrentTheme()
}
open override var intrinsicContentSize: CGSize {
return sizeThatFits(maxSize)
}
open override func sizeThatFits(_ size: CGSize) -> CGSize {
var w: CGFloat = 0
func setMaxWidth(_ width: CGFloat) {
w = max(w, width)
w = min(w, size.width)
}
setMaxWidth(titleAreaSizeThatFits(width: size.width).width)
setMaxWidth(buttonAreaSizeThatFits(width: size.width).width)
setMaxWidth(contentAreaSizeThatFits(width: size.width).width)
var h: CGFloat = 0
h += titleAreaSizeThatFits(width: w).height
h += buttonAreaSizeThatFits(width: w).height
h += contentAreaSizeThatFits(width: w).height
h = min(h, size.height)
return CGSize(width: w, height: h)
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutTitleArea()
layoutButtonArea()
layoutContentArea()
layoutScrollView()
layoutDividers()
/// Position button area after having correct sizes.
buttonArea.frame.origin.y = scrollView.frame.maxY
}
/**
Calculates the size for title area that best fits the specified width.
- Parameter width: A CGFloat.
- Returns: Calculated CGSize.
*/
open func titleAreaSizeThatFits(width: CGFloat) -> CGSize {
guard !titleLabel.isEmpty else {
return .zero
}
let insets = Constants.titleArea.insets
var size = titleLabel.sizeThatFits(CGSize(width: width - insets.left - insets.right, height: .greatestFiniteMagnitude))
size.width += insets.left + insets.right
size.height += insets.top + insets.bottom
return size
}
/**
Calculates the size for button area that best fits the specified width.
- Parameter width: A CGFloat.
- Returns: Calculated CGSize.
*/
open func buttonAreaSizeThatFits(width: CGFloat) -> CGSize {
guard !nonHiddenButtons.isEmpty else {
return .zero
}
let isStacked = requiredButtonAreaWidth > width
let buttonsHeight = Constants.button.height * CGFloat(isStacked ? nonHiddenButtons.count : 1)
let buttonsInterim = isStacked ? CGFloat(nonHiddenButtons.count - 1) * Constants.button.interimStacked : 0
let insets = isStacked ? Constants.buttonArea.insetsStacked : Constants.buttonArea.insets
let h = buttonsInterim + buttonsHeight + insets.bottom + insets.top
let w = min(width, isStacked ? requiredButtonAreaWidthForStacked : requiredButtonAreaWidth)
return CGSize(width: w, height: h)
}
/**
Calculates the size for content area that best fits the specified width.
- Parameter width: A CGFloat.
- Returns: Calculated CGSize.
*/
open func contentAreaSizeThatFits(width: CGFloat) -> CGSize {
guard !detailsLabel.isEmpty else {
return .zero
}
let insets = titleLabel.isEmpty ? Constants.contentArea.insetsNoTitle : Constants.contentArea.insets
var size = detailsLabel.sizeThatFits(CGSize(width: width - insets.left - insets.right, height: .greatestFiniteMagnitude))
size.width += insets.left + insets.right
size.height += insets.top + insets.bottom
return size
}
/**
Applies the given theme.
- Parameter theme: A Theme.
*/
open func apply(theme: Theme) {
backgroundColor = theme.surface
titleLabel.textColor = theme.onSurface.withAlphaComponent(0.87)
detailsLabel.textColor = theme.onSurface.withAlphaComponent(0.60)
titleArea.dividerColor = theme.onSurface.withAlphaComponent(0.12)
buttonArea.dividerColor = theme.onSurface.withAlphaComponent(0.12)
}
}
private extension DialogView {
/// Prepares titleArea.
func prepareTitleArea() {
addSubview(titleArea)
titleArea.dividerColor = Color.darkText.dividers
titleArea.dividerThickness = 1
titleArea.dividerAlignment = .bottom
}
/// Prepares titleTitle.
func prepareTitleLabel() {
titleArea.addSubview(titleLabel)
titleLabel.font = Theme.font.bold(with: 20)
titleLabel.textColor = Color.darkText.primary
titleLabel.numberOfLines = 0
}
/// Prepares buttonArea.
func prepareButtonArea() {
addSubview(buttonArea)
buttonArea.dividerColor = Color.darkText.dividers
buttonArea.dividerThickness = 1
buttonArea.dividerAlignment = .top
}
/// Prepares buttons.
func prepareButtons() {
[positiveButton, negativeButton, neutralButton].forEach {
buttonArea.addSubview($0)
$0.titleLabel?.font = Theme.font.medium(with: 14)
$0.contentEdgeInsets = Constants.button.insets
$0.cornerRadiusPreset = .cornerRadius1
}
}
/// Prepares scrollView.
func prepareScrollView() {
(scrollView as! DialogScrollView).dialogView = self
addSubview(scrollView)
}
/// Prepares contentArea.
func prepareContentArea() {
scrollView.addSubview(contentArea)
}
/// Prepares detailsLabel.
func prepareDetailsLabel() {
contentArea.addSubview(detailsLabel)
detailsLabel.font = Theme.font.regular(with: detailsLabel.fontSize)
detailsLabel.numberOfLines = 0
detailsLabel.textColor = Color.darkText.secondary
}
}
private extension DialogView {
/// Layout the titleArea.
func layoutTitleArea() {
let size = CGSize(width: frame.width, height: titleAreaSizeThatFits(width: frame.width).height)
titleArea.frame.size = size
guard !titleLabel.isEmpty else {
return
}
let rect = CGRect(origin: .zero, size: size)
titleLabel.frame = rect.inset(by: Constants.titleArea.insets)
}
/// Layout the buttonArea.
func layoutButtonArea() {
let width = frame.width
buttonArea.frame.size.width = width
buttonArea.frame.size.height = buttonAreaSizeThatFits(width: width).height
let buttons = nonHiddenButtons
guard !buttons.isEmpty else {
return
}
let isStacked = requiredButtonAreaWidth > width
if isStacked {
let insets = Constants.buttonArea.insetsStacked
buttons.forEach {
let w = min($0.optimalWidth, width - insets.left - insets.right)
$0.frame.size = CGSize(width: w, height: Constants.button.height)
$0.frame.origin.x = width - insets.right - w
}
positiveButton.frame.origin.y = insets.top
let belowPositive = positiveButton.isHidden ? insets.top : positiveButton.frame.maxY + Constants.button.interimStacked
negativeButton.frame.origin.y = belowPositive
neutralButton.frame.origin.y = negativeButton.isHidden ? belowPositive : negativeButton.frame.maxY + Constants.button.interimStacked
} else {
let insets = Constants.buttonArea.insets
buttons.forEach {
$0.frame.size = CGSize(width: $0.optimalWidth, height: Constants.button.height)
$0.frame.origin.y = insets.top
}
neutralButton.frame.origin.x = insets.left
positiveButton.frame.origin.x = width - insets.right - positiveButton.frame.width
let maxX = positiveButton.isHidden ? width - insets.right : positiveButton.frame.minX - Constants.button.interim
negativeButton.frame.origin.x = maxX - negativeButton.frame.width
}
}
/// Layout the contentArea.
func layoutContentArea() {
let size = CGSize(width: frame.width, height: contentAreaSizeThatFits(width: frame.width).height)
contentArea.frame.size = size
guard !detailsLabel.isEmpty else {
return
}
let rect = CGRect(origin: .zero, size: size)
let insets = titleArea.frame.height == 0 ? Constants.contentArea.insetsNoTitle : Constants.contentArea.insets
detailsLabel.frame = rect.inset(by: insets)
}
/// Layout the scrollView.
func layoutScrollView() {
let h = titleArea.frame.height + buttonArea.frame.height
let allowed = min(frame.height - h, contentArea.frame.height)
scrollView.frame.size = CGSize(width: frame.width, height: max(allowed, 0))
scrollView.frame.origin.y = titleArea.frame.maxY
scrollView.contentSize = contentArea.frame.size
}
/**
Layout the dividers.
This method is also called (by scrollView) when scrolling happens
*/
func layoutDividers() {
let isScrollable = contentArea.frame.height > scrollView.frame.height
titleArea.isDividerHidden = titleArea.frame.height == 0 || !isScrollable || scrollView.isAtTop
buttonArea.isDividerHidden = buttonArea.frame.height == 0 || !isScrollable || scrollView.isAtBottom
titleArea.layoutDivider()
buttonArea.layoutDivider()
}
}
private extension DialogView {
/// Required width to fit content of buttonArea.
var requiredButtonAreaWidth: CGFloat {
let buttons = nonHiddenButtons
guard !buttons.isEmpty else {
return 0
}
let buttonsWidth: CGFloat = buttons.reduce(0) { $0 + $1.optimalWidth }
let additional: CGFloat = neutralButton.isHidden ? 0 : 8 // additional spacing for neutral button
let insets = Constants.buttonArea.insets
return buttonsWidth + insets.left + insets.right + CGFloat((buttons.count - 1)) * Constants.button.interim + additional
}
/// Required width to fit statcked content of buttonArea.
var requiredButtonAreaWidthForStacked: CGFloat {
let insets = Constants.buttonArea.insetsStacked
return insets.left + insets.right + nonHiddenButtons.reduce(0) {
max($0, $1.optimalWidth)
}
}
/// Non-hidden buttons within buttonArea.
var nonHiddenButtons: [Button] {
positiveButton.isHidden = positiveButton.title(for: .normal)?.isEmpty ?? true
negativeButton.isHidden = negativeButton.title(for: .normal)?.isEmpty ?? true
neutralButton.isHidden = neutralButton.title(for: .normal)?.isEmpty ?? true
return [positiveButton, negativeButton, neutralButton].filter { !$0.isHidden }
}
}
private extension UIScrollView {
/// Checks if scroll view is at the top.
var isAtTop: Bool {
return contentOffset.y <= 0
}
/// Checks if scroll view is at the bottom.
var isAtBottom: Bool {
/// -1 is used to get rid of precision errors
/// make divider appear even when scroll is at the bottom.
return contentOffset.y >= (contentSize.height - frame.height - 1)
}
}
private extension Button {
/// Optimal width for dialog button.
var optimalWidth: CGFloat {
let size = CGSize(width: .greatestFiniteMagnitude, height: Constants.button.height)
return max(Constants.button.minWidth, sizeThatFits(size).width)
}
}
private extension UILabel {
/// Checks if label is empty.
var isEmpty: Bool {
let empty = text?.isEmpty ?? true
isHidden = empty
return empty
}
}
================================================
FILE: Sources/iOS/Divider/Divider.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc(DividerAlignment)
public enum DividerAlignment: Int {
case top
case left
case bottom
case right
}
public struct Divider {
/// A reference to the UIView.
internal weak var view: UIView?
/// A reference to the divider UIView.
internal var line: UIView?
/// A reference to the height.
public var thickness: CGFloat {
didSet {
reload()
}
}
/// A preset wrapper around contentEdgeInsets.
public var contentEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
contentEdgeInsets = EdgeInsetsPresetToValue(preset: contentEdgeInsetsPreset)
}
}
/// A reference to EdgeInsets.
public var contentEdgeInsets = EdgeInsets.zero {
didSet {
reload()
}
}
/// A UIColor.
public var color: UIColor? {
get {
return line?.backgroundColor
}
set(value) {
guard let v = value else {
line?.removeFromSuperview()
line = nil
return
}
if nil == line {
line = UIView()
line?.layer.zPosition = 5000
view?.addSubview(line!)
reload()
}
line?.backgroundColor = v
}
}
/// A reference to the dividerAlignment.
public var alignment = DividerAlignment.bottom {
didSet {
reload()
}
}
/**
Initializer that takes in a UIView.
- Parameter view: A UIView reference.
- Parameter thickness: A CGFloat value.
*/
internal init(view: UIView?, thickness: CGFloat = 1) {
self.view = view
self.thickness = thickness
}
/**
Hides the divier line.
*/
internal var isHidden = false {
didSet {
line?.isHidden = isHidden
}
}
/// Lays out the divider.
public func reload() {
guard let l = line, let v = view else {
return
}
let c = contentEdgeInsets
switch alignment {
case .top:
l.frame = CGRect(x: c.left, y: c.top, width: v.bounds.width - c.left - c.right, height: thickness)
case .bottom:
l.frame = CGRect(x: c.left, y: v.bounds.height - thickness - c.bottom, width: v.bounds.width - c.left - c.right, height: thickness)
case .left:
l.frame = CGRect(x: c.left, y: c.top, width: thickness, height: v.bounds.height - c.top - c.bottom)
case .right:
l.frame = CGRect(x: v.bounds.width - thickness - c.right, y: c.top, width: thickness, height: v.bounds.height - c.top - c.bottom)
}
}
}
/// A memory reference to the Divider instance.
fileprivate var DividerKey: UInt8 = 0
extension UIView {
/// TabBarItem reference.
public private(set) var divider: Divider {
get {
return AssociatedObject.get(base: self, key: &DividerKey) {
return Divider(view: self)
}
}
set(value) {
AssociatedObject.set(base: self, key: &DividerKey, value: value)
}
}
/// A preset wrapper around divider.contentEdgeInsets.
open var dividerContentEdgeInsetsPreset: EdgeInsetsPreset {
get {
return divider.contentEdgeInsetsPreset
}
set(value) {
divider.contentEdgeInsetsPreset = value
}
}
/// A reference to divider.contentEdgeInsets.
open var dividerContentEdgeInsets: EdgeInsets {
get {
return divider.contentEdgeInsets
}
set(value) {
divider.contentEdgeInsets = value
}
}
/// Divider color.
@IBInspectable
open var dividerColor: UIColor? {
get {
return divider.color
}
set(value) {
divider.color = value
}
}
/// Divider visibility.
@IBInspectable
open var isDividerHidden: Bool {
get {
return divider.isHidden
}
set(value) {
divider.isHidden = value
}
}
/// Divider animation.
open var dividerAlignment: DividerAlignment {
get {
return divider.alignment
}
set(value) {
divider.alignment = value
}
}
/// Divider thickness.
@IBInspectable
open var dividerThickness: CGFloat {
get {
return divider.thickness
}
set(value) {
divider.thickness = value
}
}
/// Sets the divider frame.
open func layoutDivider() {
divider.reload()
}
}
================================================
FILE: Sources/iOS/Extension/Material+Array.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
extension Array where Element: Equatable {
/**
Slices a out a segment of an array based on the start
and end positions.
- Parameter start: A start index.
- Parameter end: An end index.
- Returns: A segmented array based on the start and end
indices.
*/
public func slice(start: Int, end: Int?) -> [Element] {
var e = end ?? count - 1
if e >= count {
e = count - 1
}
guard -1 < start else {
fatalError("Range out of bounds for \(start) - \(end ?? 0), should be 0 - \(count).")
}
var diff = abs(e - start)
guard count > diff else {
return self
}
var ret = [Element]()
while -1 < diff {
ret.insert(self[start + diff], at: 0)
diff -= 1
}
return ret
}
}
================================================
FILE: Sources/iOS/Extension/Material+CALayer.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
fileprivate class MaterialLayer {
/// A reference to the CALayer.
fileprivate weak var layer: CALayer?
/// A property that sets the height of the layer's frame.
fileprivate var heightPreset = HeightPreset.default {
didSet {
layer?.height = heightPreset.rawValue
}
}
/// A property that sets the cornerRadius of the backing layer.
fileprivate var cornerRadiusPreset = CornerRadiusPreset.none {
didSet {
layer?.cornerRadius = CornerRadiusPresetToValue(preset: cornerRadiusPreset)
}
}
/// A preset property to set the borderWidth.
fileprivate var borderWidthPreset = BorderWidthPreset.none {
didSet {
layer?.borderWidth = borderWidthPreset.cgFloatValue
}
}
/// A preset property to set the shape.
fileprivate var shapePreset = ShapePreset.none {
didSet {
layer?.layoutShape()
}
}
/// A preset value for Depth.
fileprivate var depthPreset: DepthPreset {
get {
return depth.preset
}
set(value) {
depth.preset = value
}
}
/// Grid reference.
fileprivate var depth = Depth.zero {
didSet {
guard let v = layer else {
return
}
v.shadowOffset = depth.offset.asSize
v.shadowOpacity = depth.opacity
v.shadowRadius = depth.radius
v.layoutShadowPath()
}
}
/// Enables automatic shadowPath sizing.
fileprivate var isShadowPathAutoSizing = false
/**
Initializer that takes in a CALayer.
- Parameter view: A CALayer reference.
*/
fileprivate init(layer: CALayer?) {
self.layer = layer
}
}
fileprivate var MaterialLayerKey: UInt8 = 0
extension CALayer {
/// MaterialLayer Reference.
fileprivate var materialLayer: MaterialLayer {
get {
return AssociatedObject.get(base: self, key: &MaterialLayerKey) {
return MaterialLayer(layer: self)
}
}
set(value) {
AssociatedObject.set(base: self, key: &MaterialLayerKey, value: value)
}
}
/// A property that accesses the frame.origin.x property.
@IBInspectable
open var x: CGFloat {
get {
return frame.origin.x
}
set(value) {
frame.origin.x = value
layoutShadowPath()
}
}
/// A property that accesses the frame.origin.y property.
@IBInspectable
open var y: CGFloat {
get {
return frame.origin.y
}
set(value) {
frame.origin.y = value
layoutShadowPath()
}
}
/// A property that accesses the frame.size.width property.
@IBInspectable
open var width: CGFloat {
get {
return frame.size.width
}
set(value) {
frame.size.width = value
if .none != shapePreset {
frame.size.height = value
layoutShape()
}
layoutShadowPath()
}
}
/// A property that accesses the frame.size.height property.
@IBInspectable
open var height: CGFloat {
get {
return frame.size.height
}
set(value) {
frame.size.height = value
if .none != shapePreset {
frame.size.width = value
layoutShape()
}
layoutShadowPath()
}
}
/// HeightPreset value.
open var heightPreset: HeightPreset {
get {
return materialLayer.heightPreset
}
set(value) {
materialLayer.heightPreset = value
}
}
/**
A property that manages the overall shape for the object. If either the
width or height property is set, the other will be automatically adjusted
to maintain the shape of the object.
*/
open var shapePreset: ShapePreset {
get {
return materialLayer.shapePreset
}
set(value) {
materialLayer.shapePreset = value
}
}
/// A preset value for Depth.
open var depthPreset: DepthPreset {
get {
return depth.preset
}
set(value) {
depth.preset = value
}
}
/// Grid reference.
open var depth: Depth {
get {
return materialLayer.depth
}
set(value) {
materialLayer.depth = value
}
}
/// Enables automatic shadowPath sizing.
@IBInspectable
open var isShadowPathAutoSizing: Bool {
get {
return materialLayer.isShadowPathAutoSizing
}
set(value) {
materialLayer.isShadowPathAutoSizing = value
}
}
/// A property that sets the cornerRadius of the backing layer.
open var cornerRadiusPreset: CornerRadiusPreset {
get {
return materialLayer.cornerRadiusPreset
}
set(value) {
materialLayer.cornerRadiusPreset = value
}
}
/// A preset property to set the borderWidth.
open var borderWidthPreset: BorderWidthPreset {
get {
return materialLayer.borderWidthPreset
}
set(value) {
materialLayer.borderWidthPreset = value
}
}
}
extension CALayer {
/// Manages the layout for the shape of the view instance.
open func layoutShape() {
guard .none != shapePreset else {
return
}
if 0 == bounds.width {
bounds.size.width = bounds.height
}
if 0 == bounds.height {
bounds.size.height = bounds.width
}
guard .circle == shapePreset else {
cornerRadius = 0
return
}
cornerRadius = bounds.width / 2
}
/// Sets the shadow path.
open func layoutShadowPath() {
guard isShadowPathAutoSizing else {
return
}
if case .none = depthPreset.rawValue {
shadowPath = nil
} else if nil == shadowPath {
shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
} else {
animate(.shadow(path: UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath))
}
}
}
================================================
FILE: Sources/iOS/Extension/Material+MotionAnimation.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
public extension MotionAnimation {
/**
Animates the view's current shadow offset to the given one.
- Parameter offset: A CGSize.
- Returns: A MotionAnimation.
*/
static func shadow(offset: Offset) -> MotionAnimation {
return .shadow(offset: offset.asSize)
}
/**
Animates the views shadow offset, opacity, and radius using a DepthPreset.
- Parameter _ preset: A DepthPreset.
*/
static func depth(_ preset: DepthPreset) -> MotionAnimation {
return .depth(DepthPresetToValue(preset: preset).rawValue)
}
/**
Animates the views shadow offset, opacity, and radius using a given Depth.
- Parameter _ preset: A Depth.
*/
static func depth(_ depth: Depth) -> MotionAnimation {
return .depth(depth.rawValue)
}
}
================================================
FILE: Sources/iOS/Extension/Material+NSMutableAttributedString.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
extension NSMutableAttributedString {
/**
Updates a NSAttributedStringKey for a given range.
- Parameter _ name: A NSAttributedStringKey.
- Parameter value: Any type.
- Parameter range: A NSRange.
*/
open func updateAttribute(_ name: NSAttributedString.Key, value: Any, range: NSRange) {
removeAttribute(name, range: range)
addAttribute(name, value: value, range: range)
}
/**
Updates a Dictionary of NSAttributedStringKeys for a given range.
- Parameter _ attrs: A Dictionary of NSAttributedStringKey type keys and Any type values.
- Parameter range: A NSRange.
*/
open func updateAttributes(_ attrs: [NSAttributedString.Key: Any], range: NSRange) {
for (k, v) in attrs {
updateAttribute(k, value: v, range: range)
}
}
/**
Removes a Dictionary of NSAttributedStringKeys for a given range.
- Parameter _ attrs: An Array of attributedStringKeys.
- Parameter range: A NSRange.
*/
open func removeAttributes(_ attrs: [NSAttributedString.Key], range: NSRange) {
for k in attrs {
removeAttribute(k, range: range)
}
}
}
================================================
FILE: Sources/iOS/Extension/Material+String.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
extension String {
/**
:name: trim
*/
public var trimmed: String {
return trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
/**
:name: lines
*/
public var lines: [String] {
return components(separatedBy: CharacterSet.newlines)
}
/**
:name: firstLine
*/
public var firstLine: String? {
return lines.first?.trimmed
}
/**
:name: lastLine
*/
public var lastLine: String? {
return lines.last?.trimmed
}
/**
:name: replaceNewLineCharater
*/
public func replaceNewLineCharater(separator: String = " ") -> String {
return components(separatedBy: CharacterSet.whitespaces).joined(separator: separator).trimmed
}
/**
:name: replacePunctuationCharacters
*/
public func replacePunctuationCharacters(separator: String = "") -> String {
return components(separatedBy: CharacterSet.punctuationCharacters).joined(separator: separator).trimmed
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIButton.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public extension UIButton {
/// Convenience way to change titleLabel font size.
var fontSize: CGFloat {
get {
return titleLabel?.font?.pointSize ?? UIFont.buttonFontSize
}
set(value) {
titleLabel?.font = titleLabel?.font?.withSize(value) ?? UIFont.systemFont(ofSize: value)
}
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIColor.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public extension UIColor {
/**
A convenience initializer that creates color from
argb(alpha red green blue) hexadecimal representation.
- Parameter argb: An unsigned 32 bit integer. E.g 0xFFAA44CC.
*/
convenience init(argb: UInt32) {
let a = argb >> 24
let r = argb >> 16
let g = argb >> 8
let b = argb >> 0
func f(_ v: UInt32) -> CGFloat {
return CGFloat(v & 0xff) / 255
}
self.init(red: f(r), green: f(g), blue: f(b), alpha: f(a))
}
/**
A convenience initializer that creates color from
rgb(red green blue) hexadecimal representation with alpha value 1.
- Parameter rgb: An unsigned 32 bit integer. E.g 0xAA44CC.
*/
convenience init(rgb: UInt32) {
self.init(argb: (0xff000000 as UInt32) | rgb)
}
}
internal extension UIColor {
/// A tuple of the rgba components.
var components: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
getRed(&r, green: &g, blue: &b, alpha: &a)
return (r, g, b, a)
}
/**
Blends given coverColor over this color.
- Parameter with coverColor: A UIColor.
- Returns: Resultant color of blending.
*/
func blend(with coverColor: UIColor) -> UIColor {
/// Blends channels according to https://en.wikipedia.org/wiki/Alpha_compositing (see `over` operator).
func blendChannel(value: CGFloat, bValue: CGFloat, alpha: CGFloat, bAlpha: CGFloat) -> CGFloat {
return ((1 - alpha) * bValue * bAlpha + alpha * value) / (alpha + bAlpha * (1 - alpha))
}
let (r, g, b, a) = coverColor.components
let (bR, bG, bB, bA) = components
let newR = blendChannel(value: r, bValue: bR, alpha: a, bAlpha: bA)
let newG = blendChannel(value: g, bValue: bG, alpha: a, bAlpha: bA)
let newB = blendChannel(value: b, bValue: bB, alpha: a, bAlpha: bA)
let newA = a + bA * (1 - a)
return UIColor(red: newR, green: newG, blue: newB, alpha: newA)
}
/**
Adjusts brightness of the color by given value.
- Parameter by value: A CGFloat value.
- Returns: Adjusted color.
*/
func adjustingBrightness(by value: CGFloat) -> UIColor {
var h: CGFloat = 0
var s: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: h, saturation: s, brightness: (b + value).clamp(0, 1), alpha: 1)
}
/// A lighter version of the color.
var lighter: UIColor {
return adjustingBrightness(by: 0.1)
}
/// A darker version of the color.
var darker: UIColor {
return adjustingBrightness(by: -0.1)
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIFont.swift
================================================
/*
* Copyright (C) 2015 - 2019 and CosmicMind, Inc. .
* All rights reserved.
*
* Authors:
* Daniel Dahan ,
* Orkhan Alikhanov
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
extension UIFont {
/**
Calculates a CGSize value based on a width and length of a string with a
given UIFont.
- Parameter string: A String.
- Parameter constrainedTo width: A CGFloat.
- Returns a CGSize.
*/
open func stringSize(string: String, constrainedTo width: CGFloat) -> CGSize {
return string.boundingRect(with: CGSize(width: width, height: CGFloat(Double.greatestFiniteMagnitude)),
options: .usesLineFragmentOrigin,
attributes: [.font: self],
context: nil).size
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIImage.swift
================================================
/*
* Copyright (C) 2015 - 2019 and CosmicMind, Inc. .
* All rights reserved.
*
* Authors:
* Daniel Dahan ,
* Orkhan Alikhanov
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
import Accelerate
import Motion
@objc(ImageFormat)
public enum ImageFormat: Int {
case png
case jpeg
}
extension UIImage {
/// Width of the UIImage.
open var width: CGFloat {
return size.width
}
/// Height of the UIImage.
open var height: CGFloat {
return size.height
}
}
extension UIImage {
/**
Resizes an image based on a given width.
- Parameter toWidth w: A width value.
- Returns: An optional UIImage.
*/
open func resize(toWidth w: CGFloat) -> UIImage? {
return internalResize(toWidth: w)
}
/**
Resizes an image based on a given height.
- Parameter toHeight h: A height value.
- Returns: An optional UIImage.
*/
open func resize(toHeight h: CGFloat) -> UIImage? {
return internalResize(toHeight: h)
}
/**
Internally resizes the image.
- Parameter toWidth tw: A width.
- Parameter toHeight th: A height.
- Returns: An optional UIImage.
*/
private func internalResize(toWidth tw: CGFloat = 0, toHeight th: CGFloat = 0) -> UIImage? {
var w: CGFloat?
var h: CGFloat?
if 0 < tw {
h = height * tw / width
} else if 0 < th {
w = width * th / height
}
let g: UIImage?
let t: CGRect = CGRect(x: 0, y: 0, width: w ?? tw, height: h ?? th)
UIGraphicsBeginImageContextWithOptions(t.size, false, Screen.scale)
draw(in: t, blendMode: .normal, alpha: 1)
g = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return g
}
}
extension UIImage {
/**
Creates a new image with the passed in color.
- Parameter color: The UIColor to create the image from.
- Returns: A UIImage that is the color passed in.
*/
open func tint(with color: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, Screen.scale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0.0, y: -size.height)
context.setBlendMode(.multiply)
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
context.clip(to: rect, mask: cgImage!)
color.setFill()
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image?.withRenderingMode(.alwaysOriginal)
}
}
extension UIImage {
/**
Creates an Image that is a color.
- Parameter color: The UIColor to create the image from.
- Parameter size: The size of the image to create.
- Returns: A UIImage that is the color passed in.
*/
open class func image(with color: UIColor, size: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, Screen.scale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0.0, y: -size.height)
context.setBlendMode(.multiply)
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
color.setFill()
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image?.withRenderingMode(.alwaysOriginal)
}
}
extension UIImage {
/**
Crops an image to a specified width and height.
- Parameter toWidth tw: A specified width.
- Parameter toHeight th: A specified height.
- Returns: An optional UIImage.
*/
open func crop(toWidth tw: CGFloat, toHeight th: CGFloat) -> UIImage? {
let g: UIImage?
let b = width > height
let s: CGFloat = b ? th / height : tw / width
let t: CGSize = CGSize(width: tw, height: th)
let w = width * s
let h = height * s
UIGraphicsBeginImageContext(t)
draw(in: b ? CGRect(x: -1 * (w - t.width) / 2, y: 0, width: w, height: h) : CGRect(x: 0, y: -1 * (h - t.height) / 2, width: w, height: h), blendMode: .normal, alpha: 1)
g = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return g
}
}
extension UIImage {
/**
Creates a clear image.
- Returns: A UIImage that is clear.
*/
open class func clear(size: CGSize = CGSize(width: 16, height: 16)) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
extension UIImage {
/**
Asynchronously load images with a completion block.
- Parameter URL: A URL destination to fetch the image from.
- Parameter completion: A completion block that is executed once the image
has been retrieved.
*/
open class func contentsOfURL(url: URL, completion: @escaping ((UIImage?, Error?) -> Void)) {
URLSession.shared.dataTask(with: URLRequest(url: url)) { [completion = completion] (data: Data?, response: URLResponse?, error: Error?) in
Motion.async {
if let v = error {
completion(nil, v)
} else if let v = data {
completion(UIImage(data: v), nil)
}
}
}.resume()
}
}
extension UIImage {
/**
Adjusts the orientation of the image from the capture orientation.
This is an issue when taking images, the capture orientation is not set correctly
when using Portrait.
- Returns: An optional UIImage if successful.
*/
open func adjustOrientation() -> UIImage? {
guard .up != imageOrientation else {
return self
}
var transform: CGAffineTransform = .identity
// Rotate if Left, Right, or Down.
switch imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat(Double.pi))
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat(Double.pi / 2))
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: -CGFloat(Double.pi / 2))
default:break
}
// Flip if mirrored.
switch imageOrientation {
case .upMirrored, .downMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .leftMirrored, .rightMirrored:
transform = transform.translatedBy(x: size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
default:break
}
// Draw the underlying cgImage with the calculated transform.
guard let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage!.bitsPerComponent, bytesPerRow: 0, space: cgImage!.colorSpace!, bitmapInfo: cgImage!.bitmapInfo.rawValue) else {
return nil
}
context.concatenate(transform)
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
context.draw(cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
context.draw(cgImage!, in: CGRect(origin: .zero, size: size))
}
guard let cgImage = context.makeImage() else {
return nil
}
return UIImage(cgImage: cgImage)
}
}
/**
Creates an effect buffer for images that already have effects.
- Parameter context: A CGContext.
- Returns: vImage_Buffer.
*/
fileprivate func createEffectBuffer(context: CGContext) -> vImage_Buffer {
let data = context.data
let width = vImagePixelCount(context.width)
let height = vImagePixelCount(context.height)
let rowBytes = context.bytesPerRow
return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
}
extension UIImage {
/**
Applies a blur effect to a UIImage.
- Parameter radius: The radius of the blur effect.
- Parameter tintColor: The color used for the blur effect (optional).
- Parameter saturationDeltaFactor: The delta factor for the saturation of the blur effect.
- Returns: a UIImage.
*/
open func blur(radius: CGFloat = 0, tintColor: UIColor? = nil, saturationDeltaFactor: CGFloat = 0) -> UIImage? {
var effectImage = self
let screenScale = Screen.scale
let imageRect = CGRect(origin: .zero, size: size)
let hasBlur = radius > CGFloat(Float.ulpOfOne)
let hasSaturationChange = abs(saturationDeltaFactor - 1.0) > CGFloat(Float.ulpOfOne)
if hasBlur || hasSaturationChange {
UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
let inContext = UIGraphicsGetCurrentContext()!
inContext.scaleBy(x: 1.0, y: -1.0)
inContext.translateBy(x: 0, y: -size.height)
inContext.draw(cgImage!, in: imageRect)
var inBuffer = createEffectBuffer(context: inContext)
UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
let outContext = UIGraphicsGetCurrentContext()!
var outBuffer = createEffectBuffer(context: outContext)
if hasBlur {
let a = sqrt(2 * .pi)
let b = CGFloat(a) / 4
let c = radius * screenScale
let d = c * 3.0 * b
var e = UInt32(floor(d + 0.5))
if 1 != e % 2 {
e += 1 // force radius to be odd so that the three box-blur methodology works.
}
let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, e, e, nil, imageEdgeExtendFlags)
vImageBoxConvolve_ARGB8888(&outBuffer, &inBuffer, nil, 0, 0, e, e, nil, imageEdgeExtendFlags)
vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, e, e, nil, imageEdgeExtendFlags)
}
var effectImageBuffersAreSwapped = false
if hasSaturationChange {
let s = saturationDeltaFactor
let floatingPointSaturationMatrix: [CGFloat] = [
0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
0, 0, 0, 1
]
let divisor: CGFloat = 256
let matrixSize = floatingPointSaturationMatrix.count
var saturationMatrix = [Int16](repeating: 0, count: matrixSize)
for i in 0...
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public extension UILabel {
/// Convenience way to change font size.
var fontSize: CGFloat {
get {
return font?.pointSize ?? UIFont.labelFontSize
}
set(value) {
font = font?.withSize(value) ?? UIFont.systemFont(ofSize: value)
}
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIView.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
extension UIView {
/// A property that accesses the backing layer's shadow
@objc
open var shadowColor: UIColor? {
get {
guard let v = layer.shadowColor else {
return nil
}
return UIColor(cgColor: v)
}
set(value) {
layer.shadowColor = value?.cgColor
}
}
/// A property that accesses the layer.borderColor property.
@objc
open var borderColor: UIColor? {
get {
guard let v = layer.borderColor else {
return nil
}
return UIColor(cgColor: v)
}
set(value) {
layer.borderColor = value?.cgColor
}
}
/// HeightPreset value.
open var heightPreset: HeightPreset {
get {
return layer.heightPreset
}
set(value) {
layer.heightPreset = value
}
}
/**
A property that manages the overall shape for the object. If either the
width or height property is set, the other will be automatically adjusted
to maintain the shape of the object.
*/
@objc
open var shapePreset: ShapePreset {
get {
return layer.shapePreset
}
set(value) {
layer.shapePreset = value
}
}
/// A preset value for Depth.
open var depthPreset: DepthPreset {
get {
return layer.depthPreset
}
set(value) {
layer.depthPreset = value
}
}
/// Depth reference.
open var depth: Depth {
get {
return layer.depth
}
set(value) {
layer.depth = value
}
}
/// Enables automatic shadowPath sizing.
@IBInspectable
@objc
open var isShadowPathAutoSizing: Bool {
get {
return layer.isShadowPathAutoSizing
}
set(value) {
layer.isShadowPathAutoSizing = value
}
}
/// A property that sets the cornerRadius of the backing layer.
@objc
open var cornerRadiusPreset: CornerRadiusPreset {
get {
return layer.cornerRadiusPreset
}
set(value) {
layer.cornerRadiusPreset = value
}
}
/// A preset property to set the borderWidth.
@objc
open var borderWidthPreset: BorderWidthPreset {
get {
return layer.borderWidthPreset
}
set(value) {
layer.borderWidthPreset = value
}
}
}
internal extension UIView {
/// Manages the layout for the shape of the view instance.
func layoutShape() {
layer.layoutShape()
}
/// Sets the shadow path.
func layoutShadowPath() {
layer.layoutShadowPath()
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIViewController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
internal extension UIViewController {
/**
Finds a view controller with a given type based on
the view controller subclass.
- Returns: An optional of type T.
*/
func traverseViewControllerHierarchyForClassType() -> T? {
var v: UIViewController? = self
while nil != v {
if v is T {
return v as? T
}
v = v?.parent
}
return Application.rootViewController?.traverseTransitionViewControllerHierarchyForClassType()
}
/**
Traverses the child view controllers to find the correct view controller type T.
- Returns: An optional of type T.
*/
func traverseTransitionViewControllerHierarchyForClassType() -> T? {
if let v = self as? T {
return v
} else if let v = self as? TransitionController {
return v.rootViewController.traverseTransitionViewControllerHierarchyForClassType()
}
return nil
}
}
================================================
FILE: Sources/iOS/Extension/Material+UIWindow.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
extension UIWindow {
/**
Captures a screenshot of the contents in the apps keyWindow.
- Returns: An optional UIImage.
*/
open func capture() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(frame.size, isOpaque, Screen.scale)
layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
================================================
FILE: Sources/iOS/FABMenu/FABMenu.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(FABMenuItemTitleLabelPosition)
public enum FABMenuItemTitleLabelPosition: Int {
case left
case right
}
@objc(FABMenuDirection)
public enum FABMenuDirection: Int {
case up
case down
case left
case right
}
open class FABMenuItem: View {
/// A reference to the titleLabel.
public let titleLabel = UILabel()
/// The titleLabel side.
open var titleLabelPosition = FABMenuItemTitleLabelPosition.left
/// A reference to the fabButton.
public let fabButton = FABButton()
open override func prepare() {
super.prepare()
backgroundColor = nil
prepareFABButton()
prepareTitleLabel()
}
/// A reference to the titleLabel text.
open var title: String? {
get {
return titleLabel.text
}
set(value) {
titleLabel.text = value
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
guard let t = title, 0 < t.utf16.count else {
titleLabel.removeFromSuperview()
return
}
if nil == titleLabel.superview {
addSubview(titleLabel)
}
}
}
extension FABMenuItem {
/// Shows the titleLabel.
open func showTitleLabel() {
let interimSpace = InterimSpacePresetToValue(preset: .interimSpace6)
titleLabel.sizeToFit()
titleLabel.frame.size.width += 1.5 * interimSpace
titleLabel.frame.size.height += interimSpace / 2
titleLabel.frame.origin.y = (bounds.height - titleLabel.bounds.height) / 2
switch titleLabelPosition {
case .left:
titleLabel.frame.origin.x = -titleLabel.bounds.width - interimSpace
case .right:
titleLabel.frame.origin.x = frame.bounds.width + interimSpace
}
titleLabel.alpha = 0
titleLabel.isHidden = false
UIView.animate(withDuration: 0.25, animations: { [weak self] in
guard let `self` = self else {
return
}
`self`.titleLabel.alpha = 1
})
}
/// Hides the titleLabel.
open func hideTitleLabel() {
titleLabel.isHidden = true
}
}
extension FABMenuItem {
/// Prepares the fabButton.
fileprivate func prepareFABButton() {
layout(fabButton).edges()
}
/// Prepares the titleLabel.
fileprivate func prepareTitleLabel() {
titleLabel.font = Theme.font.regular(with: 14)
titleLabel.textAlignment = .center
titleLabel.backgroundColor = .white
titleLabel.depthPreset = fabButton.depthPreset
titleLabel.cornerRadiusPreset = .cornerRadius1
}
}
@objc(FABMenuDelegate)
public protocol FABMenuDelegate {
/**
A delegation method that is executed to determine whether fabMenu should open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuShouldOpen(fabMenu: FABMenu) -> Bool
/**
A delegation method that is execited when the fabMenu will open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuWillOpen(fabMenu: FABMenu)
/**
A delegation method that is execited when the fabMenu did open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuDidOpen(fabMenu: FABMenu)
/**
A delegation method that is executed to determine whether fabMenu should close.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuShouldClose(fabMenu: FABMenu) -> Bool
/**
A delegation method that is execited when the fabMenu will close.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuWillClose(fabMenu: FABMenu)
/**
A delegation method that is execited when the fabMenu did close.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuDidClose(fabMenu: FABMenu)
/**
A delegation method that is executed when the user taps while
the menu is opened.
- Parameter fabMenu: A FABMenu.
- Parameter tappedAt point: A CGPoint.
- Parameter isOutside: A boolean indicating whether the tap
was outside the menu button area.
*/
@objc
optional func fabMenu(fabMenu: FABMenu, tappedAt point: CGPoint, isOutside: Bool)
}
@objc(FABMenu)
open class FABMenu: View {
/// A flag to avoid the double tap.
fileprivate var shouldAvoidHitTest = false
/// A reference to the SpringAnimation object.
let spring = SpringAnimation()
open var fabMenuDirection: FABMenuDirection {
get {
switch spring.springDirection {
case .up:
return .up
case .down:
return .down
case .left:
return .left
case .right:
return .right
}
}
set(value) {
switch value {
case .up:
spring.springDirection = .up
case .down:
spring.springDirection = .down
case .left:
spring.springDirection = .left
case .right:
spring.springDirection = .right
}
layoutSubviews()
}
}
/// A reference to the base FABButton.
open var fabButton: FABButton? {
didSet {
oldValue?.removeFromSuperview()
guard let v = fabButton else {
return
}
addSubview(v)
v.addTarget(self, action: #selector(handleFABButton(button:)), for: .touchUpInside)
}
}
/// An open handler for the FABButton.
open var handleFABButtonCallback: ((UIButton) -> Void)?
/// An internal handler for the open function.
internal var handleOpenCallback: (() -> Void)?
/// An internal handler for the close function.
internal var handleCloseCallback: (() -> Void)?
/// An internal handler for the completion function.
internal var handleCompletionCallback: ((UIView) -> Void)?
/// Size of FABMenuItems.
open var fabMenuItemSize: CGSize {
get {
return spring.itemSize
}
set(value) {
spring.itemSize = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return spring.interimSpacePreset
}
set(value) {
spring.interimSpacePreset = value
}
}
/// The space between views.
open var interimSpace: InterimSpace {
get {
return spring.interimSpace
}
set(value) {
spring.interimSpace = value
}
}
/// A boolean indicating if the menu is open or not.
open var isOpened: Bool {
get {
return spring.isOpened
}
set(value) {
spring.isOpened = value
}
}
/// A boolean indicating if the menu is enabled.
open var isEnabled: Bool {
get {
return spring.isEnabled
}
set(value) {
spring.isEnabled = value
}
}
/// An optional delegation handler.
open weak var delegate: FABMenuDelegate?
/// A reference to the FABMenuItems
open var fabMenuItems: [FABMenuItem] {
get {
return spring.views as! [FABMenuItem]
}
set(value) {
for v in spring.views {
v.removeFromSuperview()
}
for v in value {
addSubview(v)
}
spring.views = value
}
}
open override func layoutSubviews() {
super.layoutSubviews()
fabButton?.frame = bounds
fabButton?.setNeedsLayout()
fabButton?.layoutIfNeeded()
spring.baseSize = bounds.size
}
open override func prepare() {
super.prepare()
backgroundColor = nil
interimSpacePreset = .interimSpace6
}
}
extension FABMenu {
/**
Open the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func open(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIView.AnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
open(isTriggeredByUserInteraction: false, duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
/**
Open the Menu component with animation options.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the
state was changed by a user interaction, true if yes, false otherwise.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func open(isTriggeredByUserInteraction: Bool, duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIView.AnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
if isTriggeredByUserInteraction && false == delegate?.fabMenuShouldOpen?(fabMenu: self) {
return
}
handleOpenCallback?()
if isTriggeredByUserInteraction {
delegate?.fabMenuWillOpen?(fabMenu: self)
}
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations) { [weak self, isTriggeredByUserInteraction = isTriggeredByUserInteraction, completion = completion] (view) in
guard let `self` = self else {
return
}
(view as? FABMenuItem)?.showTitleLabel()
if isTriggeredByUserInteraction && view == self.fabMenuItems.last {
self.delegate?.fabMenuDidOpen?(fabMenu: self)
}
completion?(view)
self.handleCompletionCallback?(view)
}
}
/**
Close the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func close(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIView.AnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
close(isTriggeredByUserInteraction: false, duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
/**
Close the Menu component with animation options.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the
state was changed by a user interaction, true if yes, false otherwise.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func close(isTriggeredByUserInteraction: Bool, duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIView.AnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
if isTriggeredByUserInteraction && false == delegate?.fabMenuShouldClose?(fabMenu: self) {
return
}
handleCloseCallback?()
if isTriggeredByUserInteraction {
delegate?.fabMenuWillClose?(fabMenu: self)
}
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations) { [weak self, isTriggeredByUserInteraction = isTriggeredByUserInteraction, completion = completion] (view) in
guard let `self` = self else {
return
}
(view as? FABMenuItem)?.hideTitleLabel()
if isTriggeredByUserInteraction && view == self.fabMenuItems.last {
self.delegate?.fabMenuDidClose?(fabMenu: self)
}
completion?(view)
self.handleCompletionCallback?(view)
}
}
}
extension FABMenu {
/**
Handles the hit test for the Menu and views outside of the Menu bounds.
- Parameter _ point: A CGPoint.
- Parameter with event: An optional UIEvent.
- Returns: An optional UIView.
*/
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard isOpened, isEnabled else {
return super.hitTest(point, with: event)
}
for v in subviews {
let p = v.convert(point, from: self)
if v.bounds.contains(p) {
if !shouldAvoidHitTest {
delegate?.fabMenu?(fabMenu: self, tappedAt: point, isOutside: false)
}
shouldAvoidHitTest = !shouldAvoidHitTest
return v.hitTest(p, with: event)
}
}
delegate?.fabMenu?(fabMenu: self, tappedAt: point, isOutside: true)
close(isTriggeredByUserInteraction: true)
return super.hitTest(point, with: event)
}
}
extension FABMenu {
/**
Handler to toggle the FABMenu opened or closed.
- Parameter button: A UIButton.
*/
@objc
fileprivate func handleFABButton(button: UIButton) {
guard nil == handleFABButtonCallback else {
handleFABButtonCallback?(button)
return
}
guard isOpened else {
open(isTriggeredByUserInteraction: true)
return
}
close(isTriggeredByUserInteraction: true)
}
}
================================================
FILE: Sources/iOS/FABMenu/FABMenuController.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public enum FABMenuBacking {
case none
case fade
case blur
}
extension UIViewController {
/**
A convenience property that provides access to the FABMenuController.
This is the recommended method of accessing the FABMenuController
through child UIViewControllers.
*/
public var fabMenuController: FABMenuController? {
return traverseViewControllerHierarchyForClassType()
}
}
open class FABMenuController: TransitionController {
/// Reference to the MenuView.
@IBInspectable
open var fabMenu = FABMenu()
/// A FABMenuBacking value type.
open var fabMenuBacking = FABMenuBacking.blur
/// The fabMenuBacking UIBlurEffectStyle.
open var fabMenuBackingBlurEffectStyle = UIBlurEffect.Style.light
/// A reference to the blurView.
open fileprivate(set) var blurView: UIView?
open override func layoutSubviews() {
super.layoutSubviews()
rootViewController.view.frame = view.bounds
}
open override func prepare() {
super.prepare()
prepareFABMenu()
}
}
extension FABMenuController: FABMenuDelegate {}
fileprivate extension FABMenuController {
/// Prepares the fabMenu.
func prepareFABMenu() {
fabMenu.delegate = self
fabMenu.layer.zPosition = 1000
fabMenu.handleFABButtonCallback = { [weak self] in
self?.handleFABButtonCallback(button: $0)
}
fabMenu.handleOpenCallback = { [weak self] in
self?.handleOpenCallback()
}
fabMenu.handleCloseCallback = { [weak self] in
self?.handleCloseCallback()
}
fabMenu.handleCompletionCallback = { [weak self] in
self?.handleCompletionCallback(view: $0)
}
view.addSubview(fabMenu)
}
}
fileprivate extension FABMenuController {
/// Shows the fabMenuBacking.
func showFabMenuBacking() {
showFade()
showBlurView()
}
/// Hides the fabMenuBacking.
func hideFabMenuBacking() {
hideFade()
hideBlurView()
}
/// Shows the blurView.
func showBlurView() {
guard .blur == fabMenuBacking else {
return
}
guard !fabMenu.isOpened, fabMenu.isEnabled else {
return
}
guard nil == blurView else {
return
}
let blur = UIVisualEffectView(effect: UIBlurEffect(style: fabMenuBackingBlurEffectStyle))
blurView = UIView()
blurView?.layout(blur).edges()
view.layout(blurView!).edges()
view.bringSubviewToFront(fabMenu)
}
/// Hides the blurView.
func hideBlurView() {
guard .blur == fabMenuBacking else {
return
}
guard fabMenu.isOpened, fabMenu.isEnabled else {
return
}
blurView?.removeFromSuperview()
blurView = nil
}
/// Shows the fade.
func showFade() {
guard .fade == fabMenuBacking else {
return
}
guard !fabMenu.isOpened, fabMenu.isEnabled else {
return
}
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.rootViewController.view.alpha = 0.15
})
}
/// Hides the fade.
func hideFade() {
guard .fade == fabMenuBacking else {
return
}
guard fabMenu.isOpened, fabMenu.isEnabled else {
return
}
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.rootViewController.view.alpha = 1
})
}
}
fileprivate extension FABMenuController {
/**
Handler to toggle the FABMenu opened or closed.
- Parameter button: A UIButton.
*/
func handleFABButtonCallback(button: UIButton) {
guard fabMenu.isOpened else {
fabMenu.open(isTriggeredByUserInteraction: true)
return
}
fabMenu.close(isTriggeredByUserInteraction: true)
}
/// Handler for when the FABMenu.open function is called.
func handleOpenCallback() {
isUserInteractionEnabled = false
showFabMenuBacking()
}
/// Handler for when the FABMenu.close function is called.
func handleCloseCallback() {
isUserInteractionEnabled = false
hideFabMenuBacking()
}
/**
Completion handler for FABMenu open and close calls.
- Parameter view: A UIView.
*/
func handleCompletionCallback(view: UIView) {
if view == fabMenu.fabMenuItems.last {
isUserInteractionEnabled = true
}
}
}
================================================
FILE: Sources/iOS/Font/DynamicFontType.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(DynamicFontTypeDelegate)
public protocol DynamicFontTypeDelegate {
/**
A delegation method that is executed when the dynamic type
is changed.
- Parameter dynamicFontType: A DynamicFontType.
*/
func dynamicFontType(dynamicFontType: DynamicFontType)
}
@objc(DynamicFontType)
open class DynamicFontType: NSObject {
/// A weak reference to a DynamicFontTypeDelegate.
open weak var delegate: DynamicFontTypeDelegate?
/// Initializer.
public override init() {
super.init()
prepare()
}
@objc
internal func handleContentSizeChange() {
delegate?.dynamicFontType(dynamicFontType: self)
}
/// Prepare the instance object.
private func prepare() {
prepareContentSizeObservation()
}
/// Prepares observation for content size changes.
private func prepareContentSizeObservation() {
NotificationCenter.default.addObserver(self, selector: #selector(handleContentSizeChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
}
}
================================================
FILE: Sources/iOS/Font/Font.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public protocol FontType {
/**
Regular with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
static func regular(with size: CGFloat) -> UIFont
/**
Medium with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
static func medium(with size: CGFloat) -> UIFont
/**
Bold with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
static func bold(with size: CGFloat) -> UIFont
}
public struct Font {
/// Size of font.
public static let pointSize: CGFloat = 16
/**
Retrieves the system font with a specified size.
- Parameter ofSize size: A CGFloat.
*/
public static func systemFont(ofSize size: CGFloat) -> UIFont {
return UIFont.systemFont(ofSize: size)
}
/**
Retrieves the bold system font with a specified size..
- Parameter ofSize size: A CGFloat.
*/
public static func boldSystemFont(ofSize size: CGFloat) -> UIFont {
return UIFont.boldSystemFont(ofSize: size)
}
/**
Retrieves the italic system font with a specified size.
- Parameter ofSize size: A CGFloat.
*/
public static func italicSystemFont(ofSize size: CGFloat) -> UIFont {
return UIFont.italicSystemFont(ofSize: size)
}
/**
Loads a given font if needed.
- Parameter name: A String font name.
*/
public static func loadFontIfNeeded(name: String) {
FontLoader.loadFontIfNeeded(name: name)
}
}
/// Loads fonts packaged with Material.
private class FontLoader {
/// A Dictionary of the fonts already loaded.
static var loadedFonts: Dictionary = Dictionary()
/**
Loads a given font if needed.
- Parameter fontName: A String font name.
*/
static func loadFontIfNeeded(name: String) {
let loadedFont: String? = FontLoader.loadedFonts[name]
if nil == loadedFont && nil == UIFont(name: name, size: 1) {
FontLoader.loadedFonts[name] = name
let bundle = Bundle(for: FontLoader.self)
let identifier = bundle.bundleIdentifier
let fontURL = true == identifier?.hasPrefix("org.cocoapods") ? bundle.url(forResource: name, withExtension: "ttf", subdirectory: "com.cosmicmind.material.fonts.bundle") : bundle.url(forResource: name, withExtension: "ttf")
if let v = fontURL {
let data = NSData(contentsOf: v as URL)!
let provider = CGDataProvider(data: data)!
let font = CGFont(provider)
var error: Unmanaged?
if !CTFontManagerRegisterGraphicsFont(font!, &error) {
let errorDescription = CFErrorCopyDescription(error!.takeUnretainedValue())
let nsError = error!.takeUnretainedValue() as Any as! Error
NSException(name: .internalInconsistencyException, reason: errorDescription as String?, userInfo: [NSUnderlyingErrorKey: nsError as Any]).raise()
}
}
}
}
}
================================================
FILE: Sources/iOS/Font/RobotoFont.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public struct RobotoFont: FontType {
/// Size of font.
public static var pointSize: CGFloat {
return Font.pointSize
}
/// Thin font.
public static var thin: UIFont {
return thin(with: Font.pointSize)
}
/// Light font.
public static var light: UIFont {
return light(with: Font.pointSize)
}
/// Regular font.
public static var regular: UIFont {
return regular(with: Font.pointSize)
}
/// Medium font.
public static var medium: UIFont {
return medium(with: Font.pointSize)
}
/// Bold font.
public static var bold: UIFont {
return bold(with: Font.pointSize)
}
/**
Thin with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
public static func thin(with size: CGFloat) -> UIFont {
Font.loadFontIfNeeded(name: "Roboto-Thin")
if let f = UIFont(name: "Roboto-Thin", size: size) {
return f
}
return Font.systemFont(ofSize: size)
}
/**
Light with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
public static func light(with size: CGFloat) -> UIFont {
Font.loadFontIfNeeded(name: "Roboto-Light")
if let f = UIFont(name: "Roboto-Light", size: size) {
return f
}
return Font.systemFont(ofSize: size)
}
/**
Regular with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
public static func regular(with size: CGFloat) -> UIFont {
Font.loadFontIfNeeded(name: "Roboto-Regular")
if let f = UIFont(name: "Roboto-Regular", size: size) {
return f
}
return Font.systemFont(ofSize: size)
}
/**
Medium with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
public static func medium(with size: CGFloat) -> UIFont {
Font.loadFontIfNeeded(name: "Roboto-Medium")
if let f = UIFont(name: "Roboto-Medium", size: size) {
return f
}
return Font.boldSystemFont(ofSize: size)
}
/**
Bold with size font.
- Parameter with size: A CGFLoat for the font size.
- Returns: A UIFont.
*/
public static func bold(with size: CGFloat) -> UIFont {
Font.loadFontIfNeeded(name: "Roboto-Bold")
if let f = UIFont(name: "Roboto-Bold", size: size) {
return f
}
return Font.boldSystemFont(ofSize: size)
}
}
================================================
FILE: Sources/iOS/Grid/Grid.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
@objc(GridAxisDirection)
public enum GridAxisDirection: Int {
case any
case horizontal
case vertical
}
public struct GridAxis {
/// The direction the grid lays its views out.
public var direction = GridAxisDirection.horizontal
/// The rows size.
public var rows: Int
/// The columns size.
public var columns: Int
/**
Initializer.
- Parameter rows: The number of rows, vertical axis the grid will use.
- Parameter columns: The number of columns, horizontal axis the grid will use.
*/
internal init(rows: Int = 12, columns: Int = 12) {
self.rows = rows
self.columns = columns
}
}
public struct GridOffset {
/// The rows size.
public var rows: Int
/// The columns size.
public var columns: Int
/**
Initializer.
- Parameter rows: The number of rows, vertical axis the grid will use.
- Parameter columns: The number of columns, horizontal axis the grid will use.
*/
internal init(rows: Int = 0, columns: Int = 0) {
self.rows = rows
self.columns = columns
}
}
public struct Grid {
/// Context view.
internal weak var context: UIView?
/// Defer the calculation.
public var isDeferred = false
/// Number of rows.
public var rows: Int {
didSet {
reload()
}
}
/// Number of columns.
public var columns: Int {
didSet {
reload()
}
}
/// Offsets for rows and columns.
public var offset = GridOffset() {
didSet {
reload()
}
}
/// The axis in which the Grid is laying out its views.
public var axis = GridAxis() {
didSet {
reload()
}
}
/// Preset inset value for grid.
public var layoutEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
layoutEdgeInsets = EdgeInsetsPresetToValue(preset: contentEdgeInsetsPreset)
}
}
/// Insets value for grid.
public var layoutEdgeInsets = EdgeInsets.zero {
didSet {
reload()
}
}
/// Preset inset value for grid.
public var contentEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
contentEdgeInsets = EdgeInsetsPresetToValue(preset: contentEdgeInsetsPreset)
}
}
/// Insets value for grid.
public var contentEdgeInsets = EdgeInsets.zero {
didSet {
reload()
}
}
/// A preset wrapper for interim space.
public var interimSpacePreset = InterimSpacePreset.none {
didSet {
interimSpace = InterimSpacePresetToValue(preset: interimSpacePreset)
}
}
/// The space between grid rows and columnss.
public var interimSpace: InterimSpace {
didSet {
reload()
}
}
/// An Array of UIButtons.
public var views = [UIView]() {
didSet {
oldValue.forEach {
$0.removeFromSuperview()
}
reload()
}
}
/**
Initializer.
- Parameter rows: The number of rows, vertical axis the grid will use.
- Parameter columns: The number of columns, horizontal axis the grid will use.
- Parameter interimSpace: The interim space between rows or columns.
*/
internal init(context: UIView?, rows: Int = 0, columns: Int = 0, interimSpace: InterimSpace = 0) {
self.context = context
self.rows = rows
self.columns = columns
self.interimSpace = interimSpace
}
/// Begins a deferred block.
public mutating func begin() {
isDeferred = true
}
/// Completes a deferred block.
public mutating func commit() {
isDeferred = false
reload()
}
/**
Update grid in a deferred block.
- Parameter _ block: An update code block.
*/
public mutating func update(_ block: (Grid) -> Void) {
begin()
block(self)
commit()
}
/// Reload the button layout.
public func reload() {
guard !isDeferred else {
return
}
guard let canvas = context else {
return
}
for v in views {
if canvas != v.superview {
canvas.addSubview(v)
}
}
let count = views.count
guard 0 < count else {
return
}
/// It is important to call `setNeedsLayout` and `layoutIfNeeded`
/// in order to have the parent view's `frame` calculation be set
/// for `Grid` to be able to then calculate the correct dimenions
/// of its `child` views.
canvas.setNeedsLayout()
canvas.layoutIfNeeded()
guard 0 < canvas.bounds.width && 0 < canvas.bounds.height else {
return
}
var n = 0
var i = 0
for v in views {
// Forces the view to adjust accordingly to size changes, ie: UILabel.
(v as? UILabel)?.sizeToFit()
switch axis.direction {
case .horizontal:
let c = 0 == v.grid.columns ? axis.columns / count : v.grid.columns
let co = v.grid.offset.columns
let w = (canvas.bounds.width - contentEdgeInsets.left - contentEdgeInsets.right - layoutEdgeInsets.left - layoutEdgeInsets.right + interimSpace) / CGFloat(axis.columns)
v.frame.origin.x = CGFloat(i + n + co) * w + contentEdgeInsets.left + layoutEdgeInsets.left
v.frame.origin.y = contentEdgeInsets.top + layoutEdgeInsets.top
v.frame.size.width = w * CGFloat(c) - interimSpace
v.frame.size.height = canvas.bounds.height - contentEdgeInsets.top - contentEdgeInsets.bottom - layoutEdgeInsets.top - layoutEdgeInsets.bottom
n += c + co - 1
case .vertical:
let r = 0 == v.grid.rows ? axis.rows / count : v.grid.rows
let ro = v.grid.offset.rows
let h = (canvas.bounds.height - contentEdgeInsets.top - contentEdgeInsets.bottom - layoutEdgeInsets.top - layoutEdgeInsets.bottom + interimSpace) / CGFloat(axis.rows)
v.frame.origin.x = contentEdgeInsets.left + layoutEdgeInsets.left
v.frame.origin.y = CGFloat(i + n + ro) * h + contentEdgeInsets.top + layoutEdgeInsets.top
v.frame.size.width = canvas.bounds.width - contentEdgeInsets.left - contentEdgeInsets.right - layoutEdgeInsets.left - layoutEdgeInsets.right
v.frame.size.height = h * CGFloat(r) - interimSpace
n += r + ro - 1
case .any:
let r = 0 == v.grid.rows ? axis.rows / count : v.grid.rows
let ro = v.grid.offset.rows
let c = 0 == v.grid.columns ? axis.columns / count : v.grid.columns
let co = v.grid.offset.columns
let w = (canvas.bounds.width - contentEdgeInsets.left - contentEdgeInsets.right - layoutEdgeInsets.left - layoutEdgeInsets.right + interimSpace) / CGFloat(axis.columns)
let h = (canvas.bounds.height - contentEdgeInsets.top - contentEdgeInsets.bottom - layoutEdgeInsets.top - layoutEdgeInsets.bottom + interimSpace) / CGFloat(axis.rows)
v.frame.origin.x = CGFloat(co) * w + contentEdgeInsets.left + layoutEdgeInsets.left
v.frame.origin.y = CGFloat(ro) * h + contentEdgeInsets.top + layoutEdgeInsets.top
v.frame.size.width = w * CGFloat(c) - interimSpace
v.frame.size.height = h * CGFloat(r) - interimSpace
}
i += 1
/// reload the grid layout for each view on
/// each iteration, in order to ensure that
/// subviews are recalculated correctly.
if (isDeferred) { continue }
v.grid.reload()
}
}
}
fileprivate var AssociatedInstanceKey: UInt8 = 0
extension UIView {
/// Grid reference.
public var grid: Grid {
get {
return AssociatedObject.get(base: self, key: &AssociatedInstanceKey) {
return Grid(context: self)
}
}
set(value) {
AssociatedObject.set(base: self, key: &AssociatedInstanceKey, value: value)
}
}
/// A reference to grid's layoutEdgeInsetsPreset.
open var layoutEdgeInsetsPreset: EdgeInsetsPreset {
get {
return grid.layoutEdgeInsetsPreset
}
set(value) {
grid.layoutEdgeInsetsPreset = value
}
}
/// A reference to grid's layoutEdgeInsets.
@IBInspectable
open var layoutEdgeInsets: EdgeInsets {
get {
return grid.layoutEdgeInsets
}
set(value) {
grid.layoutEdgeInsets = value
}
}
}
================================================
FILE: Sources/iOS/Height/HeightPreset.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public enum HeightPreset {
case none
case tiny
case xsmall
case small
case `default`
case normal
case medium
case large
case xlarge
case xxlarge
case custom(CGFloat)
public var rawValue: CGFloat {
switch self {
case .none:
return 0
case .tiny:
return 20
case .xsmall:
return 28
case .small:
return 36
case .`default`:
return 44
case .normal:
return 49
case .medium:
return 52
case .large:
return 60
case .xlarge:
return 68
case .xxlarge:
return 104
case .custom(let v):
return v
}
}
}
================================================
FILE: Sources/iOS/Icon/Icon.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
public struct Icon {
/// An internal reference to the icons bundle.
private static var internalBundle: Bundle?
/**
A public reference to the icons bundle, that aims to detect
the correct bundle to use.
*/
public static var bundle: Bundle {
if nil == Icon.internalBundle {
Icon.internalBundle = Bundle(for: View.self)
let url = Icon.internalBundle!.resourceURL!
let b = Bundle(url: url.appendingPathComponent("com.cosmicmind.material.icons.bundle"))
if let v = b {
Icon.internalBundle = v
}
}
return Icon.internalBundle!
}
/// Get the icon by the file name.
public static func icon(_ name: String) -> UIImage? {
return UIImage(named: name, in: bundle, compatibleWith: nil)?.withRenderingMode(.alwaysTemplate)
}
/// Google icons.
public static var add = Icon.icon("ic_add_white")
public static var addCircle = Icon.icon("ic_add_circle_white")
public static var addCircleOutline = Icon.icon("ic_add_circle_outline_white")
public static var arrowBack = Icon.icon("ic_arrow_back_white")
public static var arrowDownward = Icon.icon("ic_arrow_downward_white")
public static var audio = Icon.icon("ic_audiotrack_white")
public static var bell = Icon.icon("cm_bell_white")
public static var cameraFront = Icon.icon("ic_camera_front_white")
public static var cameraRear = Icon.icon("ic_camera_rear_white")
public static var check = Icon.icon("ic_check_white")
public static var clear = Icon.icon("ic_close_white")
public static var close = Icon.icon("ic_close_white")
public static var edit = Icon.icon("ic_edit_white")
public static var email = Icon.icon("ic_email_white")
public static var favorite = Icon.icon("ic_favorite_white")
public static var favoriteBorder = Icon.icon("ic_favorite_border_white")
public static var flashAuto = Icon.icon("ic_flash_auto_white")
public static var flashOff = Icon.icon("ic_flash_off_white")
public static var flashOn = Icon.icon("ic_flash_on_white")
public static var history = Icon.icon("ic_history_white")
public static var home = Icon.icon("ic_home_white")
public static var image = Icon.icon("ic_image_white")
public static var menu = Icon.icon("ic_menu_white")
public static var moreHorizontal = Icon.icon("ic_more_horiz_white")
public static var moreVertical = Icon.icon("ic_more_vert_white")
public static var movie = Icon.icon("ic_movie_white")
public static var pen = Icon.icon("ic_edit_white")
public static var place = Icon.icon("ic_place_white")
public static var phone = Icon.icon("ic_phone_white")
public static var photoCamera = Icon.icon("ic_photo_camera_white")
public static var photoLibrary = Icon.icon("ic_photo_library_white")
public static var search = Icon.icon("ic_search_white")
public static var settings = Icon.icon("ic_settings_white")
public static var share = Icon.icon("ic_share_white")
public static var star = Icon.icon("ic_star_white")
public static var starBorder = Icon.icon("ic_star_border_white")
public static var starHalf = Icon.icon("ic_star_half_white")
public static var videocam = Icon.icon("ic_videocam_white")
public static var visibility = Icon.icon("ic_visibility_white")
public static var visibilityOff = Icon.icon("ic_visibility_off_white")
public static var work = Icon.icon("ic_work_white")
/// CosmicMind icons.
public struct cm {
public static var add = Icon.icon("cm_add_white")
public static var arrowBack = Icon.icon("cm_arrow_back_white")
public static var arrowDownward = Icon.icon("cm_arrow_downward_white")
public static var audio = Icon.icon("cm_audio_white")
public static var audioLibrary = Icon.icon("cm_audio_library_white")
public static var bell = Icon.icon("cm_bell_white")
public static var check = Icon.icon("cm_check_white")
public static var clear = Icon.icon("cm_close_white")
public static var close = Icon.icon("cm_close_white")
public static var edit = Icon.icon("cm_pen_white")
public static var image = Icon.icon("cm_image_white")
public static var menu = Icon.icon("cm_menu_white")
public static var microphone = Icon.icon("cm_microphone_white")
public static var moreHorizontal = Icon.icon("cm_more_horiz_white")
public static var moreVertical = Icon.icon("cm_more_vert_white")
public static var movie = Icon.icon("cm_movie_white")
public static var pause = Icon.icon("cm_pause_white")
public static var pen = Icon.icon("cm_pen_white")
public static var photoCamera = Icon.icon("cm_photo_camera_white")
public static var photoLibrary = Icon.icon("cm_photo_library_white")
public static var play = Icon.icon("cm_play_white")
public static var search = Icon.icon("cm_search_white")
public static var settings = Icon.icon("cm_settings_white")
public static var share = Icon.icon("cm_share_white")
public static var shuffle = Icon.icon("cm_shuffle_white")
public static var skipBackward = Icon.icon("cm_skip_backward_white")
public static var skipForward = Icon.icon("cm_skip_forward_white")
public static var star = Icon.icon("cm_star_white")
public static var videocam = Icon.icon("cm_videocam_white")
public static var volumeHigh = Icon.icon("cm_volume_high_white")
public static var volumeMedium = Icon.icon("cm_volume_medium_white")
public static var volumeOff = Icon.icon("cm_volume_off_white")
}
}
================================================
FILE: Sources/iOS/Layer/Layer.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
@objc(Layer)
open class Layer: CAShapeLayer {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
allows the dropshadow effect on the backing layer, while clipping
the image to a desired shape within the visualLayer.
*/
public let visualLayer = CAShapeLayer()
/**
A property that manages an image for the visualLayer's contents
property. Images should not be set to the backing layer's contents
property to avoid conflicts when using clipsToBounds.
*/
@IBInspectable
open var image: UIImage? {
didSet {
visualLayer.contents = image?.cgImage
}
}
/**
Allows a relative subrectangle within the range of 0 to 1 to be
specified for the visualLayer's contents property. This allows
much greater flexibility than the contentsGravity property in
terms of how the image is cropped and stretched.
*/
open override var contentsRect: CGRect {
didSet {
visualLayer.contentsRect = contentsRect
}
}
/**
A CGRect that defines a stretchable region inside the visualLayer
with a fixed border around the edge.
*/
open override var contentsCenter: CGRect {
didSet {
visualLayer.contentsCenter = contentsCenter
}
}
/**
A floating point value that defines a ratio between the pixel
dimensions of the visualLayer's contents property and the size
of the layer. By default, this value is set to the Screen.scale.
*/
@IBInspectable
open override var contentsScale: CGFloat {
didSet {
visualLayer.contentsScale = contentsScale
}
}
/// Determines how content should be aligned within the visualLayer's bounds.
@IBInspectable
open override var contentsGravity: CALayerContentsGravity {
get {
return visualLayer.contentsGravity
}
set(value) {
visualLayer.contentsGravity = value
}
}
/**
A property that sets the cornerRadius of the backing layer. If the shape
property has a value of .circle when the cornerRadius is set, it will
become .none, as it no longer maintains its circle shape.
*/
@IBInspectable
open override var cornerRadius: CGFloat {
didSet {
layoutShadowPath()
shapePreset = .none
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepareVisualLayer()
}
/**
An initializer the same as init(). The layer parameter is ignored
to avoid crashes on certain architectures.
- Parameter layer: Any.
*/
public override init(layer: Any) {
super.init(layer: layer)
prepareVisualLayer()
}
/// A convenience initializer.
public override init() {
super.init()
prepareVisualLayer()
}
/**
An initializer that initializes the object with a CGRect object.
- Parameter frame: A CGRect instance.
*/
public convenience init(frame: CGRect) {
self.init()
self.frame = frame
}
open override func layoutSublayers() {
super.layoutSublayers()
layoutShape()
layoutVisualLayer()
layoutShadowPath()
}
}
fileprivate extension Layer {
/// Prepares the visualLayer property.
func prepareVisualLayer() {
contentsGravity = .resizeAspectFill
visualLayer.zPosition = 0
visualLayer.masksToBounds = true
addSublayer(visualLayer)
}
/// Manages the layout for the visualLayer property.
func layoutVisualLayer() {
visualLayer.frame = bounds
visualLayer.cornerRadius = cornerRadius
}
}
================================================
FILE: Sources/iOS/Layout/Layout.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
import Motion
/// A protocol that's conformed by UIView and UILayoutGuide.
public protocol Constraintable: class { }
@available(iOS 9.0, *)
extension UILayoutGuide: Constraintable { }
extension UIView: Constraintable { }
/// Layout extension for UIView.
public extension UIView {
/**
Used to chain layout constraints on a child context.
- Parameter child: A child UIView to layout.
- Returns: A Layout instance.
*/
func layout(_ child: UIView) -> Layout {
if self != child.superview {
addSubview(child)
}
child.translatesAutoresizingMaskIntoConstraints = false
return child.layout
}
/// Layout instance for the view.
var layout: Layout {
return Layout(constraintable: self)
}
/// Anchor instance for the view.
var anchor: LayoutAnchor {
return LayoutAnchor(constraintable: self)
}
/**
Anchor instance for safeAreaLayoutGuide.
Below iOS 11, it will be same as view.anchor.
*/
var safeAnchor: LayoutAnchor {
if #available(iOS 11.0, *) {
return LayoutAnchor(constraintable: safeAreaLayoutGuide)
} else {
return anchor
}
}
}
private extension NSLayoutConstraint {
/**
Checks if the constraint is equal to given constraint.
- Parameter _ other: An NSLayoutConstraint.
- Returns: A Bool indicating whether constraints are equal.
*/
func equalTo(_ other: NSLayoutConstraint) -> Bool {
return firstItem === other.firstItem
&& secondItem === other.secondItem
&& firstAttribute == other.firstAttribute
&& secondAttribute == other.secondAttribute
&& relation == other.relation
}
}
/// A memory reference to the lastConstraint of UIView.
private var LastConstraintKey: UInt8 = 0
private extension UIView {
/**
The last consntraint that's created by Layout system.
Used to set multiplier/priority on the last constraint.
*/
var lastConstraint: NSLayoutConstraint? {
get {
return AssociatedObject.get(base: self, key: &LastConstraintKey) {
nil
}
}
set(value) {
AssociatedObject.set(base: self, key: &LastConstraintKey, value: value)
}
}
}
public struct Layout {
/// A weak reference to the constraintable.
public weak var constraintable: Constraintable?
/// Parent view of the view.
var parent: UIView? {
return (constraintable as? UIView)?.superview
}
/// Returns the view that is being laied out.
public var view: UIView? {
var v = constraintable as? UIView
if #available(iOS 9.0, *), v == nil {
v = (constraintable as? UILayoutGuide)?.owningView
}
return v
}
/**
An initializer taking Constraintable.
- Parameter view: A Constraintable.
*/
init(constraintable: Constraintable) {
self.constraintable = constraintable
}
}
public extension Layout {
/**
Sets multiplier of the last created constraint.
Not meant for updating the multiplier as it will re-create the constraint.
- Parameter _ multiplier: A CGFloat multiplier.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func multiply(_ multiplier: CGFloat) -> Layout {
return resetLastConstraint(multiplier: multiplier)
}
/**
Sets priority of the last created constraint.
Not meant for updating the multiplier as it will re-create the constraint.
- Parameter _ value: A Float priority.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func priority(_ value: Float) -> Layout {
return priority(.init(rawValue: value))
}
/**
Sets priority of the last created constraint.
Not meant for updating the priority as it will re-create the constraint.
- Parameter _ priority: A UILayoutPriority.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func priority(_ priority: UILayoutPriority) -> Layout {
return resetLastConstraint(priority: priority)
}
/**
Removes the last created constraint and creates new one with the new multiplier and/or priority (if provided).
- Parameter multiplier: An optional CGFloat.
- Parameter priority: An optional UILayoutPriority.
- Returns: A Layout instance to allow chaining.
*/
private func resetLastConstraint(multiplier: CGFloat? = nil, priority: UILayoutPriority? = nil) -> Layout {
guard let v = view?.lastConstraint, v.isActive else {
return self
}
v.isActive = false
let newV = NSLayoutConstraint(item: v.firstItem as Any,
attribute: v.firstAttribute,
relatedBy: v.relation,
toItem: v.secondItem,
attribute: v.secondAttribute,
multiplier: multiplier ?? v.multiplier,
constant: v.constant)
newV.priority = priority ?? v.priority
newV.isActive = true
view?.lastConstraint = newV
return self
}
}
public extension Layout {
/**
Constraints top of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func top(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.top, relationer: relationer, constant: offset)
}
/**
Constraints left of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func left(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.left, relationer: relationer, constant: offset)
}
/**
Constraints right of the view to its parent.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func right(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.right, relationer: relationer, constant: -offset)
}
/**
Constraints leading of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leading(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.leading, relationer: relationer, constant: offset)
}
/**
Constraints trailing of the view to its parent.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func trailing(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.trailing, relationer: relationer, constant: -offset)
}
/**
Constraints bottom of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottom(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.bottom, relationer: relationer, constant: -offset)
}
/**
Constraints top-left of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, constants: top, left)
}
/**
Constraints top-right of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, constants: top, -right)
}
/**
Constraints bottom-left of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, constants: -bottom, left)
}
/**
Constraints bottom-right of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, constants: -bottom, -right)
}
/**
Constraints left and right of the view to its parent's.
- Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leftRight(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, constants: left, -right)
}
/**
Constraints top-leading of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topLeading(top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.topLeading, constants: top, leading)
}
/**
Constraints top-trailing of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topTrailing(top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.topTrailing, constants: top, -trailing)
}
/**
Constraints bottom-leading of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomLeading(bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.bottomLeading, constants: -bottom, leading)
}
/**
Constraints bottom-trailing of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomTrailing(bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.bottomTrailing, constants: -bottom, -trailing)
}
/**
Constraints leading and trailing of the view to its parent's.
- Parameter leading: A CGFloat offset for leading.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leadingTrailing(leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.leadingTrailing, constants: leading, -trailing)
}
/**
Constraints top and bottom of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter bottom: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topBottom(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, constants: top, -bottom)
}
/**
Constraints center of the view to its parent's.
- Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter offsetY: A CGFloat offset for vertical center.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, constants: offsetX, offsetY)
}
/**
Constraints horizontal center of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerX(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.centerX, relationer: relationer, constant: offset)
}
/**
Constraints vertical center of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerY(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.centerY, relationer: relationer, constant: offset)
}
/**
Constraints width of the view to its parent's.
- Parameter offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func width(offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.width, relationer: relationer, constant: offset)
}
/**
Constraints height of the view to its parent's.
- Parameter offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func height(offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.height, relationer: relationer, constant: offset)
}
/**
Constraints edges of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, constants: top, left, -bottom, -right)
}
}
public extension Layout {
/**
Constraints top of the view to its parent's safeArea.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.top, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints left of the view to its parent's safeArea.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leftSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.left, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints right of the view to its parent.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func rightSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.right, relationer: relationer, constant: -offset, useSafeArea: true)
}
/**
Constraints leading of the view to its parent's safeArea.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leadingSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.leading, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints trailing of the view to its parent.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func trailingSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.trailing, relationer: relationer, constant: -offset, useSafeArea: true)
}
/**
Constraints bottom of the view to its parent's safeArea.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.bottom, relationer: relationer, constant: -offset, useSafeArea: true)
}
/**
Constraints top-left of the view to its parent's safeArea.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topLeftSafe(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, constants: top, left, useSafeArea: true)
}
/**
Constraints top-right of the view to its parent's safeArea.
- Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topRightSafe(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, constants: top, -right, useSafeArea: true)
}
/**
Constraints bottom-left of the view to its parent's safeArea.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomLeftSafe(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, constants: -bottom, left, useSafeArea: true)
}
/**
Constraints bottom-right of the view to its parent's safeArea.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomRightSafe(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, constants: -bottom, -right, useSafeArea: true)
}
/**
Constraints left and right of the view to its parent's safeArea.
- Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leftRightSafe(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, constants: left, -right, useSafeArea: true)
}
/**
Constraints top-leading of the view to its parent's safeArea.
- Parameter top: A CGFloat offset for top.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topLeadingSafe(top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.topLeading, constants: top, leading, useSafeArea: true)
}
/**
Constraints top-trailing of the view to its parent's safeArea.
- Parameter top: A CGFloat offset for top.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topTrailingSafe(top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.topTrailing, constants: top, -trailing, useSafeArea: true)
}
/**
Constraints bottom-leading of the view to its parent's safeArea.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomLeadingSafe(bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.bottomLeading, constants: -bottom, leading, useSafeArea: true)
}
/**
Constraints bottom-trailing of the view to its parent's safeArea.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomTrailingSafe(bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.bottomTrailing, constants: -bottom, -trailing, useSafeArea: true)
}
/**
Constraints leading and trailing of the view to its parent's safeArea.
- Parameter leading: A CGFloat offset for leading.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leadingTrailingSafe(leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.leadingTrailing, constants: leading, -trailing, useSafeArea: true)
}
/**
Constraints top and bottom of the view to its parent's safeArea.
- Parameter top: A CGFloat offset for top.
- Parameter bottom: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topBottomSafe(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, constants: top, -bottom, useSafeArea: true)
}
/**
Constraints center of the view to its parent's safeArea.
- Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter offsetY: A CGFloat offset for vertical center.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerSafe(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, constants: offsetX, offsetY, useSafeArea: true)
}
/**
Constraints horizontal center of the view to its parent's safeArea.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerXSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.centerX, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints vertical center of the view to its parent's safeArea.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerYSafe(_ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.centerY, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints width of the view to its parent's safeArea.
- Parameter offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func widthSafe(offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.width, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints height of the view to its parent's safeArea.
- Parameter offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func heightSafe(offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.height, relationer: relationer, constant: offset, useSafeArea: true)
}
/**
Constraints edges of the view to its parent's safeArea.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func edgesSafe(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, constants: top, left, -bottom, -right, useSafeArea: true)
}
}
public extension Layout {
/**
Constraints width of the view to a constant value.
- Parameter _ width: A CGFloat value.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func width(_ width: CGFloat, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.constantWidth, relationer: relationer, constants: width)
}
/**
Constraints height of the view to a constant value.
- Parameter _ height: A CGFloat value.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func height(_ height: CGFloat, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.constantHeight, relationer: relationer, constants: height)
}
/**
The width and height of the view to its parent's.
- Parameter _ size: A CGSize offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func size(_ size: CGSize) -> Layout {
return width(size.width).height(size.height)
}
}
public extension Layout {
/**
Constraints top of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func top(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.top, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints left of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func left(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.left, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func right(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.right, to: anchor, relationer: relationer, constant: -offset)
}
/**
Constraints leading of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leading(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.leading, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func trailing(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.trailing, to: anchor, relationer: relationer, constant: -offset)
}
/**
Constraints bottom of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottom(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.bottom, to: anchor, relationer: relationer, constant: -offset)
}
/**
Constraints top-leading of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topLeading(_ anchor: LayoutAnchorable, top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.topLeading, to: anchor, constants: top, leading)
}
/**
Constraints top-trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topTrailing(_ anchor: LayoutAnchorable, top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.topTrailing, to: anchor, constants: top, -trailing)
}
/**
Constraints bottom-leading of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomLeading(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.bottomLeading, to: anchor, constants: -bottom, leading)
}
/**
Constraints bottom-trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomTrailing(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.bottomTrailing, to: anchor, constants: -bottom, -trailing)
}
/**
Constraints leading and trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter leading: A CGFloat offset for leading.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leadingTrailing(_ anchor: LayoutAnchorable, leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.leadingTrailing, to: anchor, constants: leading, -trailing)
}
/**
Constraints top-left of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topLeft(_ anchor: LayoutAnchorable, top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, to: anchor, constants: top, left)
}
/**
Constraints top-right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topRight(_ anchor: LayoutAnchorable, top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, to: anchor, constants: top, -right)
}
/**
Constraints bottom-left of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomLeft(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, to: anchor, constants: -bottom, left)
}
/**
Constraints bottom-right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func bottomRight(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, to: anchor, constants: -bottom, -right)
}
/**
Constraints left and right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func leftRight(_ anchor: LayoutAnchorable, left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, to: anchor, constants: left, -right)
}
/**
Constraints top and bottom of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter bottom: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func topBottom(_ anchor: LayoutAnchorable, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, to: anchor, constants: top, -bottom)
}
/**
Constraints center of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter offsetY: A CGFloat offset for vertical center.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func center(_ anchor: LayoutAnchorable, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, to: anchor, constants: offsetX, offsetY)
}
/**
Constraints horizontal center of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerX(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.centerX, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints vertical center of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func centerY(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.centerY, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints width of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func width(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.width, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints height of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Parameter _ relationer: A LayoutRelationer.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func height(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0, _ relationer: LayoutRelationer = LayoutRelationerStruct.equal) -> Layout {
return constraint(.height, to: anchor, relationer: relationer, constant: offset)
}
/**
Constraints edges of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func edges(_ anchor: LayoutAnchorable, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, to: anchor, constants: top, left, -bottom, -right)
}
}
public extension Layout {
/**
Constraints the object and sets it's anchor to `bottom`.
- Parameter _ view: A UIView.
- Parameter offset: A CGFloat offset for top.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func below(_ view: UIView, _ offset: CGFloat = 0) -> Layout {
return top(view.anchor.bottom, offset)
}
/**
Constraints the object and sets it's anchor to `top`.
- Parameter _ view: A UIView.
- Parameter offset: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func above(_ view: UIView, _ offset: CGFloat = 0) -> Layout {
return bottom(view.anchor.top, offset)
}
/**
Constraints the object and sets it's anchor to `before/left`.
- Parameter _ view: A UIView.
- Parameter offset: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func before(_ view: UIView, _ offset: CGFloat = 0) -> Layout {
return right(view.anchor.left, offset)
}
/**
Constraints the object and sets it's anchor to `after/right`.
- Parameter _ view: A UIView.
- Parameter offset: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func after(_ view: UIView, _ offset: CGFloat = 0) -> Layout {
return left(view.anchor.right, offset)
}
/**
Constraints the object and sets it's aspect.
- Parameter ratio: A CGFloat ratio multiplier.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
func aspect(_ ratio: CGFloat = 1) -> Layout {
return height(view!.anchor.width).multiply(ratio)
}
}
private extension Layout {
/**
Constraints the view to its parent according to the provided attribute.
If the constraint already exists, will update its constant.
- Parameter _ attribute: A LayoutAttribute.
- Parameter _ relationer: A LayoutRelationer.
- Parameter constant: A CGFloat.
- Returns: A Layout instance to allow chaining.
*/
func constraint(_ attribute: LayoutAttribute, relationer: LayoutRelationer = LayoutRelationerStruct.equal, constant: CGFloat, useSafeArea: Bool = false) -> Layout {
return constraint([attribute], relationer: relationer, constants: constant, useSafeArea: useSafeArea)
}
/**
Constraints the view to its parent according to the provided attributes.
If any of the constraints already exists, will update its constant.
- Parameter _ attributes: An array of LayoutAttribute.
- Parameter _ relationer: A LayoutRelationer.
- Parameter constants: A list of CGFloat.
- Returns: A Layout instance to allow chaining.
*/
func constraint(_ attributes: [LayoutAttribute], relationer: LayoutRelationer = LayoutRelationerStruct.equal, constants: CGFloat..., useSafeArea: Bool = false) -> Layout {
var attributes = attributes
var anchor: LayoutAnchor!
if attributes == .constantHeight || attributes == .constantWidth {
attributes.removeLast()
anchor = LayoutAnchor(constraintable: nil, attributes: [.notAnAttribute])
} else {
guard parent != nil else {
fatalError("[Material Error: Constraint requires view to have parent.")
}
anchor = LayoutAnchor(constraintable: useSafeArea ? parent?.safeAnchor.constraintable : parent, attributes: attributes)
}
return constraint(attributes, to: anchor, relationer: relationer, constants: constants)
}
/**
Constraints the view to the given anchor according to the provided attribute.
If the constraint already exists, will update its constant.
- Parameter _ attribute: A LayoutAttribute.
- Parameter to anchor: A LayoutAnchorable.
- Parameter relation: A LayoutRelation between anchors.
- Parameter constant: A CGFloat.
- Returns: A Layout instance to allow chaining.
*/
func constraint(_ attribute: LayoutAttribute, to anchor: LayoutAnchorable, relationer: LayoutRelationer = LayoutRelationerStruct.equal, constant: CGFloat) -> Layout {
return constraint([attribute], to: anchor, relationer: relationer, constants: constant)
}
/**
Constraints the view to the given anchor according to the provided attributes.
If any of the constraints already exists, will update its constant.
- Parameter _ attributes: An array of LayoutAttribute.
- Parameter to anchor: A LayoutAnchorable.
- Parameter relation: A LayoutRelation between anchors.
- Parameter constants: A list of CGFloat.
- Returns: A Layout instance to allow chaining.
*/
func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchorable, relationer: LayoutRelationer = LayoutRelationerStruct.equal, constants: CGFloat...) -> Layout {
return constraint(attributes, to: anchor, relationer: relationer, constants: constants)
}
/**
Constraints the view to the given anchor according to the provided attributes.
If any of the constraints already exists, will update its constant.
- Parameter _ attributes: An array of LayoutAttribute.
- Parameter to anchor: A LayoutAnchorable.
- Parameter relation: A LayoutRelation between anchors.
- Parameter constants: An array of CGFloat.
- Returns: A Layout instance to allow chaining.
*/
func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchorable, relationer: LayoutRelationer, constants: [CGFloat]) -> Layout {
let from = LayoutAnchor(constraintable: constraintable, attributes: attributes)
var to = anchor as? LayoutAnchor
if to?.attributes.isEmpty ?? true {
let v = (anchor as? UIView) ?? (anchor as? LayoutAnchor)?.constraintable
to = LayoutAnchor(constraintable: v, attributes: attributes)
}
let constraint = LayoutConstraint(fromAnchor: from, toAnchor: to!, relation: relationer(.nil, .nil), constants: constants)
let constraints = (view?.constraints ?? []) + (view?.superview?.constraints ?? [])
let newConstraints = constraint.constraints
for newConstraint in newConstraints {
guard let activeConstraint = constraints.first(where: { $0.equalTo(newConstraint) }) else {
newConstraint.isActive = true
view?.lastConstraint = newConstraint
continue
}
activeConstraint.constant = newConstraint.constant
}
return self
}
}
/// A closure typealias for relation operators.
public typealias LayoutRelationer = (LayoutRelationerStruct, LayoutRelationerStruct) -> LayoutRelation
/// A dummy struct used in creating relation operators (==, >=, <=).
public struct LayoutRelationerStruct {
/// Passed as an unused argument to the LayoutRelationer closures.
static let `nil` = LayoutRelationerStruct()
/**
A method used as a default parameter for LayoutRelationer closures.
Swift does not allow using == operator directly, so we had to create this.
*/
public static func equal(left: LayoutRelationerStruct, right: LayoutRelationerStruct) -> LayoutRelation {
return .equal
}
}
/// A method returning `LayoutRelation.equal`
public func ==(left: LayoutRelationerStruct, right: LayoutRelationerStruct) -> LayoutRelation {
return .equal
}
/// A method returning `LayoutRelation.greaterThanOrEqual`
public func >=(left: LayoutRelationerStruct, right: LayoutRelationerStruct) -> LayoutRelation {
return .greaterThanOrEqual
}
/// A method returning `LayoutRelation.lessThanOrEqual`
public func <=(left: LayoutRelationerStruct, right: LayoutRelationerStruct) -> LayoutRelation {
return .lessThanOrEqual
}
================================================
FILE: Sources/iOS/Layout/LayoutAnchor.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc. .
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
/// A protocol that's conformed by UIView, LayoutAnchor, and Layout.
public protocol LayoutAnchorable { }
extension UIView: LayoutAnchorable { }
extension Layout: LayoutAnchorable { }
extension LayoutAnchor: LayoutAnchorable { }
public struct LayoutAnchor {
/// A weak reference to the constraintable.
public weak var constraintable: Constraintable?
/// An array of LayoutAttribute for the view.
public let attributes: [LayoutAttribute]
/**
An initializer taking constraintable and anchor attributes.
- Parameter view: A Constraintable.
- Parameter attributes: An array of LayoutAtrribute.
*/
public init(constraintable: Constraintable?, attributes: [LayoutAttribute] = []) {
self.constraintable = constraintable
self.attributes = attributes
}
}
public extension LayoutAnchor {
/// A layout anchor representing top of the view.
var top: LayoutAnchor {
return anchor(.top)
}
/// A layout anchor representing bottom of the view.
var bottom: LayoutAnchor {
return anchor(.bottom)
}
/// A layout anchor representing left of the view.
var left: LayoutAnchor {
return anchor(.left)
}
/// A layout anchor representing right of the view.
var right: LayoutAnchor {
return anchor(.right)
}
/// A layout anchor representing leading of the view.
var leading: LayoutAnchor {
return anchor(.leading)
}
/// A layout anchor representing trailing of the view.
var trailing: LayoutAnchor {
return anchor(.trailing)
}
/// A layout anchor representing top-left of the view.
var topLeft: LayoutAnchor {
return acnhor(.topLeft)
}
/// A layout anchor representing top-right of the view.
var topRight: LayoutAnchor {
return acnhor(.topRight)
}
/// A layout anchor representing bottom-left of the view.
var bottomLeft: LayoutAnchor {
return acnhor(.bottomLeft)
}
/// A layout anchor representing bottom-right of the view.
var bottomRight: LayoutAnchor {
return acnhor(.bottomRight)
}
/// A layout anchor representing top-leading of the view.
var topLeading: LayoutAnchor {
return acnhor(.topLeading)
}
/// A layout anchor representing top-trailing of the view.
var topTrailing: LayoutAnchor {
return acnhor(.topTrailing)
}
/// A layout anchor representing bottom-leading of the view.
var bottomLeading: LayoutAnchor {
return acnhor(.bottomLeading)
}
/// A layout anchor representing bottom-trailing of the view.
var bottomTrailing: LayoutAnchor {
return acnhor(.bottomTrailing)
}
/// A layout anchor representing top and bottom of the view.
var topBottom: LayoutAnchor {
return acnhor(.topBottom)
}
/// A layout anchor representing left and right of the view.
var leftRight: LayoutAnchor {
return acnhor(.leftRight)
}
/// A layout anchor representing leading and trailing of the view.
var leadingTrailing: LayoutAnchor {
return acnhor(.leadingTrailing)
}
/// A layout anchor representing center of the view.
var center: LayoutAnchor {
return acnhor(.center)
}
/// A layout anchor representing horizontal center of the view.
var centerX: LayoutAnchor {
return anchor(.centerX)
}
/// A layout anchor representing vertical center of the view.
var centerY: LayoutAnchor {
return anchor(.centerY)
}
/// A layout anchor representing top, left, bottom and right of the view.
var edges: LayoutAnchor {
return acnhor(.edges)
}
/// A layout anchor representing width of the view.
var width: LayoutAnchor {
return anchor(.width)
}
/// A layout anchor representing height of the view.
var height: LayoutAnchor {
return anchor(.height)
}
}
private extension LayoutAnchor {
/**
Creates LayoutAnchor with the given attribute.
- Parameter attribute: A LayoutAttribute.
- Returns: A LayoutAnchor.
*/
func anchor(_ attribute: LayoutAttribute) -> LayoutAnchor {
return LayoutAnchor(constraintable: constraintable, attributes: [attribute])
}
/**
Creates LayoutAnchor with the given attributes.
- Parameter attributes: An array of LayoutAttribute.
- Returns: A LayoutAnchor.
*/
func acnhor(_ attributes: [LayoutAttribute]) -> LayoutAnchor {
return LayoutAnchor(constraintable: constraintable, attributes: attributes)
}
}
================================================
FILE: Sources/iOS/Layout/LayoutAttribute.swift
================================================
/*
* The MIT License (MIT)
*
* Copyright (C) 2019, CosmicMind, Inc.