blob: 54a40eebdbeb4b3742e5e103deeb9e8fc2ff68b8 [file] [log] [blame]
// 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
}
}