| // Copyright 2020-present the Material Components for iOS authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import UIKit |
| import MaterialComponents.MaterialAppBar |
| import MaterialComponents.MaterialAppBar_Theming |
| import MaterialComponents.MaterialBottomNavigation |
| import MaterialComponents.MaterialButtons |
| import MaterialComponents.MaterialButtons_Theming |
| import MaterialComponents.MaterialCards |
| import MaterialComponents.MaterialCards_Theming |
| import MaterialComponents.MaterialShadowElevations |
| import MaterialComponents.MaterialContainerScheme |
| |
| /// An example for testing the performance of `MDCShadowLayer`. This example is not intended to |
| /// demonstrate how to setup any specific components but rather to push `MDCShadowLayer` to the |
| /// limits for metric testing. |
| class MDCShadowPerformanceExample: |
| UIViewController, |
| UICollectionViewDelegate, |
| UICollectionViewDataSource, |
| UICollectionViewDelegateFlowLayout |
| { |
| |
| /// Injected `MDCAppBarViewController` so we can render the shadow on it. |
| lazy var appBar = makeAppBar() |
| |
| /// The _plus_ FAB. |
| let createButton = MDCFloatingButton() |
| |
| /// Used to render a _top_ shadow on a component that is pinned to the bottom. |
| let bottomNavBar = MDCBottomNavigationBar() |
| |
| /// Used for styling the sub components based on the app container scheme. |
| // `@objc` is required for `setupTransition` to work within `MDCDragonsController`. |
| @objc var containerScheme: MDCContainerScheming = MDCContainerScheme() |
| |
| /// The main scroll view content. |
| let collectionView = UICollectionView( |
| frame: .zero, |
| collectionViewLayout: UICollectionViewFlowLayout()) |
| |
| /// Number of cells in the `collectionView`. |
| let cellCount = 200 |
| |
| /// Cell reuse identifier. |
| let cellIdentifier = "Cell" |
| |
| /// Padding between cells. |
| let defaultPadding: CGFloat = 8 |
| |
| /// The title of the example |
| fileprivate static let exampleTitle = "Shadow & Performance" |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| view.backgroundColor = containerScheme.colorScheme.backgroundColor |
| appBar.headerView.observesTrackingScrollViewScrollEvents = true |
| appBar.applyPrimaryTheme(withScheme: containerScheme) |
| appBar.didMove(toParent: self) |
| appBar.navigationBar.title = MDCShadowPerformanceExample.exampleTitle |
| |
| collectionView.dataSource = self |
| collectionView.delegate = self |
| collectionView.backgroundColor = containerScheme.colorScheme.backgroundColor |
| collectionView.alwaysBounceVertical = true |
| collectionView.register(MDCCardCollectionCell.self, forCellWithReuseIdentifier: cellIdentifier) |
| collectionView.translatesAutoresizingMaskIntoConstraints = false |
| view.addSubview(collectionView) |
| view.addSubview(appBar.view) |
| appBar.headerView.trackingScrollView = collectionView |
| |
| createButton.sizeToFit() |
| let plusImage = UIImage(named: "system_icons/add") |
| createButton.setImage(plusImage, for: .normal) |
| createButton.accessibilityLabel = "Create" |
| createButton.applySecondaryTheme(withScheme: containerScheme) |
| view.addSubview(createButton) |
| |
| view.addSubview(bottomNavBar) |
| bottomNavBar.titleVisibility = .always |
| bottomNavBar.alignment = .centered |
| let tabBarItem1 = UITabBarItem( |
| title: "Home", image: UIImage(named: "system_icons/home"), tag: 0) |
| let tabBarItem2 = |
| UITabBarItem(title: "Messages", image: UIImage(named: "system_icons/email"), tag: 1) |
| bottomNavBar.items = [tabBarItem1, tabBarItem2] |
| bottomNavBar.selectedItem = tabBarItem2 |
| } |
| |
| override func viewWillLayoutSubviews() { |
| super.viewWillLayoutSubviews() |
| |
| let width = view.bounds.width |
| let height = view.bounds.height |
| var safeArea: UIEdgeInsets = .zero |
| safeArea = view.safeAreaInsets |
| collectionView.contentInsetAdjustmentBehavior = .always |
| |
| collectionView.frame = CGRect( |
| origin: .zero, size: CGSize(width: width, height: height - (safeArea.bottom + safeArea.top))) |
| |
| let size = bottomNavBar.sizeThatFits(view.bounds.size) |
| var bottomNavBarFrame = CGRect( |
| x: 0, |
| y: height - size.height, |
| width: size.width, |
| height: size.height) |
| bottomNavBarFrame.size.height += safeArea.bottom |
| bottomNavBarFrame.origin.y -= safeArea.bottom |
| bottomNavBar.frame = bottomNavBarFrame |
| |
| let createButtonDimension: CGFloat = 56 |
| let createButtonPadding: CGFloat = 16 |
| createButton.frame = CGRect( |
| x: width - (createButtonDimension + safeArea.left + createButtonPadding), |
| y: bottomNavBarFrame.origin.y - (createButtonDimension + createButtonPadding), |
| width: createButtonDimension, |
| height: createButtonDimension |
| ) |
| } |
| |
| private func makeAppBar() -> MDCAppBarViewController { |
| let appBarViewController = MDCAppBarViewController() |
| addChild(appBarViewController) |
| appBarViewController.headerView.minMaxHeightIncludesSafeArea = false |
| appBarViewController.inferTopSafeAreaInsetFromViewController = true |
| appBarViewController.headerView.canOverExtend = false |
| return appBarViewController |
| } |
| |
| // MARK: Collection View methods |
| |
| func collectionView( |
| _ collectionView: UICollectionView, |
| cellForItemAt indexPath: IndexPath |
| ) -> UICollectionViewCell { |
| |
| let cell = collectionView.dequeueReusableCell( |
| withReuseIdentifier: cellIdentifier, for: indexPath) |
| guard let cardCell = cell as? MDCCardCollectionCell else { return cell } |
| |
| cardCell.enableRippleBehavior = true |
| cardCell.isAccessibilityElement = true |
| cardCell.accessibilityLabel = title |
| cardCell.setShadowElevation(.cardResting, for: .normal) |
| cardCell.applyTheme(withScheme: containerScheme) |
| |
| return cardCell |
| } |
| |
| func numberOfSections(in collectionView: UICollectionView) -> Int { |
| return 1 |
| } |
| |
| func collectionView( |
| _ collectionView: UICollectionView, |
| numberOfItemsInSection section: Int |
| ) -> Int { |
| return cellCount |
| } |
| |
| func collectionView( |
| _ collectionView: UICollectionView, |
| layout collectionViewLayout: UICollectionViewLayout, |
| sizeForItemAt indexPath: IndexPath |
| ) -> CGSize { |
| let cardSize = (collectionView.bounds.size.width / 3) - 12 |
| return CGSize(width: cardSize, height: cardSize) |
| } |
| |
| func collectionView( |
| _ collectionView: UICollectionView, |
| layout collectionViewLayout: UICollectionViewLayout, |
| insetForSectionAt section: Int |
| ) -> UIEdgeInsets { |
| return UIEdgeInsets( |
| top: defaultPadding, |
| left: defaultPadding, |
| bottom: defaultPadding, |
| right: defaultPadding |
| ) |
| } |
| |
| func collectionView( |
| _ collectionView: UICollectionView, |
| layout collectionViewLayout: UICollectionViewLayout, |
| minimumLineSpacingForSectionAt section: Int |
| ) -> CGFloat { |
| return defaultPadding |
| } |
| |
| func collectionView( |
| _ collectionView: UICollectionView, |
| layout collectionViewLayout: UICollectionViewLayout, |
| minimumInteritemSpacingForSectionAt section: Int |
| ) -> CGFloat { |
| return defaultPadding |
| } |
| } |
| |
| // MARK: Catalog by convention |
| extension MDCShadowPerformanceExample { |
| @objc class func catalogMetadata() -> [String: Any] { |
| return [ |
| "breadcrumbs": ["Shadow", MDCShadowPerformanceExample.exampleTitle], |
| "primaryDemo": false, |
| "presentable": true, |
| "description": """ |
| This example is not intended to demonstrate how to setup any specific components but rather to |
| push `MDCShadowLayer` to the limits for metric testing. |
| """, |
| ] |
| } |
| |
| @objc func catalogShouldHideNavigation() -> Bool { |
| return true |
| } |
| } |