diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6979b0784b8..0960d32d9e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,132 @@
+# 112.0.0
+
+This major releases deletes several deprecated APIs, expands Ripple support, and fixes several bugs.
+
+## Breaking changes
+
+Buttons' `MDCTitleColorAccessibilityMutator` and FeatureHighlight's
+`MDCFeatureHighlightAccessibilityMutator` have been deleted. There are no official replacements for
+these APIs.
+
+`MDCHeaderStackViewColorThemer` has been deleted. There is no replacement for this API; use the
+AppBar Theming APIs instead.
+
+`MDCSnackbarFontThemer` and `MDCSnackbarTypographyThemer` have been deleted. Use the Snackbar
+Theming APIs instead.
+
+## New features
+
+Buttons has several new Ripple APIs, including `rippleStyle`, `rippleColor`, `rippleEdgeInsets`,
+and `rippleMaximumRadius`. These APIs are intended to act as replacements for the similar Ink APIs.
+
+Chips similarly has added a `rippleForState` API.
+
+TabBarView now has a Theming extension.
+
+TextControls now expose a `preferredContainerHeight` API.
+
+## API changes
+
+## Component changes
+
+### BottomAppBar
+
+* [Material io bottom app bar](https://github.com/material-components/material-components-ios/commit/d6255fa0f431f3ee62b83637f0a50fb07c153292) (Andrew Overton)
+
+### BottomNavigation
+
+* [When large content view is enabled, on long presses it will correctly switch to the last selected item](https://github.com/material-components/material-components-ios/commit/3481620f18b9c3e62d1f9a5858f362843269099b) (Alyssa Weiss)
+
+### ButtonBar
+
+* [Fix ButtonBar typical example's crash by making containerScheme a property.](https://github.com/material-components/material-components-ios/commit/0a0f7347fac7639ac132934705e88f69d7fedc6b) (Wenyu Zhang)
+
+### Buttons
+
+* [Add rippleColor and rippleStyle APIs](https://github.com/material-components/material-components-ios/commit/9039d47602220f44f5ea2fa72d1683173e35b5c8) (Cody Weaver)
+* [Add rippleEdgeInsets API.](https://github.com/material-components/material-components-ios/commit/330125967a95335c7c678982715b2bbdb085d9de) (Cody Weaver)
+* [Add rippleMaximumRadius API.](https://github.com/material-components/material-components-ios/commit/2a5996b407f3ba821672bf35cee5cace2bebdbd8) (Cody Weaver)
+* [Fix lint error `UppercaseAttributedString` in `MDCButton`](https://github.com/material-components/material-components-ios/commit/3132beea793356e3b0f6cb77c32c9c5a1a23e68b) (Nobody)
+* [Remove unused TitleColorAccessibilityMutator.](https://github.com/material-components/material-components-ios/commit/7b45e787db8aaa9885e8c42bb94aa6406658f3dc) (Bryan Oltman)
+
+### Chips
+
+* [Add rippleForState APIs.](https://github.com/material-components/material-components-ios/commit/8a509d558ffdf3225b56823140c632fb2aa39ca1) (Cody Weaver)
+
+### Dialogs
+
+* [Improved titleIconView example with animation.](https://github.com/material-components/material-components-ios/commit/89ddea0cf1a5d6b646be808f288978318d7ce0dc) (Galia Kaufman)
+
+### FeatureHighlight
+
+* [Delete deprecated FeatureHighlightAccessibilityMutator.](https://github.com/material-components/material-components-ios/commit/180a5bf724f0fac5fa824660cca1c11a02de9721) (Bryan Oltman)
+
+### HeaderStackView
+
+* [Delete deprecated MDCHeaderStackViewColorThemer](https://github.com/material-components/material-components-ios/commit/77c7bfae5f113d90971c95f20fccb82cd3fe1cd2) (Bryan Oltman)
+
+### ProgressView
+
+* [Remove request for VoiceOver focus in MDCProgressView's setHidden.](https://github.com/material-components/material-components-ios/commit/1b68b485221f21a1a4ee4655f3971a3b56b769e4) (Bryan Oltman)
+
+### Shapes
+
+* [Add unit tests demonstrating a divide by zero error.](https://github.com/material-components/material-components-ios/commit/fb4f43d36c7b1887f8396dada4d88177ea32d3d7) (Jeff Verkoeyen)
+* [Fix divide by zero bug.](https://github.com/material-components/material-components-ios/commit/2db3fd1ced49aefcd3d440e91e7fbc675967053d) (Jeff Verkoeyen)
+* [Fixes for the shape shadow layer if the borderWidth is set multiple times and there isn't a prepareShadowPath pass.](https://github.com/material-components/material-components-ios/commit/a5ddb265b2967e449e18ab228f1da841e3bf4ae3) (Yarden Eitan)
+* [Update shapeLayer to the correct path to correctly be a mask for content.](https://github.com/material-components/material-components-ios/commit/f4263fe70ed103b05912bec7cdf48b271d3b31ca) (Yarden Eitan)
+* [Updates the shapeGenerator with a line width to inset the line rather than center it.](https://github.com/material-components/material-components-ios/commit/3f3816f014bc4c5f0760c8838a0dc6916dd42f33) (Yarden Eitan)
+
+### Slider
+
+* [Add EarlGrey test to expose bug where tapping on slider track opposite anchor point doesn't trigger UIControlEventValueChanged](https://github.com/material-components/material-components-ios/commit/0495b7987138dab2608f57772fdc5869481e59a7) (Bryan Oltman)
+* [Remove supplemental directory from examples.](https://github.com/material-components/material-components-ios/commit/de482d077aa4fa1310bcb268952ce98a6d2291ba) (Bryan Oltman)
+
+### Snackbar
+
+* [Delete deprecated MDCSnackbarFontThemer.](https://github.com/material-components/material-components-ios/commit/40e66874e6ab9000226e825b75d71f91c93e07c0) (Bryan Oltman)
+* [Delete deprecated MDCSnackbarTypographyThemer.](https://github.com/material-components/material-components-ios/commit/9b23c3474ad643d9ae63e6b163cd989e0c36d135) (Bryan Oltman)
+* [Use different margins for multi-line snackbars](https://github.com/material-components/material-components-ios/commit/d6498592978d0ef3e22f11a7c49fc9f1aad25547) (Alyssa Weiss)
+
+### Tabs
+
+* [Adding theming extension for TabBarView](https://github.com/material-components/material-components-ios/commit/6f3b23dfb83d883b88aa061c05de4dc6c4d72a0c) (Alyssa Weiss)
+
+### TextControls
+
+* [Add debug stuff to text controls examples](https://github.com/material-components/material-components-ios/commit/93fb8836455d7716810afa054e09ccb9e4e02220) (Andrew Overton)
+* [Expose preferredContainerHeight on MDCBaseTextField](https://github.com/material-components/material-components-ios/commit/e360f3c1c4e26bfad204876072241de753fc261e) (Andrew Overton)
+* [Fix jumpiness in text areas](https://github.com/material-components/material-components-ios/commit/66497f78375748c4d71d1a45a82420ff438373e4) (Andrew Overton)
+* [Fix secureTextEntry layout pass infinite loop](https://github.com/material-components/material-components-ios/commit/b18a9191cc84973f74003e277627676bb245177f) (Andrew Overton)
+
+### private/Snapshot
+
+* [Internal change.](https://github.com/material-components/material-components-ios/commit/8410fe26310d619a73d3ca54d2a143b33d4f9ffb) (Wenyu Zhang)
+
+### private/TextControlsPrivate
+
+* [Make filled positioning reference subclass NSObject](https://github.com/material-components/material-components-ios/commit/ae6938670e6083c94f300cbd2e9484d69ec060a4) (Andrew Overton)
+
+### private/ThumbTrack
+
+* [Add centerVisibleArea support with snapshot tests.](https://github.com/material-components/material-components-ios/commit/a142aae3e7be48a732d1730c1858f9c15125abc3) (Wenyu Zhang)
+* [Add snapshot tests to MDCThumbTrack.](https://github.com/material-components/material-components-ios/commit/e4efea4264dd431768cc491c8e442624f4d39937) (Wenyu Zhang)
+* [Fix an issue where sliders with non-min/max anchor points do not publish a UIControlEventValueChanged event when a track tap causes the value to cross the anchor point.](https://github.com/material-components/material-components-ios/commit/c5dbf01d5a072596ee9447a67e6dff5090936ea7) (Bryan Oltman)
+* [Internal Change](https://github.com/material-components/material-components-ios/commit/6aea60a148025c15ddf8b8558192f4366abfb672) (Alyssa Weiss)
+* [Use shapeGenerator to render the backing layer.](https://github.com/material-components/material-components-ios/commit/634db350f56c83c4337bbe7041be86beb347734f) (Wenyu Zhang)
+
+### schemes/Shape
+
+* [Add test demonstrating issue with calling borderWidth multiple times with same value.](https://github.com/material-components/material-components-ios/commit/719d68811944b8d20e92d83db3d95d6844dedc70) (Yarden Eitan)
+
+## Multi-component changes
+
+* [Allow setting underline thickness regardless of state](https://github.com/material-components/material-components-ios/commit/0950ece8f3d980d9fd13cd2718dc32e67e4ed18c) (Andrew Overton)
+* [Prepare to expose preferredContainerHeight on text controls](https://github.com/material-components/material-components-ios/commit/49b441e84976df1d1f70b1809c2630c5073f1a44) (Andrew Overton)
+* [Replace material.io API doc links with links to the relevant header f…](https://github.com/material-components/material-components-ios/commit/07c46757cc73d995eddc6205455d8c480f3270b5) (Andrew Overton)
+
+---
+
# 111.0.0
In this Major release we removed some deprecated action sheet themers, made visual improvements to Chips, ProgressView, Banner and Tabs. We also made links clickable in Alert Dialogs
diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec
index 788c7c5dede..725ad915021 100644
--- a/MaterialComponents.podspec
+++ b/MaterialComponents.podspec
@@ -2,7 +2,7 @@ load 'scripts/generated/icons.rb'
Pod::Spec.new do |mdc|
mdc.name = "MaterialComponents"
- mdc.version = "111.0.0"
+ mdc.version = "112.0.0"
mdc.authors = "The Material Components authors."
mdc.summary = "A collection of stand-alone production-ready UI libraries focused on design details."
mdc.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/MaterialComponentsBeta.podspec b/MaterialComponentsBeta.podspec
index abdc3f5ec01..18a20e9aff0 100644
--- a/MaterialComponentsBeta.podspec
+++ b/MaterialComponentsBeta.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |mdc|
mdc.name = "MaterialComponentsBeta"
- mdc.version = "111.0.0"
+ mdc.version = "112.0.0"
mdc.authors = "The Material Components authors."
mdc.summary = "A collection of stand-alone alpha UI libraries that are not yet guaranteed to be ready for general production use. Use with caution."
mdc.homepage = "https://github.com/material-components/material-components-ios"
@@ -48,6 +48,25 @@ Pod::Spec.new do |mdc|
end
end
+ mdc.subspec "Tabs+TabBarViewTheming" do |extension|
+ extension.ios.deployment_target = '9.0'
+ extension.public_header_files = "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.h"
+ extension.source_files = [
+ "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/*.{h,m}",
+ "components/#{extension.base_name.split('+')[0]}/src/#{extension.base_name.split('+')[1]}/private/*.{h,m}"
+ ]
+ extension.dependency "MaterialComponentsBeta/#{extension.base_name.split('+')[0]}+TabBarView"
+ extension.dependency "MaterialComponents/schemes/Container"
+
+ extension.test_spec 'UnitTests' do |unit_tests|
+ unit_tests.source_files = [
+ "components/#{extension.base_name.split('+')[0]}/tests/unit/TabBarView/MDCTabBarViewThemingTests.m",
+ ]
+ unit_tests.dependency "MaterialComponents/schemes/Container"
+ end
+ end
+
+
# Private
mdc.subspec "private" do |private_spec|
diff --git a/MaterialComponentsEarlGreyTests.podspec b/MaterialComponentsEarlGreyTests.podspec
index fd722e66b6e..216a8ddf4ce 100644
--- a/MaterialComponentsEarlGreyTests.podspec
+++ b/MaterialComponentsEarlGreyTests.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialComponentsEarlGreyTests"
- s.version = "111.0.0"
+ s.version = "112.0.0"
s.authors = "The Material Components authors."
s.summary = "This spec is an aggregate of all the Material Components EarlGrey tests."
s.description = "This spec is made for use in the MDC Catalog."
diff --git a/MaterialComponentsExamples.podspec b/MaterialComponentsExamples.podspec
index a8b360c6463..038ff821185 100644
--- a/MaterialComponentsExamples.podspec
+++ b/MaterialComponentsExamples.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialComponentsExamples"
- s.version = "111.0.0"
+ s.version = "112.0.0"
s.authors = "The Material Components authors."
s.summary = "This spec is an aggregate of all the Material Components examples."
s.description = "This spec is made for use in the MDC Catalog. Used in conjunction with CatalogByConvention we create our Material Catalog."
diff --git a/MaterialComponentsSnapshotTests.podspec b/MaterialComponentsSnapshotTests.podspec
index 4b8784ea271..1d95fa20235 100644
--- a/MaterialComponentsSnapshotTests.podspec
+++ b/MaterialComponentsSnapshotTests.podspec
@@ -53,7 +53,7 @@ end
Pod::Spec.new do |s|
s.name = "MaterialComponentsSnapshotTests"
- s.version = "111.0.0"
+ s.version = "112.0.0"
s.authors = "The Material Components authors."
s.summary = "This spec is an aggregate of all the Material Components snapshot tests."
s.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/VERSION b/VERSION
index 12cd89fb66d..6d852789748 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-111.0.0
+112.0.0
diff --git a/catalog/MDCCatalog/Info.plist b/catalog/MDCCatalog/Info.plist
index 0bdf2b1bef3..b192a35ef14 100644
--- a/catalog/MDCCatalog/Info.plist
+++ b/catalog/MDCCatalog/Info.plist
@@ -15,11 +15,11 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 111.0.0
+ 112.0.0
CFBundleSignature
????
CFBundleVersion
- 111.0.0
+ 112.0.0
LSRequiresIPhoneOS
UIAppFonts
diff --git a/catalog/MDCDragons/Info.plist b/catalog/MDCDragons/Info.plist
index 0988818b047..0f2bb89a6f3 100644
--- a/catalog/MDCDragons/Info.plist
+++ b/catalog/MDCDragons/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 111.0.0
+ 112.0.0
CFBundleVersion
- 111.0.0
+ 112.0.0
LSRequiresIPhoneOS
UILaunchStoryboardName
diff --git a/catalog/MaterialCatalog/MaterialCatalog.podspec b/catalog/MaterialCatalog/MaterialCatalog.podspec
index a102022349d..60b061982b3 100644
--- a/catalog/MaterialCatalog/MaterialCatalog.podspec
+++ b/catalog/MaterialCatalog/MaterialCatalog.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialCatalog"
- s.version = "111.0.0"
+ s.version = "112.0.0"
s.summary = "Helper Objective-C classes for the MDC catalog."
s.description = "This spec is made for use in the MDC Catalog."
s.homepage = "https://github.com/material-components/material-components-ios"
diff --git a/components/ActionSheet/README.md b/components/ActionSheet/README.md
index 64f86dc0358..055331412d8 100644
--- a/components/ActionSheet/README.md
+++ b/components/ActionSheet/README.md
@@ -22,9 +22,9 @@ the screen and displays actions a user can take.
## Design & API documentation
## Table of contents
diff --git a/components/ActivityIndicator/README.md b/components/ActivityIndicator/README.md
index 8ecbefa863b..bfe673707a4 100644
--- a/components/ActivityIndicator/README.md
+++ b/components/ActivityIndicator/README.md
@@ -26,11 +26,10 @@ This component only provides the circular implementation. See
## Related components
diff --git a/components/BottomAppBar/README.md b/components/BottomAppBar/README.md
index 32921ebfa59..14e2130a6a3 100644
--- a/components/BottomAppBar/README.md
+++ b/components/BottomAppBar/README.md
@@ -2,86 +2,82 @@
title: "App bars: bottom"
layout: detail
section: components
-excerpt: "A bottom app bar displays navigation and key actions at the bottom of the screen."
+excerpt: "A bottom app bar displays navigation and key actions at the bottom of mobile screens."
iconId: bottom_app_bar
path: /catalog/bottomappbar/
api_doc_root: true
-->
-
-
# App bars: bottom
[![Open bugs badge](https://img.shields.io/badge/dynamic/json.svg?label=open%20bugs&url=https%3A%2F%2Fapi.github.com%2Fsearch%2Fissues%3Fq%3Dis%253Aopen%2Blabel%253Atype%253ABug%2Blabel%253A%255BBottomAppBar%255D&query=%24.total_count)](https://github.com/material-components/material-components-ios/issues?q=is%3Aopen+is%3Aissue+label%3Atype%3ABug+label%3A%5BBottomAppBar%5D)
-A bottom app bar displays navigation and key actions at the bottom of the screen. Bottom app bars
-work like [navigation bars](../NavigationBar), but with the additional option to show a
-[floating action button](../Buttons).
+[Bottom app bars](https://material.io/components/app-bars-bottom/) display navigation and key actions at the bottom of the screen.
-
-
-
+![Bottom app bar hero](docs/assets/bottom-app-bar-hero.png)
-## Design & API documentation
+## Contents
-
+* [Using bottom app bars](#using-bottom-app-bars)
+* [Installing bottom app bars](#installing-bottom-app-bars)
+* [Making bottom app bars accessible](#making-bottom-app-bars-accessible)
+* [Bottom app bar anatomy](#bottom-app-bar-anatomy)
+* [Theming bottom app bars](#theming-bottom-app-bars)
-## Table of contents
+## Using bottom app bars
-- [Overview](#overview)
-- [Installation](#installation)
- - [Installation with CocoaPods](#installation-with-cocoapods)
- - [Importing](#importing)
-- [Usage](#usage)
- - [Typical use](#typical-use)
-- [Extensions](#extensions)
- - [Color Theming](#color-theming)
-- [Accessibility](#accessibility)
- - [Set `-accessibilityLabel`](#set-`-accessibilitylabel`)
- - [Set `-accessibilityHint`](#set-`-accessibilityhint`)
+Bottom app bars group primary and secondary actions at the bottom of the screen, where they are easily reachable by the user's thumb.
-- - -
+Use the `UIView` subclass `MDCBottomAppBarView` to add a bottom app bar to your app. `MDCBottomAppBarView` contains a horizontally centered [floating action button](https://github.com/material-components/material-components-ios/blob/stable/components/Buttons/src/MDCFloatingButton.h) for primary actions and a customizable [navigation bar](https://material.io/components/ios/catalog/flexible-headers/navigation-bars/) for secondary actions. The `MDCBottomAppBarView` API includes properties that allow changes in elevation, position, and visibility of the embedded floating action button.
-## Overview
+Instances of `UIBarButtonItem` can be added to a `MDCBottomAppBarView`'s navigation bar. Leading and trailing navigation items will be shown and hidden based on the position of the floating action button.
-Bottom app bars follow a recommended Material Design interaction design pattern for providing primary and secondary actions that are easily accessible. With a bottom app bar users are more easily able to use single-handed touch interaction with an application since actions are displayed close to the bottom of the screen within easy reach of a user's thumb.
+Transitions between floating action button position, elevation, and visibility states are animated by default, but can be disabled if desired.
-The bottom app bar includes a floating action button that is intended to provide users with a primary action. Secondary actions are available on a navigation bar that can be customized with several buttons on the left and right sides of the navigation bar. The primary action floating action button is centered on the bottom app bar by default.
+### Bottom app bar example
-MDCBottomAppBarView should be attached to the bottom of the screen or used in conjunction with an expandable bottom drawer. The MDCBottomAppBarView API includes properties that allow changes to the elevation, position and visibility of the embedded floating action button.
+`MDCBottomAppBarView` can be added to a view hierarchy like any `UIView`. Material Design guidelines
+recommend always placing the bottom app bar at the bottom of the screen.
-UIBarButtonItems can be added to the navigation bar of the MDCBottomAppBarView. Leading and trailing navigation items will be shown and hidden based on the position of the floating action button.
+
+#### Swift
-Transitions between floating action button position, elevation and visibility states are animated by default, but can be disabled if desired.
+```swift
+let bottomAppBar = MDCBottomAppBarView()
+addSubview(bottomAppBar)
+view.leftAnchor.constraint(equalTo: bottomAppBarView.leftAnchor).isActive = true
+view.rightAnchor.constraint(equalTo: bottomAppBarView.rightAnchor).isActive = true
+view.bottomAnchor.constraint(equalTo: bottomAppBarView.bottomAnchor).isActive = true
+```
+
+#### Objective-C
-## Installation
+```objc
+MDCBottomAppBarView *bottomAppBar = [[MDCBottomAppBarView alloc] init];
+[self addSubview:bottomAppBar];
+[self.view.leftAnchor constraintEqualToAnchor:bottomAppBarView.leftAnchor].active = YES;
+[self.view.rightAnchor constraintEqualToAnchor:bottomAppBarView.rightAnchor].active = YES;
+[self.view.bottomAnchor constraintEqualToAnchor:self.textField.bottomAnchor].active = YES;
+```
-
+
-### Installation with CocoaPods
+## Installing `MDCBottomAppBarView`
-Add the following to your `Podfile`:
+In order to use `MDCBottomAppBarView`, first add the component to your `Podfile`:
```bash
-pod 'MaterialComponents/BottomAppBar'
+pod MaterialComponents/BottomAppBar
```
-Then, run the following command:
+Then, run the installer.
```bash
pod install
```
-### Importing
-
-To import the component:
+After that, import the component target.
#### Swift
@@ -90,38 +86,16 @@ import MaterialComponents.MaterialBottomAppBar
```
#### Objective-C
-
```objc
#import "MaterialBottomAppBar.h"
```
+From there, initialize an `MDCBottomAppBarView` like you would any `UIView`.
-## Usage
-
-
-
-### Typical use
-
-MDCBottomAppBarView can be added to a view hierarchy like any UIView. Material Design guidelines
-recommend always placing the bottom app bar at the bottom of the screen.
-
+## Making bottom app bars accessible
-## Extensions
-
-
-
-### Color Theming
-
-MDCBottomAppBarView does not yet have a Material Design color system theming extension. Please
-indicate interest by commenting on https://github.com/material-components/material-components-ios/issues/7172.
-
-## Accessibility
-
-
-
-To help ensure your bottom app bar is accessible to as many users as possible, please be sure to review the
-following recommendations:
+The following recommendations will ensure that the bottom app bar is accessible to as many users as possible:
### Set `-accessibilityLabel`
@@ -183,3 +157,39 @@ trailingButton.accessibilityHint = @"Purchase the item";
```
+## Bottom app bar anatomy
+
+A bottom app bar has a container and an optional navigation icon, anchored
+floating action button (FAB), action item(s) and an overflow menu.
+
+![Bottom app bar anatomy diagram](docs/assets/bottom-app-bar-anatomy.png)
+
+1. Container
+2. Navigation icon (optional)
+3. Floating action button (FAB) (optional)
+4. Action item(s) (optional)
+5. Overflow menu (optional)
+
+### Container attributes
+
+ | Attribute | Related method(s) | Default value
+------------- | -------------------- | ------------------------------------------ | -------------
+**Color** | `barTintColor` | `-setBarTintColor:`
`-barTintColor` | White
+**Elevation** | `elevation` | `-setElevation:`
`-elevation` | 8
+
+### Navigation icon attributes
+
+ | Attribute | Related method(s) | Default value
+--------- | -------------------- | ------------------------------------------ | -------------
+**Icon** | `leadingBarButtonItems`
`trailingBarButtonItems` | `-setLeadingBarButtonItems:`
`-leadingBarButtonItems`
`-setTrailingBarButtonItems:`
`-trailingBarButtonItems` | `nil`
+
+### FAB attributes
+
+ | Attribute | Related method(s) | Default value
+-------------------------------- | ---------------------------------- | ---------------------------------------------------------------------- | -------------
+**Alignment mode** | `floatingButtonPosition` | `-setFloatingButtonPosition:`
`-floatingButtonPosition` | `.center`
+**Elevation** | `floatingButtonElevation` | `-setFloatingButtonElevation:`
`-floatingButtonElevation` | 0
+
+## Theming bottom app bars
+
+`MDCBottomAppBarView` does not currently have a Material Design theming extension or otherwise support theming. Please indicate interest in adding theming support by commenting on [issue #7172](https://github.com/material-components/material-components-ios/issues/7172).
diff --git a/components/BottomAppBar/docs/README.md b/components/BottomAppBar/docs/README.md
deleted file mode 100644
index e5c27fdffb6..00000000000
--- a/components/BottomAppBar/docs/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# App bars: bottom
-
-
-
-A bottom app bar displays navigation and key actions at the bottom of the screen. Bottom app bars
-work like [navigation bars](../../NavigationBar), but with the additional option to show a
-[floating action button](../../Buttons).
-
-
-
-
-
-
-
-
-
-- - -
-
-## Overview
-
-Bottom app bars follow a recommended Material Design interaction design pattern for providing primary and secondary actions that are easily accessible. With a bottom app bar users are more easily able to use single-handed touch interaction with an application since actions are displayed close to the bottom of the screen within easy reach of a user's thumb.
-
-The bottom app bar includes a floating action button that is intended to provide users with a primary action. Secondary actions are available on a navigation bar that can be customized with several buttons on the left and right sides of the navigation bar. The primary action floating action button is centered on the bottom app bar by default.
-
-MDCBottomAppBarView should be attached to the bottom of the screen or used in conjunction with an expandable bottom drawer. The MDCBottomAppBarView API includes properties that allow changes to the elevation, position and visibility of the embedded floating action button.
-
-UIBarButtonItems can be added to the navigation bar of the MDCBottomAppBarView. Leading and trailing navigation items will be shown and hidden based on the position of the floating action button.
-
-Transitions between floating action button position, elevation and visibility states are animated by default, but can be disabled if desired.
-
-## Installation
-
-- [Typical installation](../../../docs/component-installation.md)
-
-## Usage
-
-- [Typical use](typical-use.md)
-
-## Extensions
-
-- [Color Theming](color-theming.md)
-
-## Accessibility
-
-- [Accessibility](accessibility.md)
diff --git a/components/BottomAppBar/docs/accessibility.md b/components/BottomAppBar/docs/accessibility.md
deleted file mode 100644
index 9abf43ba794..00000000000
--- a/components/BottomAppBar/docs/accessibility.md
+++ /dev/null
@@ -1,62 +0,0 @@
-To help ensure your bottom app bar is accessible to as many users as possible, please be sure to review the
-following recommendations:
-
-### Set `-accessibilityLabel`
-
-Set an appropriate
-[`accessibilityLabel`](https://developer.apple.com/documentation/uikit/uiaccessibilityelement/1619577-accessibilitylabel)
-to all buttons within the bottom app bar.
-
-
-#### Swift
-
-```swift
-bottomAppBar.floatingButton.accessibilityLabel = "Compose"
-let trailingButton = UIBarButtonItem()
-trailingButton.accessibilityLabel = "Buy"
-bottomAppBar.trailingBarButtonItems = [ trailingButton ]
-```
-
-#### Objective-C
-
-```objc
-bottomAppBar.floatingButton.accessibilityLabel = @"Compose";
-UIBarButtonItem *trailingButton =
- [[UIBarButtonItem alloc] initWithTitle:nil
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(didTapTrailing:)];
-trailingButton.accessibilityLabel = @"Buy";
-[bottomAppBar setTrailingBarButtonItems:@[ trailingButton ]];
-```
-
-
-### Set `-accessibilityHint`
-
-Set an appropriate
-[`accessibilityHint`](https://developer.apple.com/documentation/objectivec/nsobject/1615093-accessibilityhint)
-to all buttons within the bottom app bar.
-
-
-#### Swift
-
-```swift
-bottomAppBar.floatingButton.accessibilityHint = "Create new email"
-let trailingButton = UIBarButtonItem()
-trailingButton.accessibilityHint = "Purchase the item"
-bottomAppBar.trailingBarButtonItems = [ trailingButton ]
-```
-
-#### Objective-C
-
-```objc
-bottomAppBar.floatingButton.accessibilityHint = @"Create new email";
-UIBarButtonItem *trailingButton =
- [[UIBarButtonItem alloc] initWithTitle:nil
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(didTapTrailing:)];
-trailingButton.accessibilityHint = @"Purchase the item";
-[bottomAppBar setTrailingBarButtonItems:@[ trailingButton ]];
-```
-
diff --git a/components/BottomAppBar/docs/assets/bottom-app-bar-anatomy.png b/components/BottomAppBar/docs/assets/bottom-app-bar-anatomy.png
new file mode 100644
index 00000000000..2808d532df4
Binary files /dev/null and b/components/BottomAppBar/docs/assets/bottom-app-bar-anatomy.png differ
diff --git a/components/BottomAppBar/docs/assets/bottom-app-bar-hero.png b/components/BottomAppBar/docs/assets/bottom-app-bar-hero.png
new file mode 100644
index 00000000000..58819cfbf20
Binary files /dev/null and b/components/BottomAppBar/docs/assets/bottom-app-bar-hero.png differ
diff --git a/components/BottomAppBar/docs/color-theming.md b/components/BottomAppBar/docs/color-theming.md
deleted file mode 100644
index 5dcb19ec69f..00000000000
--- a/components/BottomAppBar/docs/color-theming.md
+++ /dev/null
@@ -1,4 +0,0 @@
-### Color Theming
-
-MDCBottomAppBarView does not yet have a Material Design color system theming extension. Please
-indicate interest by commenting on https://github.com/material-components/material-components-ios/issues/7172.
diff --git a/components/BottomAppBar/docs/typical-use.md b/components/BottomAppBar/docs/typical-use.md
deleted file mode 100644
index 8e6c5a9d61b..00000000000
--- a/components/BottomAppBar/docs/typical-use.md
+++ /dev/null
@@ -1,4 +0,0 @@
-### Typical use
-
-MDCBottomAppBarView can be added to a view hierarchy like any UIView. Material Design guidelines
-recommend always placing the bottom app bar at the bottom of the screen.
diff --git a/components/BottomNavigation/README.md b/components/BottomNavigation/README.md
index 8118698d49b..1b6a072170e 100644
--- a/components/BottomNavigation/README.md
+++ b/components/BottomNavigation/README.md
@@ -206,7 +206,6 @@ bottomNavBar.frame = bottomNavBarFrame
API and source code:
* `MDCBottomNavigationBar`
- * [Class reference](https://material.io/components/ios/catalog/bottomnavigation/api-docs/Classes/MDCBottomNavigationBar.html)
* [Class source](https://github.com/material-components/material-components-ios/blob/develop/components/BottomNavigation/src/MDCBottomNavigationBar.h)
To achieve something like the example image above, add the following code to your view controller:
diff --git a/components/BottomNavigation/src/MDCBottomNavigationBar.m b/components/BottomNavigation/src/MDCBottomNavigationBar.m
index bfa65f1c978..d4e26a41dd7 100644
--- a/components/BottomNavigation/src/MDCBottomNavigationBar.m
+++ b/components/BottomNavigation/src/MDCBottomNavigationBar.m
@@ -916,6 +916,15 @@ - (CGFloat)mdc_currentElevation {
- (void)largeContentViewerInteraction:(UILargeContentViewerInteraction *)interaction
didEndOnItem:(id)item
atPoint:(CGPoint)point NS_AVAILABLE_IOS(13_0) {
+ if (item) {
+ for (NSUInteger i = 0; i < self.items.count; i++) {
+ MDCBottomNavigationItemView *itemView = self.itemViews[i];
+ if (item == itemView) {
+ [self didTouchUpInsideButton:itemView.button];
+ }
+ }
+ }
+
self.lastLargeContentViewerItem = nil;
}
#endif // MDC_AVAILABLE_SDK_IOS(13_0)
diff --git a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m
index 046e9e0481c..ca0607150af 100644
--- a/components/ButtonBar/examples/ButtonBarTypicalUseExample.m
+++ b/components/ButtonBar/examples/ButtonBarTypicalUseExample.m
@@ -20,6 +20,7 @@
@interface ButtonBarTypicalUseExample : UIViewController
@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
@property(nonatomic, strong) MDCTypographyScheme *typographyScheme;
+@property(nonatomic, strong) MDCContainerScheme *containerScheme;
@end
@implementation ButtonBarTypicalUseExample
@@ -27,22 +28,19 @@ @implementation ButtonBarTypicalUseExample
- (id)init {
self = [super init];
if (self) {
- self.colorScheme =
+ _colorScheme =
[[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804];
- self.typographyScheme =
+ _typographyScheme =
[[MDCTypographyScheme alloc] initWithDefaults:MDCTypographySchemeDefaultsMaterial201804];
+ MDCContainerScheme *containerScheme = [[MDCContainerScheme alloc] init];
+ containerScheme.colorScheme = _colorScheme;
+ containerScheme.typographyScheme = _typographyScheme;
+ _containerScheme = containerScheme;
self.title = @"Button Bar";
}
return self;
}
-- (MDCContainerScheme *)containerScheme {
- MDCContainerScheme *scheme = [[MDCContainerScheme alloc] init];
- scheme.colorScheme = self.colorScheme;
- scheme.typographyScheme = self.typographyScheme;
- return scheme;
-}
-
- (void)viewDidLoad {
[super viewDidLoad];
diff --git a/components/Buttons/docs/buttons.md b/components/Buttons/docs/buttons.md
index d3a143c6042..b6717063d96 100644
--- a/components/Buttons/docs/buttons.md
+++ b/components/Buttons/docs/buttons.md
@@ -25,7 +25,6 @@ There are four types of buttons:
## Using buttons
All Material buttons are implemented by `MDCButton`, a subclass of [UIButton](https://developer.apple.com/documentation/uikit/uibutton).
-* [API documentation](https://material.io/develop/ios/components/buttons/api-docs/Classes/MDCButton.html)
* [GitHub source](https://github.com/material-components/material-components-ios/blob/develop/components/Buttons/src/MDCButton.h)
### Install `MDCButton`
@@ -295,7 +294,7 @@ An outlined button has a text label, a container, and an optional icon.
### Contained button example
-Contained buttons are implemented by [MDCButton](https://material.io/develop/ios/components/buttons/api-docs/Classes/MDCButton.html). To achieve a contained button use the contained button theming method on the MDCButton theming extension. To access the theming extension see the [Theming section](#theming).
+Contained buttons are implemented by [MDCButton](https://github.com/material-components/material-components-ios/blob/stable/components/Buttons/src/MDCButton.h). To achieve a contained button use the contained button theming method on the MDCButton theming extension. To access the theming extension see the [Theming section](#theming).
#### Objective-C
diff --git a/components/Buttons/docs/fabs.md b/components/Buttons/docs/fabs.md
index 39337664068..54246a3345c 100644
--- a/components/Buttons/docs/fabs.md
+++ b/components/Buttons/docs/fabs.md
@@ -24,8 +24,7 @@ There are three types of FABs:
A FAB performs the primary, or most common, action on a screen. It appears in front of all screen content, typically as a circular shape with an icon in its center.
-FABs are implemented by `MDCFloatingButton`, a subclass of [MDCButton](https://material.io/develop/ios/components/buttons/api-docs/Classes/MDCButton.html).
-* [API documentation](https://material.io/develop/ios/components/buttons/api-docs/Classes/MDCFloatingButton.html)
+FABs are implemented by `MDCFloatingButton`, a subclass of [MDCButton](https://github.com/material-components/material-components-ios/blob/stable/components/Buttons/src/MDCButton.h).
* [GitHub source](https://github.com/material-components/material-components-ios/blob/develop/components/Buttons/src/MDCFloatingButton.h)
Only use a FAB if it is the most suitable way to present a screen’s primary action.
diff --git a/components/Buttons/src/MDCButton.h b/components/Buttons/src/MDCButton.h
index 5aba27b9a5d..e4c143a0bcf 100644
--- a/components/Buttons/src/MDCButton.h
+++ b/components/Buttons/src/MDCButton.h
@@ -17,6 +17,7 @@
#import "MaterialElevation.h"
#import "MaterialInk.h"
+#import "MaterialRipple.h"
#import "MaterialShadowElevations.h"
#import "MaterialShapes.h"
@@ -36,17 +37,24 @@
*/
@interface MDCButton : UIButton
-/** The ink style of the button. */
-@property(nonatomic, assign) MDCInkStyle inkStyle UI_APPEARANCE_SELECTOR;
+/** The ripple style of the button. */
+@property(nonatomic, assign) MDCRippleStyle rippleStyle;
-/** The ink color of the button. */
-@property(nonatomic, strong, null_resettable) UIColor *inkColor UI_APPEARANCE_SELECTOR;
+/**
+ The color of the ripple.
-/*
- Maximum radius of the button's ink. If the radius <= 0 then half the length of the diagonal of
- self.bounds is used. This value is ignored if button's @c inkStyle is set to |MDCInkStyleBounded|.
+ @note Defaults to a transparent black.
*/
-@property(nonatomic, assign) CGFloat inkMaxRippleRadius UI_APPEARANCE_SELECTOR;
+@property(nonatomic, strong, null_resettable) UIColor *rippleColor;
+
+/**
+ The maximum radius the ripple can expand to.
+
+ @note This property is ignored if @c rippleStyle is set to @c MDCRippleStyleBounded.
+
+ @note Defaults to 0.
+ */
+@property(nonatomic, assign) CGFloat rippleMaximumRadius;
/**
This property determines if an @c MDCButton should use the @c MDCInkView behavior or not.
@@ -96,6 +104,11 @@
*/
@property(nonatomic) CGSize inkViewOffset;
+/**
+ The inset or outset margins for the rectangle surrounding the button’s ripple.
+ */
+@property(nonatomic, assign) UIEdgeInsets rippleEdgeInsets;
+
/**
The minimum size of the button’s alignment rect. If either the height or width are non-positive
(negative or zero), they will be ignored and that axis will adjust to its content size.
@@ -396,4 +409,16 @@
*/
@property(nonatomic, assign) BOOL accessibilityTraitsIncludesButton;
+/** The ink style of the button. */
+@property(nonatomic, assign) MDCInkStyle inkStyle UI_APPEARANCE_SELECTOR;
+
+/** The ink color of the button. */
+@property(nonatomic, strong, null_resettable) UIColor *inkColor UI_APPEARANCE_SELECTOR;
+
+/*
+ Maximum radius of the button's ink. If the radius <= 0 then half the length of the diagonal of
+ self.bounds is used. This value is ignored if button's @c inkStyle is set to |MDCInkStyleBounded|.
+ */
+@property(nonatomic, assign) CGFloat inkMaxRippleRadius UI_APPEARANCE_SELECTOR;
+
@end
diff --git a/components/Buttons/src/MDCButton.m b/components/Buttons/src/MDCButton.m
index fa7262ae0db..65d8f745fa5 100644
--- a/components/Buttons/src/MDCButton.m
+++ b/components/Buttons/src/MDCButton.m
@@ -33,6 +33,7 @@
static const CGFloat MDCButtonMinimumTouchTargetHeight = 48;
static const CGFloat MDCButtonMinimumTouchTargetWidth = 48;
static const CGFloat MDCButtonDefaultCornerRadius = 2.0;
+static const CGFloat kDefaultRippleAlpha = (CGFloat)0.12;
static const NSTimeInterval MDCButtonAnimationDuration = 0.2;
@@ -65,7 +66,7 @@ static inline CGSize CGSizeShrinkWithInsets(CGSize size, UIEdgeInsets edgeInsets
MAX(0, size.height - (edgeInsets.top + edgeInsets.bottom)));
}
-static NSAttributedString *uppercaseAttributedString(NSAttributedString *string) {
+static NSAttributedString *UppercaseAttributedString(NSAttributedString *string) {
// Store the attributes.
NSMutableArray *attributes = [NSMutableArray array];
[string enumerateAttributesInRange:NSMakeRange(0, [string length])
@@ -111,6 +112,7 @@ @interface MDCButton () {
BOOL _mdc_adjustsFontForContentSizeCategory;
BOOL _cornerRadiusObserverAdded;
+ CGFloat _inkMaxRippleRadius;
}
@property(nonatomic, strong, readonly, nonnull) MDCStatefulRippleView *rippleView;
@property(nonatomic, strong) MDCInkView *inkView;
@@ -230,7 +232,8 @@ - (void)commonMDCButtonInit {
_inkView.inkColor = [UIColor colorWithWhite:1 alpha:(CGFloat)0.2];
_rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds];
- _rippleView.rippleColor = [UIColor colorWithWhite:1 alpha:(CGFloat)0.12];
+ _rippleColor = [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha];
+ _rippleView.rippleColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.12];
// Default content insets
// The default contentEdgeInsets are set here (instead of above, as they were previously) because
@@ -337,6 +340,7 @@ - (void)layoutSubviews {
} else {
CGRect bounds = CGRectStandardize(self.bounds);
bounds = CGRectOffset(bounds, self.inkViewOffset.width, self.inkViewOffset.height);
+ bounds = UIEdgeInsetsInsetRect(bounds, self.rippleEdgeInsets);
_inkView.frame = bounds;
self.rippleView.frame = bounds;
}
@@ -559,7 +563,7 @@ - (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)
}
if (_uppercaseTitle) {
- title = uppercaseAttributedString(title);
+ title = UppercaseAttributedString(title);
}
[super setAttributedTitle:title forState:state];
}
@@ -621,6 +625,12 @@ - (void)setInkStyle:(MDCInkStyle)inkStyle {
(inkStyle == MDCInkStyleUnbounded) ? MDCRippleStyleUnbounded : MDCRippleStyleBounded;
}
+- (void)setRippleStyle:(MDCRippleStyle)rippleStyle {
+ _rippleStyle = rippleStyle;
+
+ self.rippleView.rippleStyle = rippleStyle;
+}
+
- (UIColor *)inkColor {
return _inkView.inkColor;
}
@@ -630,17 +640,38 @@ - (void)setInkColor:(UIColor *)inkColor {
[self.rippleView setRippleColor:inkColor forState:MDCRippleStateHighlighted];
}
+- (void)setRippleColor:(UIColor *)rippleColor {
+ _rippleColor = rippleColor ?: [UIColor colorWithWhite:0 alpha:kDefaultRippleAlpha];
+ [self.rippleView setRippleColor:_rippleColor forState:MDCRippleStateHighlighted];
+}
+
+- (CGFloat)inkMaxRippleRadius {
+ return _inkMaxRippleRadius;
+}
+
- (void)setInkMaxRippleRadius:(CGFloat)inkMaxRippleRadius {
_inkMaxRippleRadius = inkMaxRippleRadius;
_inkView.maxRippleRadius = inkMaxRippleRadius;
self.rippleView.maximumRadius = inkMaxRippleRadius;
}
+- (void)setRippleMaximumRadius:(CGFloat)rippleMaximumRadius {
+ _rippleMaximumRadius = rippleMaximumRadius;
+
+ self.rippleView.maximumRadius = rippleMaximumRadius;
+}
+
- (void)setInkViewOffset:(CGSize)inkViewOffset {
_inkViewOffset = inkViewOffset;
[self setNeedsLayout];
}
+- (void)setRippleEdgeInsets:(UIEdgeInsets)rippleEdgeInsets {
+ _rippleEdgeInsets = rippleEdgeInsets;
+
+ [self setNeedsLayout];
+}
+
- (void)setEnableRippleBehavior:(BOOL)enableRippleBehavior {
_enableRippleBehavior = enableRippleBehavior;
diff --git a/components/Buttons/src/TitleColorAccessibilityMutator/MDCButtonTitleColorAccessibilityMutator.h b/components/Buttons/src/TitleColorAccessibilityMutator/MDCButtonTitleColorAccessibilityMutator.h
deleted file mode 100644
index 32179da24ae..00000000000
--- a/components/Buttons/src/TitleColorAccessibilityMutator/MDCButtonTitleColorAccessibilityMutator.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016-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
-
-#import "MaterialButtons.h"
-
-/**
- A Mutator that will change an instance of MDCButton to have a high enough contrast text between
- its background.
- */
-@interface MDCButtonTitleColorAccessibilityMutator : NSObject
-
-/**
- This method will change the title color for each state of the button to ensure a high accessiblity
- contrast with its background.
- */
-+ (void)changeTitleColorOfButton:(MDCButton *)button;
-
-@end
diff --git a/components/Buttons/src/TitleColorAccessibilityMutator/MDCButtonTitleColorAccessibilityMutator.m b/components/Buttons/src/TitleColorAccessibilityMutator/MDCButtonTitleColorAccessibilityMutator.m
deleted file mode 100644
index 25340701a5a..00000000000
--- a/components/Buttons/src/TitleColorAccessibilityMutator/MDCButtonTitleColorAccessibilityMutator.m
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016-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 "MDCButtonTitleColorAccessibilityMutator.h"
-
-#import
-#import "MaterialButtons.h"
-#import "MaterialTypography.h"
-
-@implementation MDCButtonTitleColorAccessibilityMutator
-
-+ (void)changeTitleColorOfButton:(MDCButton *)button {
- // This ensures title colors will be accessible against the buttons backgrounds.
- UIControlState allControlStates = UIControlStateNormal | UIControlStateHighlighted |
- UIControlStateDisabled | UIControlStateSelected;
- MDFTextAccessibilityOptions options = 0;
- if ([MDFTextAccessibility isLargeForContrastRatios:button.titleLabel.font]) {
- options = MDFTextAccessibilityOptionsLargeFont;
- }
- for (NSUInteger controlState = 0; controlState <= allControlStates; ++controlState) {
- UIColor *backgroundColor = [button backgroundColorForState:controlState];
- if ([self isTransparentColor:backgroundColor]) {
- // TODO(randallli): We could potentially traverse the view heirarchy instead.
- backgroundColor = button.underlyingColorHint;
- }
- if (backgroundColor) {
- UIColor *existingColor = [button titleColorForState:controlState];
- if (![MDFTextAccessibility textColor:existingColor
- passesOnBackgroundColor:backgroundColor
- options:options]) {
- UIColor *color =
- [MDFTextAccessibility textColorOnBackgroundColor:backgroundColor
- targetTextAlpha:[MDCTypography buttonFontOpacity]
- options:options];
- [button setTitleColor:color forState:controlState];
- }
- }
- }
-}
-
-/** Returns YES if the color is transparent (including a nil color). */
-+ (BOOL)isTransparentColor:(UIColor *)color {
- return !color || [color isEqual:[UIColor clearColor]] || CGColorGetAlpha(color.CGColor) == 0;
-}
-
-@end
diff --git a/components/Buttons/src/TitleColorAccessibilityMutator/MaterialButtons+TitleColorAccessibilityMutator.h b/components/Buttons/src/TitleColorAccessibilityMutator/MaterialButtons+TitleColorAccessibilityMutator.h
deleted file mode 100644
index 2fcca5a0a3b..00000000000
--- a/components/Buttons/src/TitleColorAccessibilityMutator/MaterialButtons+TitleColorAccessibilityMutator.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018-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 "MDCButtonTitleColorAccessibilityMutator.h"
diff --git a/components/Buttons/tests/snapshot/ButtonsRippleSnapshotTests.m b/components/Buttons/tests/snapshot/ButtonsRippleSnapshotTests.m
index e502b49f68d..d1c50b0e750 100644
--- a/components/Buttons/tests/snapshot/ButtonsRippleSnapshotTests.m
+++ b/components/Buttons/tests/snapshot/ButtonsRippleSnapshotTests.m
@@ -43,7 +43,7 @@ - (void)setUp {
UIImage *testImage = [[UIImage mdc_testImageOfSize:CGSizeMake(24, 24)]
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self.button setImage:testImage forState:UIControlStateNormal];
- self.button.inkColor = [UIColor.magentaColor colorWithAlphaComponent:0.25];
+ self.button.rippleColor = [UIColor.magentaColor colorWithAlphaComponent:0.25];
[self.button sizeToFit];
[self configureButton:self.button
forState:UIControlStateNormal
@@ -160,4 +160,14 @@ - (void)testButtonWhenRippleTouchDownAtPointIsCalledGeneratesCorrectImage {
[self generateSnapshotAndVerifyForView:self.button];
}
+- (void)testButtonRippleWhenContentEdgeInsetsAreSet {
+ // When
+ self.button.rippleEdgeInsets = UIEdgeInsetsMake(1, 2, 3, 4);
+ self.button.rippleView.backgroundColor = UIColor.purpleColor;
+ [self.button layoutIfNeeded];
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.button];
+}
+
@end
diff --git a/components/Buttons/tests/unit/MDCButtonRippleTests.m b/components/Buttons/tests/unit/MDCButtonRippleTests.m
index 37517b828a4..d6b7c642e34 100644
--- a/components/Buttons/tests/unit/MDCButtonRippleTests.m
+++ b/components/Buttons/tests/unit/MDCButtonRippleTests.m
@@ -51,7 +51,7 @@ - (void)tearDown {
- (void)testDefaultButtonBehaviorWithRipple {
// Then
XCTAssertNotNil(self.button.rippleView);
- XCTAssertEqualObjects(self.button.rippleView.rippleColor, [UIColor colorWithWhite:1
+ XCTAssertEqualObjects(self.button.rippleView.rippleColor, [UIColor colorWithWhite:0
alpha:(CGFloat)0.12]);
XCTAssertEqual(self.button.rippleView.rippleStyle, MDCRippleStyleBounded);
XCTAssertFalse(self.button.enableRippleBehavior);
@@ -89,14 +89,14 @@ - (void)testSetEnableRippleBehaviorToYesThenNoRemovesRippleViewAsSubviewOfButton
}
/**
- Test setting @c inkColor correctly sets the @c rippleColor on @c rippleView of the button.
+ Test setting @c rippleColor correctly sets the @c rippleColor on @c rippleView of the button.
*/
-- (void)testSetCustomInkColorUpdatesRippleViewForHighlightedState {
+- (void)testSetCustomRippleColorUpdatesRippleViewForHighlightedState {
// Given
UIColor *fakeColor = UIColor.redColor;
// When
- self.button.inkColor = fakeColor;
+ self.button.rippleColor = fakeColor;
// Then
XCTAssertEqualObjects([self.button.rippleView rippleColorForState:MDCRippleStateHighlighted],
@@ -104,22 +104,22 @@ - (void)testSetCustomInkColorUpdatesRippleViewForHighlightedState {
}
/**
- Test setting @c inkStyle correctly sets the @c rippleStyle on @c rippleView of the button.
+ Test setting @c rippleStyle correctly sets the @c rippleStyle on @c rippleView of the button.
*/
-- (void)testSetInkStyleUnboundedUpdatesRippleView {
+- (void)testSetRippleStyleUnboundedUpdatesRippleView {
// When
- self.button.inkStyle = MDCInkStyleUnbounded;
+ self.button.rippleStyle = MDCRippleStyleUnbounded;
// Then
XCTAssertEqual(self.button.rippleView.rippleStyle, MDCRippleStyleUnbounded);
}
/**
- Test setting @c inkStyle correctly sets the @c rippleStyle on @c rippleView of the button.
+ Test setting @c rippleStyle correctly sets the @c rippleStyle on @c rippleView of the button.
*/
-- (void)testSetInkStyleBoundedUpdatesRippleView {
+- (void)testSetRippleStyleBoundedUpdatesRippleView {
// When
- self.button.inkStyle = MDCInkStyleBounded;
+ self.button.rippleStyle = MDCRippleStyleBounded;
// Then
XCTAssertEqual(self.button.rippleView.rippleStyle, MDCRippleStyleBounded);
@@ -182,4 +182,37 @@ - (void)testSettingSelectedUpdatesRippleTheming {
XCTAssertFalse(self.button.rippleView.isSelected);
}
+- (void)testSettingContentEdgeInsetsUpdatesRipple {
+ // Given
+ self.button.enableRippleBehavior = YES;
+ CGRect buttonFrame = CGRectMake(0, 0, 100, 100);
+ self.button.frame = buttonFrame;
+ UIEdgeInsets buttonRippleInsets = UIEdgeInsetsMake(1, 2, 3, 4);
+
+ // When
+ self.button.rippleEdgeInsets = buttonRippleInsets;
+ [self.button layoutIfNeeded];
+
+ // Then
+ XCTAssertTrue(CGRectEqualToRect(UIEdgeInsetsInsetRect(buttonFrame, buttonRippleInsets),
+ self.button.rippleView.frame),
+ @"%@ is not equal to %@",
+ NSStringFromCGRect(UIEdgeInsetsInsetRect(buttonFrame, buttonRippleInsets)),
+ NSStringFromCGRect(self.button.rippleView.frame));
+}
+
+- (void)testDefaultRippleMaximumRadius {
+ // Then
+ XCTAssertEqualWithAccuracy(self.button.rippleMaximumRadius, 0, 0.001);
+}
+
+- (void)testSettingRippleMaximumRadiusUpdatesTheRippleView {
+ // When
+ self.button.rippleMaximumRadius = 5;
+
+ // Then
+ XCTAssertEqualWithAccuracy(self.button.rippleMaximumRadius, 5, 0.001);
+ XCTAssertEqualWithAccuracy(self.button.rippleView.maximumRadius, 5, 0.001);
+}
+
@end
diff --git a/components/Buttons/tests/unit/MDCButtonTests.m b/components/Buttons/tests/unit/MDCButtonTests.m
index dae509ed213..2beb9fed037 100644
--- a/components/Buttons/tests/unit/MDCButtonTests.m
+++ b/components/Buttons/tests/unit/MDCButtonTests.m
@@ -926,6 +926,17 @@ - (void)testInkColors {
XCTAssertEqualObjects(self.button.inkColor, color);
}
+- (void)testRippleColors {
+ // Given
+ UIColor *color = randomColor();
+
+ // When
+ self.button.rippleColor = color;
+
+ // Then
+ XCTAssertEqualObjects(self.button.rippleColor, color);
+}
+
/*
TODO: things to unit test
(should these even be a thing?)
diff --git a/components/Buttons/tests/unit/MDCButtonTitleColorAccessibilityMutatorTests.m b/components/Buttons/tests/unit/MDCButtonTitleColorAccessibilityMutatorTests.m
deleted file mode 100644
index 49721d55572..00000000000
--- a/components/Buttons/tests/unit/MDCButtonTitleColorAccessibilityMutatorTests.m
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2016-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
-
-#import "MaterialButtons+TitleColorAccessibilityMutator.h"
-#import "MaterialButtons.h"
-
-// A value greater than the largest value created by combining normal values of UIControlState.
-// This is a complete hack, but UIControlState doesn't expose anything useful here.
-// This assumes that UIControlState is actually a set of bitfields and ignores application-specific
-// values.
-static const UIControlState kNumUIControlStates = 2 * UIControlStateSelected - 1;
-static const UIControlState kUIControlStateDisabledHighlighted =
- UIControlStateHighlighted | UIControlStateDisabled;
-
-static NSArray *testColors() {
- return @[
- [UIColor whiteColor], [UIColor blackColor], [UIColor redColor], [UIColor orangeColor],
- [UIColor greenColor], [UIColor blueColor], [UIColor grayColor]
- ];
-}
-
-static NSString *controlStateDescription(UIControlState controlState);
-
-@interface MDCButtonTitleColorAccessibilityMutatorTests : XCTestCase
-@end
-
-@implementation MDCButtonTitleColorAccessibilityMutatorTests
-
-- (void)testMutateChangesTextColor {
- for (UIColor *color in testColors()) {
- for (NSUInteger controlState = 0; controlState <= kNumUIControlStates; ++controlState) {
- // Given
- MDCButton *button = [[MDCButton alloc] init];
- // Making the background color the same as the title color.
- [button setBackgroundColor:color forState:(UIControlState)controlState];
- [button setTitleColor:color forState:(UIControlState)controlState];
- UIControlState disabledHighlighed = UIControlStateHighlighted | UIControlStateDisabled;
- if ((controlState & disabledHighlighed) == disabledHighlighed) {
- // Skip disabled highlighted state because UIButton ignores setTitleColor:forState: when
- // passed that state. See `UIButton strangeness` in ButtonTests.m
- continue;
- }
-
- // When
- [MDCButtonTitleColorAccessibilityMutator changeTitleColorOfButton:button];
-
- // Then
- XCTAssertNotEqualObjects([button titleColorForState:controlState], color,
- @"for control state:%@ ", controlStateDescription(controlState));
- }
- }
-}
-
-- (void)testMutateKeepsAccessibleTextColor {
- NSDictionary *colors = @{[UIColor redColor] : [UIColor blackColor]};
- for (UIColor *color in colors) {
- for (NSUInteger controlState = 0; controlState <= kNumUIControlStates; ++controlState) {
- if (controlState & kUIControlStateDisabledHighlighted) {
- // We skip the Disabled Highlighted state because UIButton setter ignores it.
- // see: testTitleColorForStateDisabledHighlight
- continue;
- }
- // Given
- MDCButton *button = [[MDCButton alloc] init];
- UIColor *backgroundColor = colors[color];
- UIColor *titleColor = color;
- // Making the background color the same as the title color.
- [button setBackgroundColor:backgroundColor forState:(UIControlState)controlState];
- [button setTitleColor:titleColor forState:(UIControlState)controlState];
-
- // When
- [MDCButtonTitleColorAccessibilityMutator changeTitleColorOfButton:button];
-
- // Then
- XCTAssertEqualObjects([button titleColorForState:controlState], titleColor,
- @"for control state:%@ ", controlStateDescription(controlState));
- }
- }
-}
-
-- (void)testMutateUsesUnderlyingColorIfButtonBackgroundColorIsTransparent {
- for (UIColor *color in testColors()) {
- for (NSUInteger controlState = 0; controlState <= kNumUIControlStates; ++controlState) {
- if ((controlState & kUIControlStateDisabledHighlighted) ==
- kUIControlStateDisabledHighlighted) {
- // Skip since it's tested with either .highlighted or (.highlighted | .selected)
- continue;
- }
- // Given
- MDCButton *button = [[MDCButton alloc] init];
- button.underlyingColorHint = color;
- [button setBackgroundColor:[UIColor clearColor] forState:controlState];
- [button setTitleColor:color forState:(UIControlState)controlState];
-
- // When
- [MDCButtonTitleColorAccessibilityMutator changeTitleColorOfButton:button];
-
- // Then
- XCTAssertNotEqualObjects([button titleColorForState:controlState], color,
- @"for control state:%@ ", controlStateDescription(controlState));
- }
- }
-}
-
-@end
-
-static NSString *controlStateDescription(UIControlState controlState) {
- if (controlState == UIControlStateNormal) {
- return @"Normal";
- }
- NSMutableString *string = [NSMutableString string];
- if ((UIControlStateHighlighted & controlState) == UIControlStateHighlighted) {
- [string appendString:@"Highlighted "];
- }
- if ((UIControlStateDisabled & controlState) == UIControlStateDisabled) {
- [string appendString:@"Disabled "];
- }
- if ((UIControlStateSelected & controlState) == UIControlStateSelected) {
- [string appendString:@"Selected "];
- }
- return [string copy];
-}
diff --git a/components/Cards/README.md b/components/Cards/README.md
index 6832b433291..1895367a027 100644
--- a/components/Cards/README.md
+++ b/components/Cards/README.md
@@ -193,11 +193,9 @@ Cards can be used to build custom UIs, like the one shown above, from [CardWithI
MDCCard and MDCCardCollectionCell inherit from UIControl and UICollectionViewCell, respectively.
* MDCCard
- * [API documentation](https://material.io/develop/ios/components/cards/api-docs/Classes/MDCCard.html)
* [GitHub source](https://github.com/material-components/material-components-ios/blob/develop/components/Cards/src/MDCCard.h)
* MDCCardCollectionCell
- * [API documentation](https://material.io/develop/ios/components/cards/api-docs/Classes/MDCCardCollectionCell.html)
* [GitHub source](https://github.com/material-components/material-components-ios/blob/develop/components/Cards/src/MDCCardCollectionCell.h)
### Card examples using MDCCard
diff --git a/components/Chips/README.md b/components/Chips/README.md
index 2ef0594ff68..0ba1be59a8e 100644
--- a/components/Chips/README.md
+++ b/components/Chips/README.md
@@ -20,13 +20,12 @@ Chips are compact elements that represent an input, attribute, or action.
## Table of contents
diff --git a/components/Chips/src/MDCChipView.h b/components/Chips/src/MDCChipView.h
index bc2daa7d902..538e46527fc 100644
--- a/components/Chips/src/MDCChipView.h
+++ b/components/Chips/src/MDCChipView.h
@@ -284,6 +284,27 @@
- (void)setInkColor:(nullable UIColor *)inkColor
forState:(UIControlState)state UI_APPEARANCE_SELECTOR;
+/*
+ Returns the ripple color associated with the specified state.
+
+ The ripple color for the specified state. If no ripple color has been set for the specific state,
+ this method returns the title associated with the @c UIControlStateNormal state.
+
+ @note Defaults to @c nil. When @c nil transparent black is used.
+
+ @param state The state that uses the ripple color.
+ @return The ripple color for the requested state.
+ */
+- (nullable UIColor *)rippleColorForState:(UIControlState)state;
+
+/*
+ Sets the ripple color for a particular control state.
+
+ @param rippleColor The ripple color to use for the specified state.
+ @param state The state that uses the specified ripple color.
+ */
+- (void)setRippleColor:(nullable UIColor *)rippleColor forState:(UIControlState)state;
+
/*
Returns the shadow color for a particular control state.
diff --git a/components/Chips/src/MDCChipView.m b/components/Chips/src/MDCChipView.m
index aff152f1cc9..eb90b2c38e0 100644
--- a/components/Chips/src/MDCChipView.m
+++ b/components/Chips/src/MDCChipView.m
@@ -114,6 +114,7 @@ @interface MDCChipView ()
@property(nonatomic, assign) BOOL shouldFullyRoundCorner;
@property(nonatomic, strong) MDCInkView *inkView;
@property(nonatomic, strong) MDCStatefulRippleView *rippleView;
+@property(nonatomic, strong, nonnull) NSMutableDictionary *rippleColors;
@property(nonatomic, readonly) CGFloat pixelScale;
@property(nonatomic, assign) BOOL enableRippleBehavior;
@property(nonatomic, assign) BOOL adjustsFontForContentSizeCategoryWhenScaledFontIsUnavailable;
@@ -196,6 +197,7 @@ - (instancetype)initWithFrame:(CGRect)frame {
[self addSubview:_inkView];
_rippleView = [[MDCStatefulRippleView alloc] initWithFrame:self.bounds];
+ _rippleColors = [NSMutableDictionary dictionary];
_imageView = [[UIImageView alloc] init];
[self addSubview:_imageView];
@@ -522,6 +524,23 @@ - (void)updateInkColor {
self.inkView.inkColor = inkColor ?: self.inkView.defaultInkColor;
}
+- (UIColor *)rippleColorForState:(UIControlState)state {
+ UIColor *rippleColor = self.rippleColors[@(state)];
+ if (!rippleColor && state != UIControlStateNormal) {
+ rippleColor = self.rippleColors[@(UIControlStateNormal)];
+ }
+ return rippleColor;
+}
+
+- (void)setRippleColor:(UIColor *)rippleColor forState:(UIControlState)state {
+ self.rippleColors[@(state)] = rippleColor;
+ NSNumber *rippleState = [self rippleStateForControlState:state];
+ if (rippleState) {
+ [self.rippleView setRippleColor:rippleColor forState:rippleState.integerValue];
+ }
+ [self updateRippleColor];
+}
+
- (void)updateRippleColor {
UIColor *rippleColor = [self inkColorForState:self.state];
// MDCStatefulRippleView sets the ripple color internally when its state changes.
diff --git a/components/Chips/tests/unit/ChipViewRippleTests.m b/components/Chips/tests/unit/ChipViewRippleTests.m
index cc44c2c4117..b75a2dd3b6d 100644
--- a/components/Chips/tests/unit/ChipViewRippleTests.m
+++ b/components/Chips/tests/unit/ChipViewRippleTests.m
@@ -18,6 +18,29 @@
#import "MaterialInk.h"
#import "MaterialRipple.h"
+static UIColor *RandomColor() {
+ switch (arc4random_uniform(5)) {
+ case 0:
+ return [UIColor colorWithRed:1 green:1 blue:1 alpha:1];
+ break;
+ case 1:
+ return [UIColor colorWithRed:0 green:0 blue:0 alpha:1];
+ break;
+ case 2:
+ return [UIColor redColor];
+ break;
+ case 3:
+ return [UIColor orangeColor];
+ break;
+ case 4:
+ return [UIColor greenColor];
+ break;
+ default:
+ return [UIColor blueColor];
+ break;
+ }
+}
+
@interface MDCChipView (Testing)
@property(nonatomic, strong) MDCStatefulRippleView *rippleView;
@property(nonatomic, strong) MDCInkView *inkView;
@@ -175,4 +198,31 @@ - (void)testChipViewNotHighlightedSetsRippleHighlightedToNO {
XCTAssertFalse(self.chipView.rippleView.isRippleHighlighted);
}
+- (void)testSetRippleColorForStateReturnsTheCorrectValue {
+ UIControlState maxState = UIControlStateNormal | UIControlStateHighlighted |
+ UIControlStateDisabled | UIControlStateSelected;
+ for (NSUInteger state = 0; state <= maxState; ++state) {
+ // Given
+ UIColor *color = RandomColor();
+
+ // When
+ [self.chipView setRippleColor:color forState:state];
+
+ // Then
+ XCTAssertEqualObjects([self.chipView rippleColorForState:state], color);
+ }
+}
+
+- (void)testSetRippleColorToNilReturnsTheCorrectValueForNormal {
+ // Given
+ UIColor *color = UIColor.orangeColor;
+ [self.chipView setRippleColor:color forState:UIControlStateNormal];
+
+ // When
+ [self.chipView setRippleColor:nil forState:UIControlStateSelected];
+
+ // Then
+ XCTAssertEqualObjects([self.chipView rippleColorForState:UIControlStateSelected], color);
+}
+
@end
diff --git a/components/CollectionCells/README.md b/components/CollectionCells/README.md
index af5aafbb314..a344abf51f0 100644
--- a/components/CollectionCells/README.md
+++ b/components/CollectionCells/README.md
@@ -17,8 +17,8 @@ Collection view cell classes that adhere to Material Design layout and styling.
- - -
diff --git a/components/CollectionLayoutAttributes/README.md b/components/CollectionLayoutAttributes/README.md
index 916fae6c72a..f89fa18f972 100644
--- a/components/CollectionLayoutAttributes/README.md
+++ b/components/CollectionLayoutAttributes/README.md
@@ -12,7 +12,7 @@ Allows passing layout attributes to the cells and supplementary views.
## Design & API Documentation
- - -
diff --git a/components/Collections/README.md b/components/Collections/README.md
index 6c23d0ef869..15cf0e9f7f7 100644
--- a/components/Collections/README.md
+++ b/components/Collections/README.md
@@ -17,11 +17,11 @@ Collection view classes that adhere to Material Design layout and styling.
- - -
diff --git a/components/Dialogs/examples/DialogsTitleImageExampleViewController.swift b/components/Dialogs/examples/DialogsTitleImageExampleViewController.swift
index 2da49caad42..5a4370fd576 100644
--- a/components/Dialogs/examples/DialogsTitleImageExampleViewController.swift
+++ b/components/Dialogs/examples/DialogsTitleImageExampleViewController.swift
@@ -280,22 +280,55 @@ extension DialogsTitleImageExampleViewController {
extension UIView {
fileprivate func animateIn(
- duration: TimeInterval = 2, options: UIView.AnimationOptions = [.curveEaseOut]
+ duration: TimeInterval = 4,
+ repeating: Bool = true,
+ options: UIView.AnimationOptions = [.curveEaseOut]
) {
- alpha = 0.2
- self.transform = self.transform
- .translatedBy(x: -self.frame.origin.x / 2, y: 0)
- .scaledBy(x: 0.2, y: 0.2)
- .rotated(by: CGFloat.pi)
- UIView.animate(
- withDuration: duration,
- delay: 0,
- usingSpringWithDamping: 0.3,
- initialSpringVelocity: 0.3,
- options: options,
+ self.alpha = 1
+ let initialTransform = self.transform.translatedBy(x: -150, y: 0)
+ self.transform = initialTransform
+ let transform1 =
+ initialTransform
+ .concatenating(CGAffineTransform(translationX: 80, y: 0))
+ .rotated(by: CGFloat.pi * 0.8)
+ let transform2 =
+ initialTransform
+ .concatenating(CGAffineTransform(translationX: 160, y: 0))
+ .rotated(by: CGFloat.pi * 1.5)
+ let transform3 =
+ initialTransform
+ .concatenating(CGAffineTransform(translationX: 240, y: 0))
+ let transform4 =
+ initialTransform
+ .concatenating(CGAffineTransform(translationX: 360, y: 0))
+ .rotated(by: CGFloat.pi * 0.75)
+
+ let animationOptions: UInt
+ if repeating {
+ animationOptions =
+ UIView.AnimationOptions.curveLinear.rawValue | UIView.AnimationOptions.repeat.rawValue
+ } else {
+ animationOptions = UIView.AnimationOptions.curveLinear.rawValue
+ }
+
+ let keyFrameAnimationOptions = UIView.KeyframeAnimationOptions(rawValue: animationOptions)
+
+ UIView.animateKeyframes(
+ withDuration: duration, delay: 0,
+ options: [keyFrameAnimationOptions, .calculationModeLinear],
animations: {
- self.transform = CGAffineTransform.identity
- self.alpha = 1
+ UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.14) { // 0.375
+ self.transform = transform1
+ }
+ UIView.addKeyframe(withRelativeStartTime: 0.14, relativeDuration: 0.14) { // 0.375
+ self.transform = transform2
+ }
+ UIView.addKeyframe(withRelativeStartTime: 0.28, relativeDuration: 0.15) { //0.25) {
+ self.transform = transform3
+ }
+ UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.15) { //0.25) {
+ self.transform = transform4
+ }
}, completion: nil)
}
}
diff --git a/components/FeatureHighlight/README.md b/components/FeatureHighlight/README.md
index cd315160e1a..98ef04768bf 100644
--- a/components/FeatureHighlight/README.md
+++ b/components/FeatureHighlight/README.md
@@ -23,8 +23,8 @@ The Feature Highlight component is a way to visually highlight a part of the scr
## Design & API documentation
## Table of contents
diff --git a/components/FeatureHighlight/examples/FeatureHighlightColorExample.m b/components/FeatureHighlight/examples/FeatureHighlightColorExample.m
index fa53f9471dc..acabc93f579 100644
--- a/components/FeatureHighlight/examples/FeatureHighlightColorExample.m
+++ b/components/FeatureHighlight/examples/FeatureHighlightColorExample.m
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h"
#import "MaterialFeatureHighlight.h"
#import "supplemental/FeatureHighlightExampleSupplemental.h"
@@ -31,7 +30,6 @@ - (void)collectionView:(UICollectionView *)collectionView
highlightController.accessibilityHint = nil;
highlightController.bodyText = @"What a nice color you've chosen.";
highlightController.outerHighlightColor = cell.accessoryView.backgroundColor;
- [MDCFeatureHighlightAccessibilityMutator mutate:highlightController];
[self presentViewController:highlightController animated:YES completion:nil];
}
diff --git a/components/FeatureHighlight/examples/FeatureHighlightCustomFontsExample.m b/components/FeatureHighlight/examples/FeatureHighlightCustomFontsExample.m
index f95b1e3156c..9d181b824dc 100644
--- a/components/FeatureHighlight/examples/FeatureHighlightCustomFontsExample.m
+++ b/components/FeatureHighlight/examples/FeatureHighlightCustomFontsExample.m
@@ -13,7 +13,6 @@
// limitations under the License.
#import "MaterialButtons.h"
-#import "MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h"
#import "MaterialFeatureHighlight.h"
#import "MaterialTypography.h"
#import "supplemental/FeatureHighlightExampleSupplemental.h"
@@ -56,7 +55,6 @@ - (void)viewDidLoad {
- (void)didTapButton:(id)sender {
MDCFeatureHighlightViewController *vc =
[[MDCFeatureHighlightViewController alloc] initWithHighlightedView:_button completion:nil];
- [MDCFeatureHighlightAccessibilityMutator mutate:vc];
vc.titleText = @"Feature Highlight can use custom fonts";
vc.bodyText = @"The title and body font can be set individually.";
diff --git a/components/FeatureHighlight/examples/FeatureHighlightShownViewExample.m b/components/FeatureHighlight/examples/FeatureHighlightShownViewExample.m
index b0470d43bbf..289f4b96752 100644
--- a/components/FeatureHighlight/examples/FeatureHighlightShownViewExample.m
+++ b/components/FeatureHighlight/examples/FeatureHighlightShownViewExample.m
@@ -15,7 +15,6 @@
#import "MaterialButtons+Theming.h"
#import "MaterialButtons.h"
#import "MaterialFeatureHighlight+ColorThemer.h"
-#import "MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h"
#import "MaterialFeatureHighlight.h"
#import "supplemental/FeatureHighlightExampleSupplemental.h"
@@ -54,7 +53,6 @@ - (void)didTapButton:(id)sender {
[self fabDidTap:fab];
}
}];
- [MDCFeatureHighlightAccessibilityMutator mutate:vc];
[MDCFeatureHighlightColorThemer applySemanticColorScheme:self.colorScheme
toFeatureHighlightViewController:vc];
vc.titleFont = self.typographyScheme.headline6;
diff --git a/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MDCFeatureHighlightAccessibilityMutator.h b/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MDCFeatureHighlightAccessibilityMutator.h
deleted file mode 100644
index e7281874b0f..00000000000
--- a/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MDCFeatureHighlightAccessibilityMutator.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2018-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.
-
-@class MDCFeatureHighlightViewController;
-
-#import
-#import
-
-/**
- A Mutator that will change an instance of MDCFeatureHighlightViewController to have a high enough
- contrast text between its background.
- Calling this mutator can overwrite UIApperance values.
- */
-@interface MDCFeatureHighlightAccessibilityMutator : NSObject
-
-/**
- This method will change the title and body color of the feature highlight to ensure a high
- accessiblity contrast with its background if needed.
- */
-+ (void)mutate:(MDCFeatureHighlightViewController *)featureHighlightViewController;
-
-@end
diff --git a/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MDCFeatureHighlightAccessibilityMutator.m b/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MDCFeatureHighlightAccessibilityMutator.m
deleted file mode 100644
index 22f31995de5..00000000000
--- a/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MDCFeatureHighlightAccessibilityMutator.m
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2018-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 "MDCFeatureHighlightAccessibilityMutator.h"
-
-#import "MaterialFeatureHighlight.h"
-#import "MaterialTypography.h"
-
-@implementation MDCFeatureHighlightAccessibilityMutator
-
-+ (void)mutate:(MDCFeatureHighlightViewController *)featureHighlightViewController {
- [MDCFeatureHighlightAccessibilityMutator mutateTitleColor:featureHighlightViewController];
- [MDCFeatureHighlightAccessibilityMutator mutateBodyColor:featureHighlightViewController];
-}
-
-+ (void)mutateTitleColor:(MDCFeatureHighlightViewController *)featureHighlightViewController {
- MDFTextAccessibilityOptions options = MDFTextAccessibilityOptionsPreferLighter;
- if ([MDFTextAccessibility isLargeForContrastRatios:featureHighlightViewController.titleFont]) {
- options |= MDFTextAccessibilityOptionsLargeFont;
- }
-
- UIColor *textColor = featureHighlightViewController.titleColor;
- UIColor *backgroundColor =
- [featureHighlightViewController.outerHighlightColor colorWithAlphaComponent:1];
- UIColor *titleColor =
- [MDCFeatureHighlightAccessibilityMutator accessibleColorForTextColor:textColor
- withBackgroundColor:backgroundColor
- options:options];
- // no change needed.
- if ([titleColor isEqual:textColor]) {
- return;
- }
-
- // Make title alpha the maximum it can be.
- CGFloat titleAlpha = [MDFTextAccessibility minAlphaOfTextColor:titleColor
- onBackgroundColor:backgroundColor
- options:options];
- titleAlpha = MAX([MDCTypography titleFontOpacity], titleAlpha);
- featureHighlightViewController.titleColor = [titleColor colorWithAlphaComponent:titleAlpha];
-}
-
-+ (void)mutateBodyColor:(MDCFeatureHighlightViewController *)featureHighlightViewController {
- MDFTextAccessibilityOptions options = MDFTextAccessibilityOptionsPreferLighter;
- if ([MDFTextAccessibility isLargeForContrastRatios:featureHighlightViewController.bodyFont]) {
- options |= MDFTextAccessibilityOptionsLargeFont;
- }
-
- UIColor *textColor = featureHighlightViewController.bodyColor;
- UIColor *backgroundColor =
- [featureHighlightViewController.outerHighlightColor colorWithAlphaComponent:1];
- featureHighlightViewController.bodyColor =
- [MDCFeatureHighlightAccessibilityMutator accessibleColorForTextColor:textColor
- withBackgroundColor:backgroundColor
- options:options];
-}
-
-#pragma mark - Private
-
-+ (UIColor *)accessibleColorForTextColor:(UIColor *)textColor
- withBackgroundColor:(UIColor *)backgroundColor
- options:(MDFTextAccessibilityOptions)options {
- if (textColor && [MDFTextAccessibility textColor:textColor
- passesOnBackgroundColor:backgroundColor
- options:options]) {
- return textColor;
- }
- return [MDFTextAccessibility textColorOnBackgroundColor:backgroundColor
- targetTextAlpha:[MDCTypography captionFontOpacity]
- options:options];
-}
-
-@end
diff --git a/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h b/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h
deleted file mode 100644
index ddb2ab2c697..00000000000
--- a/components/FeatureHighlight/src/FeatureHighlightAccessibilityMutator/MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018-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 "MDCFeatureHighlightAccessibilityMutator.h"
diff --git a/components/FeatureHighlight/tests/unit/FeatureHighlightTitleBodyAccessibilityMutatorTests.m b/components/FeatureHighlight/tests/unit/FeatureHighlightTitleBodyAccessibilityMutatorTests.m
deleted file mode 100644
index b540b44d60f..00000000000
--- a/components/FeatureHighlight/tests/unit/FeatureHighlightTitleBodyAccessibilityMutatorTests.m
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2018-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 "MaterialFeatureHighlight+FeatureHighlightAccessibilityMutator.h"
-#import "MaterialFeatureHighlight.h"
-
-#import
-
-static NSArray *testColors() {
- return @[
- [UIColor whiteColor], [UIColor blackColor], [UIColor redColor], [UIColor orangeColor],
- [UIColor greenColor], [UIColor blueColor], [UIColor grayColor]
- ];
-}
-
-@interface FeatureHighlightTitleBodyAccessibilityMutatorTests : XCTestCase
-@property(nonatomic, strong) UIView *highlightedView;
-@property(nonatomic, strong) UIView *showView;
-@end
-
-@implementation FeatureHighlightTitleBodyAccessibilityMutatorTests
-
-- (void)setUp {
- [super setUp];
- self.showView = [[UIView alloc] init];
- self.highlightedView = [[UIView alloc] init];
- [self.showView addSubview:self.highlightedView];
-}
-
-- (void)tearDown {
- for (UIView *subview in self.showView.subviews) {
- [subview removeFromSuperview];
- }
- self.showView = nil;
- self.highlightedView = nil;
- [super tearDown];
-}
-
-- (void)testMutatorChangesTextColor {
- for (UIColor *color in testColors()) {
- MDCFeatureHighlightViewController *featureHighlightViewController =
- [[MDCFeatureHighlightViewController alloc] initWithHighlightedView:self.highlightedView
- andShowView:self.showView
- completion:nil];
-
- // Making the background color the same as the title/body color.
- featureHighlightViewController.outerHighlightColor = color;
- featureHighlightViewController.titleColor = color;
- featureHighlightViewController.bodyColor = color;
- [MDCFeatureHighlightAccessibilityMutator mutate:featureHighlightViewController];
-
- XCTAssertNotNil(featureHighlightViewController.titleColor);
- XCTAssertNotNil(featureHighlightViewController.bodyColor);
- XCTAssertNotEqualObjects(featureHighlightViewController.titleColor, color);
- XCTAssertNotEqualObjects(featureHighlightViewController.bodyColor, color);
- }
-}
-
-/**
- * This test could fail even when our mutator is returning accessible color. In case MDF is
- * returning a new accessible color
- */
-- (void)testMutatorChangesTextColorToExpectedColor {
- MDCFeatureHighlightViewController *featureHighlightViewController =
- [[MDCFeatureHighlightViewController alloc] initWithHighlightedView:self.highlightedView
- andShowView:self.showView
- completion:nil];
-
- // Making the background color the same as the title/body color.
- featureHighlightViewController.outerHighlightColor = [UIColor whiteColor];
- featureHighlightViewController.titleColor = [UIColor whiteColor];
- featureHighlightViewController.bodyColor = [UIColor whiteColor];
- [MDCFeatureHighlightAccessibilityMutator mutate:featureHighlightViewController];
-
- XCTAssertNotNil(featureHighlightViewController.titleColor);
- XCTAssertNotNil(featureHighlightViewController.bodyColor);
-
- // Hard coded values, if this test fails it only means that we could mean that we changed the
- // accessible color we are returning.
- XCTAssertEqualObjects(featureHighlightViewController.titleColor,
- [UIColor colorWithWhite:0 alpha:(CGFloat)0.87]);
- XCTAssertEqualObjects(featureHighlightViewController.bodyColor,
- [UIColor colorWithWhite:0 alpha:(CGFloat)0.54]);
-}
-
-- (void)testMutatorKeepsAccessibleTextColor {
- NSDictionary *colors = @{[UIColor redColor] : [UIColor blackColor]};
- for (UIColor *color in colors) {
- MDCFeatureHighlightViewController *featureHighlightViewController =
- [[MDCFeatureHighlightViewController alloc] initWithHighlightedView:self.highlightedView
- andShowView:self.showView
- completion:nil];
- // Making the background color accessible with title/body color.
- featureHighlightViewController.outerHighlightColor = colors[color];
- featureHighlightViewController.titleColor = color;
- featureHighlightViewController.bodyColor = color;
-
- [MDCFeatureHighlightAccessibilityMutator mutate:featureHighlightViewController];
-
- XCTAssertEqualObjects(featureHighlightViewController.titleColor, color);
- XCTAssertEqualObjects(featureHighlightViewController.bodyColor, color);
- }
-}
-
-- (void)testMutatorSelectsTheRightColorWhenThereIsNoColorSet {
- MDCFeatureHighlightViewController *featureHighlightViewController =
- [[MDCFeatureHighlightViewController alloc] initWithHighlightedView:self.highlightedView
- andShowView:self.showView
- completion:nil];
-
- // Making the background color accessible with title/body color.
- featureHighlightViewController.outerHighlightColor = [UIColor blackColor];
-
- [MDCFeatureHighlightAccessibilityMutator mutate:featureHighlightViewController];
-
- XCTAssertNotNil(featureHighlightViewController.titleColor);
- XCTAssertNotNil(featureHighlightViewController.bodyColor);
- XCTAssertNotEqualObjects(featureHighlightViewController.titleColor, [UIColor clearColor]);
- XCTAssertNotEqualObjects(featureHighlightViewController.bodyColor, [UIColor clearColor]);
- XCTAssertNotEqualObjects(featureHighlightViewController.titleColor, [UIColor blackColor]);
- XCTAssertNotEqualObjects(featureHighlightViewController.bodyColor, [UIColor blackColor]);
-}
-
-@end
diff --git a/components/FlexibleHeader/README.md b/components/FlexibleHeader/README.md
index ea574b748d9..df2fe42fc2a 100644
--- a/components/FlexibleHeader/README.md
+++ b/components/FlexibleHeader/README.md
@@ -25,17 +25,16 @@ UIScrollViewDelegate events.
## Related components
diff --git a/components/HeaderStackView/README.md b/components/HeaderStackView/README.md
index d9d33bdf471..d8a4ca3c2be 100644
--- a/components/HeaderStackView/README.md
+++ b/components/HeaderStackView/README.md
@@ -21,7 +21,7 @@ bar views.
- - -
diff --git a/components/HeaderStackView/src/ColorThemer/MDCHeaderStackViewColorThemer.h b/components/HeaderStackView/src/ColorThemer/MDCHeaderStackViewColorThemer.h
deleted file mode 100644
index 0045523b58a..00000000000
--- a/components/HeaderStackView/src/ColorThemer/MDCHeaderStackViewColorThemer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017-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 "MaterialHeaderStackView.h"
-#import "MaterialThemes.h"
-
-#import
-
-#pragma mark - Soon to be deprecated
-
-/**
- A color themer for instances of MDCHeaderStackView.
- */
-__deprecated_msg("No replacement exists. Please comment on"
- " https://github.com/material-components/material-components-ios/issues/7172"
- " in order to indicate interest in a replacement API.")
- @interface MDCHeaderStackViewColorThemer : NSObject
-
-/**
- Applies a color scheme's properties to an MDCHeaderStackView.
-
- @warning This class will soon be deprecated. There will be no replacement API. Consider theming
- your flexible header view or app bar instead.
- Learn more at docs/theming.md#migration-guide-themers-to-theming-extensions
-
- @param colorScheme The color scheme to apply to the component instance.
- @param headerStackView A component instance to which the color scheme should be applied.
- */
-+ (void)applyColorScheme:(nonnull id)colorScheme
- toHeaderStackView:(nonnull MDCHeaderStackView *)headerStackView;
-
-@end
diff --git a/components/HeaderStackView/src/ColorThemer/MDCHeaderStackViewColorThemer.m b/components/HeaderStackView/src/ColorThemer/MDCHeaderStackViewColorThemer.m
deleted file mode 100644
index c51cf833076..00000000000
--- a/components/HeaderStackView/src/ColorThemer/MDCHeaderStackViewColorThemer.m
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017-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 "MDCHeaderStackViewColorThemer.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-implementations"
-@implementation MDCHeaderStackViewColorThemer
-#pragma clang diagnostic pop
-
-+ (void)applyColorScheme:(id)colorScheme
- toHeaderStackView:(MDCHeaderStackView *)headerStackView {
- if ([colorScheme respondsToSelector:@selector(primaryLightColor)]) {
- headerStackView.topBar.backgroundColor = colorScheme.primaryLightColor;
- }
- headerStackView.bottomBar.backgroundColor = colorScheme.primaryColor;
-}
-
-@end
diff --git a/components/HeaderStackView/src/ColorThemer/MaterialHeaderStackView+ColorThemer.h b/components/HeaderStackView/src/ColorThemer/MaterialHeaderStackView+ColorThemer.h
deleted file mode 100644
index 5dcf9fc5b52..00000000000
--- a/components/HeaderStackView/src/ColorThemer/MaterialHeaderStackView+ColorThemer.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018-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 "MDCHeaderStackViewColorThemer.h"
diff --git a/components/Ink/README.md b/components/Ink/README.md
index f5c2ec33136..086ea08e155 100644
--- a/components/Ink/README.md
+++ b/components/Ink/README.md
@@ -22,13 +22,12 @@ the user's touch.
## Design & API documentation
## Table of contents
diff --git a/components/LibraryInfo/src/MDCLibraryInfo.m b/components/LibraryInfo/src/MDCLibraryInfo.m
index df1227ab8d1..0f4aec508be 100644
--- a/components/LibraryInfo/src/MDCLibraryInfo.m
+++ b/components/LibraryInfo/src/MDCLibraryInfo.m
@@ -19,7 +19,7 @@
// This string is updated automatically as a part of the release process and should not be edited
// manually. Do not rename this constant or change the formatting without updating the release
// scripts.
-static NSString* const kMDCLibraryInfoVersionString = @"111.0.0";
+static NSString* const kMDCLibraryInfoVersionString = @"112.0.0";
@implementation MDCLibraryInfo
diff --git a/components/LibraryInfo/tests/unit/LibraryInfoTests.m b/components/LibraryInfo/tests/unit/LibraryInfoTests.m
index 3cd91150309..367aafe04d8 100644
--- a/components/LibraryInfo/tests/unit/LibraryInfoTests.m
+++ b/components/LibraryInfo/tests/unit/LibraryInfoTests.m
@@ -26,7 +26,7 @@ - (void)testVersionFormat {
// Given
// This regex pattern does the following:
- // Accept: "111.0.0", etc.
+ // Accept: "112.0.0", etc.
// Reject: "0.0.0", "1.2", "1", "-1.2.3", "Hi, I'm a version 1.2.3", "1.2.3 is my version", etc.
//
// Note the major version must be >= 1 since "0.0.0" is used as the version when something goes
diff --git a/components/NavigationBar/README.md b/components/NavigationBar/README.md
index 5e656d852e3..39a63d4c369 100644
--- a/components/NavigationBar/README.md
+++ b/components/NavigationBar/README.md
@@ -25,12 +25,11 @@ custom title view.
## Related components
diff --git a/components/NavigationDrawer/README.md b/components/NavigationDrawer/README.md
index 209480e858e..d18d1015a5e 100644
--- a/components/NavigationDrawer/README.md
+++ b/components/NavigationDrawer/README.md
@@ -25,14 +25,13 @@ Navigation drawers are recommended for:
## Table of contents
diff --git a/components/PageControl/README.md b/components/PageControl/README.md
index 63716dad5d7..97f5248bd03 100644
--- a/components/PageControl/README.md
+++ b/components/PageControl/README.md
@@ -26,7 +26,7 @@ desired animation of the control.
## Design & API documentation
## Table of contents
diff --git a/components/Palettes/README.md b/components/Palettes/README.md
index d13512ed44c..8aa8c281171 100644
--- a/components/Palettes/README.md
+++ b/components/Palettes/README.md
@@ -16,7 +16,7 @@ The Palettes component provides Material colors organized into similar palettes.
- - -
diff --git a/components/ProgressView/README.md b/components/ProgressView/README.md
index 1911b6a37a9..8b66f57ca4d 100644
--- a/components/ProgressView/README.md
+++ b/components/ProgressView/README.md
@@ -24,9 +24,8 @@ Progress view is a linear progress indicator that implements Material Design ani
## Related components
diff --git a/components/ProgressView/src/MDCProgressView.m b/components/ProgressView/src/MDCProgressView.m
index 68513f1526d..752170434ba 100644
--- a/components/ProgressView/src/MDCProgressView.m
+++ b/components/ProgressView/src/MDCProgressView.m
@@ -217,11 +217,6 @@ - (void)setProgress:(float)progress
completion:userCompletion];
}
-- (void)setHidden:(BOOL)hidden {
- [super setHidden:hidden];
- UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, hidden ? nil : self);
-}
-
- (void)setHidden:(BOOL)hidden
animated:(BOOL)animated
completion:(void (^__nullable)(BOOL finished))userCompletion {
diff --git a/components/Ripple/README.md b/components/Ripple/README.md
index 31166bef650..8354b38a1da 100644
--- a/components/Ripple/README.md
+++ b/components/Ripple/README.md
@@ -23,14 +23,13 @@ Ripple is a visual form of feedback for touch events providing users a clear sig
## Design & API documentation
## Table of contents
diff --git a/components/ShadowElevations/README.md b/components/ShadowElevations/README.md
index 96f3d73bb67..1ccc4ba070a 100644
--- a/components/ShadowElevations/README.md
+++ b/components/ShadowElevations/README.md
@@ -24,7 +24,7 @@ used Material Design elevations for components.
- - -
diff --git a/components/ShadowLayer/README.md b/components/ShadowLayer/README.md
index 7c15e13946c..1cb91371edc 100644
--- a/components/ShadowLayer/README.md
+++ b/components/ShadowLayer/README.md
@@ -24,8 +24,8 @@ elevation.
### MDCShadowLayer
diff --git a/components/Shapes/src/MDCShapedShadowLayer.m b/components/Shapes/src/MDCShapedShadowLayer.m
index c3afcba9e04..38110ade172 100644
--- a/components/Shapes/src/MDCShapedShadowLayer.m
+++ b/components/Shapes/src/MDCShapedShadowLayer.m
@@ -17,6 +17,9 @@
#import "MDCShapeGenerating.h"
#import "MaterialColor.h"
+// An epsilon for use with width/height values.
+static const CGFloat kDimensionalEpsilon = 0.001;
+
@implementation MDCShapedShadowLayer
- (instancetype)init {
@@ -104,7 +107,38 @@ - (void)setPath:(CGPathRef)path {
_colorLayer.fillColor = self.shapedBackgroundColor.CGColor;
_colorLayer.strokeColor = self.shapedBorderColor.CGColor;
_colorLayer.lineWidth = self.shapedBorderWidth;
+ [self generateColorPathGivenLineWidth];
+ }
+}
+
+- (void)generateColorPathGivenLineWidth {
+ if (CGPathIsEmpty(self.path) || _colorLayer.lineWidth <= 0) {
+ _colorLayer.path = self.shadowPath;
+ _shapeLayer.path = self.shadowPath;
+ return;
+ }
+ CGFloat halfOfBorderWidth = self.shapedBorderWidth / 2.f;
+ CGAffineTransform colorLayerTransform = [self generateTransformInsetByValue:halfOfBorderWidth];
+ _colorLayer.path = CGPathCreateCopyByTransformingPath(self.shadowPath, &colorLayerTransform);
+ // The shape layer is used to provide the user a mask for their content, which means also
+ // show the full border. Because the border is shown half outside and half inside
+ // the color layer path, we must inset the shape layer by the full border width.
+ CGAffineTransform shapeLayerTransform =
+ [self generateTransformInsetByValue:self.shapedBorderWidth];
+ _shapeLayer.path = CGPathCreateCopyByTransformingPath(self.shadowPath, &shapeLayerTransform);
+}
+
+- (CGAffineTransform)generateTransformInsetByValue:(CGFloat)value {
+ CGRect standardizedBounds = CGRectStandardize(self.bounds);
+ CGRect insetBounds = CGRectInset(standardizedBounds, value, value);
+ CGAffineTransform transform = CGAffineTransformMakeTranslation(value, value);
+ CGFloat width = CGRectGetWidth(standardizedBounds);
+ CGFloat height = CGRectGetHeight(standardizedBounds);
+ if (width > kDimensionalEpsilon && height > kDimensionalEpsilon) {
+ transform = CGAffineTransformScale(transform, CGRectGetWidth(insetBounds) / width,
+ CGRectGetHeight(insetBounds) / height);
}
+ return transform;
}
- (CGPathRef)path {
@@ -155,6 +189,7 @@ - (void)setShapedBorderWidth:(CGFloat)shapedBorderWidth {
} else {
self.borderWidth = 0;
_colorLayer.lineWidth = _shapedBorderWidth;
+ [self generateColorPathGivenLineWidth];
}
}
diff --git a/components/Shapes/tests/snapshot/MDCShapedViewSnapshotTests.m b/components/Shapes/tests/snapshot/MDCShapedViewSnapshotTests.m
index dc52760c21c..2129dd9562b 100644
--- a/components/Shapes/tests/snapshot/MDCShapedViewSnapshotTests.m
+++ b/components/Shapes/tests/snapshot/MDCShapedViewSnapshotTests.m
@@ -52,6 +52,57 @@ - (void)generateSnapshotAndVerifyView {
#pragma mark - Tests
+- (void)testRectShapedViewWithBorderWidthAndCorrectMaskingOfContent {
+ // Given
+ MDCShapedShadowLayer *shadowLayer = (MDCShapedShadowLayer *)self.shapedView.layer;
+ MDCRectangleShapeGenerator *shapeGenerator = [[MDCRectangleShapeGenerator alloc] init];
+ MDCRoundedCornerTreatment *cornerTreatment = [MDCRoundedCornerTreatment cornerWithRadius:50.f];
+ [shapeGenerator setCorners:cornerTreatment];
+ shadowLayer.shapedBorderWidth = 10;
+ shadowLayer.shapedBorderColor = UIColor.redColor;
+ UIView *contentView = [[UIView alloc] initWithFrame:self.shapedView.bounds];
+ contentView.backgroundColor = UIColor.systemPinkColor;
+ [self.shapedView addSubview:contentView];
+
+ // When
+ self.shapedView.shapeGenerator = shapeGenerator;
+ contentView.layer.mask = shadowLayer.shapeLayer;
+
+ // Then
+ [self generateSnapshotAndVerifyView];
+}
+
+- (void)testRectShapedViewWithCornerRadiusBySettingABorderWidthToPositiveThenZero {
+ // Given
+ MDCRectangleShapeGenerator *shapeGenerator = [[MDCRectangleShapeGenerator alloc] init];
+ MDCRoundedCornerTreatment *cornerTreatment = [MDCRoundedCornerTreatment cornerWithRadius:50.f];
+ [shapeGenerator setCorners:cornerTreatment];
+ ((MDCShapedShadowLayer *)self.shapedView.layer).shapedBorderWidth = 10;
+ ((MDCShapedShadowLayer *)self.shapedView.layer).shapedBorderColor = UIColor.redColor;
+
+ // When
+ self.shapedView.shapeGenerator = shapeGenerator;
+ ((MDCShapedShadowLayer *)self.shapedView.layer).shapedBorderWidth = 0;
+
+ // Then
+ [self generateSnapshotAndVerifyView];
+}
+
+- (void)testRectShapedViewWithCornerRadius {
+ // Given
+ MDCRectangleShapeGenerator *shapeGenerator = [[MDCRectangleShapeGenerator alloc] init];
+ MDCRoundedCornerTreatment *cornerTreatment = [MDCRoundedCornerTreatment cornerWithRadius:50.f];
+ [shapeGenerator setCorners:cornerTreatment];
+ ((MDCShapedShadowLayer *)self.shapedView.layer).shapedBorderWidth = 10;
+ ((MDCShapedShadowLayer *)self.shapedView.layer).shapedBorderColor = UIColor.redColor;
+
+ // When
+ self.shapedView.shapeGenerator = shapeGenerator;
+
+ // Then
+ [self generateSnapshotAndVerifyView];
+}
+
- (void)testCurvedRectShapedViewElevation00 {
// When
self.shapedView.shapeGenerator =
diff --git a/components/Shapes/tests/unit/MDCShapedShadowLayerPathTests.m b/components/Shapes/tests/unit/MDCShapedShadowLayerPathTests.m
new file mode 100644
index 00000000000..25a770a104e
--- /dev/null
+++ b/components/Shapes/tests/unit/MDCShapedShadowLayerPathTests.m
@@ -0,0 +1,63 @@
+#import "MaterialShapes.h"
+
+#import
+
+@interface MDCShapedShadowLayer (UnitTesting)
+
+- (void)setPath:(CGPathRef)path;
+
+@end
+
+@interface MDCShapedShadowLayerPathTests : XCTestCase
+@end
+
+@implementation MDCShapedShadowLayerPathTests
+
+- (void)testEmptyFrameWithNonEmptyPathGeneratesColorLayerPathMatchingPath {
+ // Given
+ MDCShapedShadowLayer *shadowLayer = [[MDCShapedShadowLayer alloc] init];
+ UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];
+
+ // When
+ shadowLayer.frame = CGRectZero;
+ shadowLayer.path = bezierPath.CGPath;
+
+ // Then
+ XCTAssertTrue(CGPathEqualToPath(shadowLayer.colorLayer.path, bezierPath.CGPath));
+}
+
+- (void)
+ testEmptyFrameWithNonEmptyPathAndPositiveShapedBorderWidthGeneratesColorLayerPathOffsetByHalfOfLineWidth {
+ // Given
+ MDCShapedShadowLayer *shadowLayer = [[MDCShapedShadowLayer alloc] init];
+ UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];
+
+ // When
+ shadowLayer.frame = CGRectZero;
+ shadowLayer.shapedBorderWidth = 2;
+ shadowLayer.path = bezierPath.CGPath;
+
+ // Then
+ // Note that the X and Y values here are shifted by half of the shaped border width.
+ UIBezierPath *insetPath = [UIBezierPath bezierPathWithRect:CGRectMake(1, 1, 100, 100)];
+ XCTAssertTrue(CGPathEqualToPath(shadowLayer.colorLayer.path, insetPath.CGPath));
+}
+
+- (void)testGeneratedColorLayerAndShapeLayerPathsGivenABorderAndShadowLayer {
+ // Given
+ MDCShapedShadowLayer *shadowLayer = [[MDCShapedShadowLayer alloc] init];
+ UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];
+
+ // When
+ shadowLayer.frame = CGRectMake(0, 0, 100, 100);
+ shadowLayer.shapedBorderWidth = 6;
+ shadowLayer.path = bezierPath.CGPath;
+
+ // Then
+ UIBezierPath *halfInsetPath = [UIBezierPath bezierPathWithRect:CGRectMake(3, 3, 94, 94)];
+ XCTAssertTrue(CGPathEqualToPath(shadowLayer.colorLayer.path, halfInsetPath.CGPath));
+ UIBezierPath *fullInsetPath = [UIBezierPath bezierPathWithRect:CGRectMake(6, 6, 88, 88)];
+ XCTAssertTrue(CGPathEqualToPath(shadowLayer.shapeLayer.path, fullInsetPath.CGPath));
+}
+
+@end
diff --git a/components/Slider/README.md b/components/Slider/README.md
index 19926ce7671..18cb64befa6 100644
--- a/components/Slider/README.md
+++ b/components/Slider/README.md
@@ -25,10 +25,9 @@ or discrete set of values.
## Table of contents
diff --git a/components/Slider/examples/SliderCollectionViewController.m b/components/Slider/examples/SliderCollectionViewController.m
index b894093b181..b1f6555fcbf 100644
--- a/components/Slider/examples/SliderCollectionViewController.m
+++ b/components/Slider/examples/SliderCollectionViewController.m
@@ -18,7 +18,6 @@
#import "MaterialSlider+ColorThemer.h"
#import "MaterialSlider.h"
#import "MaterialTypographyScheme.h"
-#import "supplemental/SliderCollectionSupplemental.h"
static NSString *const kReusableIdentifierItem = @"sliderItemCellIdentifier";
static CGFloat const kSliderHorizontalMargin = 16;
@@ -208,6 +207,10 @@ - (CGFloat)minimumInteritemSpacing {
@end
+@interface SliderCollectionViewController : UICollectionViewController
+@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
+@end
+
@implementation SliderCollectionViewController {
NSMutableArray *_sliders;
MDCTypographyScheme *_typographyScheme;
@@ -316,3 +319,16 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
}
@end
+
+@implementation SliderCollectionViewController (CatalogByConvention)
+
++ (NSDictionary *)catalogMetadata {
+ return @{
+ @"breadcrumbs" : @[ @"Slider", @"Slider" ],
+ @"description" : @"Sliders allow users to make selections from a range of values.",
+ @"primaryDemo" : @YES,
+ @"presentable" : @YES,
+ };
+}
+
+@end
diff --git a/components/Slider/examples/SliderEarlGreyTestViewController.m b/components/Slider/examples/SliderEarlGreyTestViewController.m
new file mode 100644
index 00000000000..8aad107294f
--- /dev/null
+++ b/components/Slider/examples/SliderEarlGreyTestViewController.m
@@ -0,0 +1,74 @@
+// 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
+
+#import "MaterialSlider.h"
+
+@interface SliderEarlGreyTestViewController : UIViewController
+@property(nonatomic, strong, nullable) UILabel *valueLabel;
+@property(nonatomic, strong, nullable) MDCSlider *slider;
+@end
+
+@implementation SliderEarlGreyTestViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.backgroundColor = UIColor.whiteColor;
+
+ self.slider = [[MDCSlider alloc] init];
+ [self.slider addTarget:self
+ action:@selector(sliderValueChanged)
+ forControlEvents:UIControlEventValueChanged];
+ self.slider.minimumValue = 0;
+ self.slider.maximumValue = 10;
+ self.slider.filledTrackAnchorValue = 5;
+ self.slider.value = 5;
+ self.slider.accessibilityIdentifier = @"slider";
+
+ self.valueLabel = [[UILabel alloc] init];
+ self.valueLabel.text = @"5";
+ self.valueLabel.textAlignment = NSTextAlignmentCenter;
+ self.valueLabel.textColor = UIColor.blackColor;
+ self.valueLabel.accessibilityIdentifier = @"slider_value_label";
+
+ UIStackView *stackView = [[UIStackView alloc] init];
+ stackView.axis = UILayoutConstraintAxisVertical;
+ stackView.spacing = 10;
+ stackView.translatesAutoresizingMaskIntoConstraints = NO;
+ [stackView addArrangedSubview:self.valueLabel];
+ [stackView addArrangedSubview:self.slider];
+
+ [self.view addSubview:stackView];
+ [stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES;
+ [stackView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES;
+ [stackView.widthAnchor constraintEqualToConstant:300].active = YES;
+}
+
+- (void)sliderValueChanged {
+ self.valueLabel.text = [NSString stringWithFormat:@"%.f", self.slider.value];
+}
+
+#pragma mark - CatalogByConvention
+
++ (NSDictionary *)catalogMetadata {
+ return @{
+ @"breadcrumbs" : @[ @"Slider", @"Earl Grey Testbed" ],
+ @"primaryDemo" : @NO,
+ @"presentable" : @NO,
+ };
+}
+
+@end
diff --git a/components/Slider/examples/supplemental/SliderCollectionSupplemental.h b/components/Slider/examples/supplemental/SliderCollectionSupplemental.h
deleted file mode 100644
index dbdb1ef1d8b..00000000000
--- a/components/Slider/examples/supplemental/SliderCollectionSupplemental.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016-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.
-
-/* IMPORTANT:
- This file contains supplemental code used to populate the demos with dummy data or instructions.
- It is not necessary to import this file to use Material Components for iOS.
- */
-
-#import
-#import "MaterialColorScheme.h"
-
-@interface SliderCollectionViewController : UICollectionViewController
-@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
-@end
diff --git a/components/Slider/examples/supplemental/SliderCollectionSupplemental.m b/components/Slider/examples/supplemental/SliderCollectionSupplemental.m
deleted file mode 100644
index b295b4f3c90..00000000000
--- a/components/Slider/examples/supplemental/SliderCollectionSupplemental.m
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016-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.
-
-/* IMPORTANT:
- This file contains supplemental code used to populate the demos with dummy data or instructions.
- It is not necessary to import this file to use Material Components for iOS.
- */
-
-#import "SliderCollectionSupplemental.h"
-
-@implementation SliderCollectionViewController (CatalogByConvention)
-
-+ (NSDictionary *)catalogMetadata {
- return @{
- @"breadcrumbs" : @[ @"Slider", @"Slider" ],
- @"description" : @"Sliders allow users to make selections from a range of values.",
- @"primaryDemo" : @YES,
- @"presentable" : @YES,
- };
-}
-
-@end
diff --git a/components/Snackbar/README.md b/components/Snackbar/README.md
index 2ee521c35d7..88f9d38d4bf 100644
--- a/components/Snackbar/README.md
+++ b/components/Snackbar/README.md
@@ -26,14 +26,13 @@ contain a text action, but no icons.
## Related components
@@ -55,7 +54,6 @@ contain a text action, but no icons.
- [Typical use: display a message with an action](#typical-use-display-a-message-with-an-action)
- [Extensions](#extensions)
- [Color Theming](#color-theming)
- - [Typography Theming](#typography-theming)
- - -
@@ -184,42 +182,3 @@ message.action = action;
There is currently no theing extension for MDCSnackbar.
-
-
-### Typography Theming
-
-You can theme an snackbar with your app's typography scheme using the TypographyThemer extension.
-
-You must first add the Typography Themer extension to your project:
-
-```bash
-pod 'MaterialComponents/Snackbar+TypographyThemer'
-```
-
-
-#### Swift
-```swift
-// Step 1: Import the TypographyThemer extension
-import MaterialComponents.MaterialSnackbar_TypographyThemer
-
-// Step 2: Create or get a typography scheme
-let typographyScheme = MDCTypographyScheme()
-
-// Step 3: Apply the typography scheme to your component
-MDCSnackbarTypographyThemer.applyTypographyScheme(typographyScheme)
-```
-
-#### Objective-C
-
-```objc
-// Step 1: Import the TypographyThemer extension
-#import "MaterialSnackbar+TypographyThemer.h"
-
-// Step 2: Create or get a typography scheme
-id typographyScheme = [[MDCTypographyScheme alloc] init];
-
-// Step 3: Apply the typography scheme to your component
-[MDCSnackbarTypographyThemer applyTypographyScheme:colorScheme];
-```
-
-
diff --git a/components/Snackbar/docs/README.md b/components/Snackbar/docs/README.md
index c44cbe172bc..305921a093f 100644
--- a/components/Snackbar/docs/README.md
+++ b/components/Snackbar/docs/README.md
@@ -53,4 +53,3 @@ visible to the user.
## Extensions
- [Color Theming](color-theming.md)
-- [Typography Theming](typography-theming.md)
diff --git a/components/Snackbar/docs/typography-theming.md b/components/Snackbar/docs/typography-theming.md
deleted file mode 100644
index af381a1b721..00000000000
--- a/components/Snackbar/docs/typography-theming.md
+++ /dev/null
@@ -1,36 +0,0 @@
-### Typography Theming
-
-You can theme an snackbar with your app's typography scheme using the TypographyThemer extension.
-
-You must first add the Typography Themer extension to your project:
-
-```bash
-pod 'MaterialComponents/Snackbar+TypographyThemer'
-```
-
-
-#### Swift
-```swift
-// Step 1: Import the TypographyThemer extension
-import MaterialComponents.MaterialSnackbar_TypographyThemer
-
-// Step 2: Create or get a typography scheme
-let typographyScheme = MDCTypographyScheme()
-
-// Step 3: Apply the typography scheme to your component
-MDCSnackbarTypographyThemer.applyTypographyScheme(typographyScheme)
-```
-
-#### Objective-C
-
-```objc
-// Step 1: Import the TypographyThemer extension
-#import "MaterialSnackbar+TypographyThemer.h"
-
-// Step 2: Create or get a typography scheme
-id typographyScheme = [[MDCTypographyScheme alloc] init];
-
-// Step 3: Apply the typography scheme to your component
-[MDCSnackbarTypographyThemer applyTypographyScheme:colorScheme];
-```
-
diff --git a/components/Snackbar/examples/supplemental/SnackbarExampleSupplemental.m b/components/Snackbar/examples/supplemental/SnackbarExampleSupplemental.m
index fd8daa04780..6d26b7f78ab 100644
--- a/components/Snackbar/examples/supplemental/SnackbarExampleSupplemental.m
+++ b/components/Snackbar/examples/supplemental/SnackbarExampleSupplemental.m
@@ -13,7 +13,6 @@
// limitations under the License.
#import "SnackbarExampleSupplemental.h"
-#import "MaterialSnackbar+TypographyThemer.h"
static NSString *const kCellIdentifier = @"Cell";
@@ -24,7 +23,6 @@ - (void)setupExampleViews:(NSArray *)choices {
self.view.backgroundColor = [UIColor whiteColor];
[self.collectionView registerClass:[MDCCollectionViewTextCell class]
forCellWithReuseIdentifier:kCellIdentifier];
- [MDCSnackbarTypographyThemer applyTypographyScheme:self.typographyScheme];
}
#pragma mark - UICollectionView
diff --git a/components/Snackbar/src/FontThemer/MDCSnackbarFontThemer.h b/components/Snackbar/src/FontThemer/MDCSnackbarFontThemer.h
deleted file mode 100644
index 5f3ba74c884..00000000000
--- a/components/Snackbar/src/FontThemer/MDCSnackbarFontThemer.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018-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
-
-#import "MaterialSnackbar.h"
-#import "MaterialThemes.h"
-
-#pragma mark - Soon to be deprecated
-
-/**
- Used to apply a font scheme to theme to MDCSnackbarMessageView.
-
- @warning This API will eventually be deprecated. See the individual method documentation for
- details on replacement APIs.
- Learn more at docs/theming.md#migration-guide-themers-to-theming-extensions
- */
-@interface MDCSnackbarFontThemer : NSObject
-@end
-
-@interface MDCSnackbarFontThemer (Deprecated)
-
-/**
- Applies a font scheme to theme to a MDCSnackbarMessageView.
-
- @warning This API will soon be deprecated. Please consider using MDCSnackbarTypographyThemer
- instead.
-
- @param fontScheme The font scheme to apply to MDCSnackbarMessageView.
- @param snackbarMessageView A MDCSnackbarMessageView instance to apply a font scheme.
-
- @warning This API will eventually be deprecated. There is no replacement yet.
- Track progress here: https://github.com/material-components/material-components-ios/issues/7172
- Learn more at docs/theming.md#migration-guide-themers-to-theming-extensions
- */
-+ (void)applyFontScheme:(nonnull id)fontScheme
- toSnackbarMessageView:(nonnull MDCSnackbarMessageView *)snackbarMessageView;
-
-/**
- Applies a font scheme to theme to a MDCSnackbarMessageView.
-
- @param fontScheme The font scheme to apply to MDCSnackbarMessageView.
-
- @warning This API will eventually be deprecated. There is no replacement yet.
- Track progress here: https://github.com/material-components/material-components-ios/issues/7172
- Learn more at docs/theming.md#migration-guide-themers-to-theming-extensions
- */
-+ (void)applyFontScheme:(nonnull id)fontScheme;
-
-@end
diff --git a/components/Snackbar/src/FontThemer/MDCSnackbarFontThemer.m b/components/Snackbar/src/FontThemer/MDCSnackbarFontThemer.m
deleted file mode 100644
index 1aa16b721fd..00000000000
--- a/components/Snackbar/src/FontThemer/MDCSnackbarFontThemer.m
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018-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 "MDCSnackbarFontThemer.h"
-
-@implementation MDCSnackbarFontThemer
-
-+ (void)applyFontScheme:(nonnull id)fontScheme
- toSnackbarMessageView:(nonnull MDCSnackbarMessageView *)snackbarMessageView {
- snackbarMessageView.messageFont = fontScheme.body2;
- snackbarMessageView.buttonFont = fontScheme.button;
-}
-
-+ (void)applyFontScheme:(nonnull id)fontScheme {
- MDCSnackbarManager.messageFont = fontScheme.body2;
- MDCSnackbarManager.buttonFont = fontScheme.button;
-}
-
-@end
diff --git a/components/Snackbar/src/MDCSnackbarMessageView.m b/components/Snackbar/src/MDCSnackbarMessageView.m
index 1d6d932a362..5184856a449 100644
--- a/components/Snackbar/src/MDCSnackbarMessageView.m
+++ b/components/Snackbar/src/MDCSnackbarMessageView.m
@@ -66,8 +66,9 @@ static BOOL UIViewHasFocusedAccessibilityElement(UIView *view) {
/**
Padding between the edges of the Snackbar and any content.
*/
-static UIEdgeInsets kContentMargin = (UIEdgeInsets){16.0, 16.0, 16.0, 8.0};
-static UIEdgeInsets kLegacyContentMargin = (UIEdgeInsets){18.0, 24.0, 18.0, 24.0};
+static const UIEdgeInsets kContentMarginSingleLineText = (UIEdgeInsets){6.0, 16.0, 6.0, 8.0};
+static const UIEdgeInsets kContentMarginMutliLineText = (UIEdgeInsets){16.0, 16.0, 16.0, 8.0};
+static const UIEdgeInsets kLegacyContentMargin = (UIEdgeInsets){18.0, 24.0, 18.0, 24.0};
/**
Padding between the image and the main title.
@@ -98,6 +99,11 @@ static BOOL UIViewHasFocusedAccessibilityElement(UIView *view) {
*/
static const CGFloat kMinimumHeight = 48;
+/**
+ The minimum height of a multiline Snackbar.
+ */
+static const CGFloat kMinimumHeightMultiline = 68;
+
/**
Each button will have a tag indexed starting from this value.
*/
@@ -207,6 +213,8 @@ @implementation MDCSnackbarMessageView {
BOOL _mdc_adjustsFontForContentSizeCategory;
BOOL _shouldDismissOnOverlayTap;
+
+ BOOL _isMultilineText;
}
@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation;
@@ -674,21 +682,30 @@ - (BOOL)shouldWaitForDismissalDuringVoiceover {
#pragma mark - Constraints and layout
+- (NSInteger)numberOfLines {
+ CGSize maxLabelSize = self.label.intrinsicContentSize;
+ CGFloat lineHeight = self.label.font.lineHeight;
+ return (NSInteger)MDCRound(maxLabelSize.height / lineHeight);
+}
+
+- (void)resetConstraints {
+ [self removeConstraints:self.viewConstraints];
+ self.viewConstraints = nil;
+ [self setNeedsUpdateConstraints];
+}
+
- (void)setAnchoredToScreenBottom:(BOOL)anchoredToScreenBottom {
_anchoredToScreenBottom = anchoredToScreenBottom;
[self invalidateIntrinsicContentSize];
if (self.viewConstraints) {
- [self removeConstraints:self.viewConstraints];
- self.viewConstraints = nil;
- [self updateConstraints];
+ [self resetConstraints];
}
}
- (void)updateConstraints {
- [super updateConstraints];
-
if (self.viewConstraints) {
+ [super updateConstraints];
return;
}
@@ -700,6 +717,8 @@ - (void)updateConstraints {
[self addConstraints:constraints];
self.viewConstraints = constraints;
+
+ [super updateConstraints];
}
/**
@@ -707,13 +726,14 @@ - (void)updateConstraints {
@c kBorderWidth. Also positions the content view and button view inside of the container view.
*/
- (NSArray *)containerViewConstraints {
+ UIEdgeInsets safeContentMargin = self.safeContentMargin;
NSDictionary *metrics = @{
@"kBorderMargin" : @(kBorderWidth),
- @"kBottomMargin" : @(self.safeContentMargin.bottom),
- @"kLeftMargin" : @(self.safeContentMargin.left),
- @"kRightMargin" : @(self.safeContentMargin.right),
+ @"kBottomMargin" : @(safeContentMargin.bottom),
+ @"kLeftMargin" : @(safeContentMargin.left),
+ @"kRightMargin" : @(safeContentMargin.right),
@"kTitleImagePadding" : @(kTitleImagePadding),
- @"kTopMargin" : @(self.safeContentMargin.top),
+ @"kTopMargin" : @(safeContentMargin.top),
@"kTitleButtonPadding" : @(kTitleButtonPadding),
@"kContentSafeBottomInset" : @(kBorderWidth + self.contentSafeBottomInset),
};
@@ -835,12 +855,13 @@ - (NSArray *)containerViewConstraints {
Provides constraints for the image view and label within the content view.
*/
- (NSArray *)contentViewConstraints {
+ UIEdgeInsets safeContentMargin = self.safeContentMargin;
NSDictionary *metrics = @{
- @"kBottomMargin" : @(self.safeContentMargin.bottom),
- @"kLeftMargin" : @(self.safeContentMargin.left),
- @"kRightMargin" : @(self.safeContentMargin.right),
+ @"kBottomMargin" : @(safeContentMargin.bottom),
+ @"kLeftMargin" : @(safeContentMargin.left),
+ @"kRightMargin" : @(safeContentMargin.right),
@"kTitleImagePadding" : @(kTitleImagePadding),
- @"kTopMargin" : @(self.safeContentMargin.top),
+ @"kTopMargin" : @(safeContentMargin.top),
};
NSMutableDictionary *views = [NSMutableDictionary dictionary];
@@ -906,12 +927,12 @@ - (NSArray *)contentViewConstraints {
*/
- (NSArray *)horizontalButtonLayoutConstraints {
NSMutableArray *constraints = [NSMutableArray array];
-
+ UIEdgeInsets safeContentMargin = self.safeContentMargin;
NSDictionary *metrics = @{
- @"kLeftMargin" : @(self.safeContentMargin.left),
- @"kRightMargin" : @(self.safeContentMargin.right),
- @"kTopMargin" : @(self.safeContentMargin.top),
- @"kBottomMargin" : @(self.safeContentMargin.bottom),
+ @"kLeftMargin" : @(safeContentMargin.left),
+ @"kRightMargin" : @(safeContentMargin.right),
+ @"kTopMargin" : @(safeContentMargin.top),
+ @"kBottomMargin" : @(safeContentMargin.bottom),
@"kTitleImagePadding" : @(kTitleImagePadding),
@"kBorderMargin" : @(kBorderWidth),
@"kTitleButtonPadding" : @(kTitleButtonPadding),
@@ -989,6 +1010,12 @@ - (NSArray *)horizontalButtonLayoutConstraints {
- (void)layoutSubviews {
[super layoutSubviews];
+ BOOL isMultilineText = [self numberOfLines] > 1;
+ if (_isMultilineText != isMultilineText) {
+ _isMultilineText = isMultilineText;
+ [self resetConstraints];
+ }
+
// As our layout changes, make sure that the shadow path is kept up-to-date.
UIBezierPath *path = [UIBezierPath
bezierPathWithRoundedRect:self.bounds
@@ -1012,7 +1039,8 @@ - (CGSize)intrinsicContentSize {
height += self.safeContentMargin.top + self.safeContentMargin.bottom;
// Make sure that the height of the image and text is larger than the minimum height;
- height = MAX(kMinimumHeight, height) + self.contentSafeBottomInset;
+ height = MAX(_isMultilineText ? kMinimumHeightMultiline : kMinimumHeight, height) +
+ self.contentSafeBottomInset;
return CGSizeMake(UIViewNoIntrinsicMetric, height);
}
@@ -1031,8 +1059,12 @@ - (CGFloat)contentSafeBottomInset {
}
- (UIEdgeInsets)safeContentMargin {
- UIEdgeInsets contentMargin =
- MDCSnackbarMessage.usesLegacySnackbar ? kLegacyContentMargin : kContentMargin;
+ UIEdgeInsets contentMargin = UIEdgeInsetsZero;
+ if (MDCSnackbarMessage.usesLegacySnackbar) {
+ contentMargin = kLegacyContentMargin;
+ } else {
+ contentMargin = _isMultilineText ? kContentMarginMutliLineText : kContentMarginSingleLineText;
+ }
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
if (@available(iOS 11.0, *)) {
diff --git a/components/Snackbar/src/TypographyThemer/MDCSnackbarTypographyThemer.h b/components/Snackbar/src/TypographyThemer/MDCSnackbarTypographyThemer.h
deleted file mode 100644
index 5b2b2da758d..00000000000
--- a/components/Snackbar/src/TypographyThemer/MDCSnackbarTypographyThemer.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018-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 "MaterialSnackbar.h"
-#import "MaterialTypographyScheme.h"
-
-/**
- The Material Design typography system's themer for all snackbar messages.
-
- @warning This API will eventually be deprecated. See the individual method documentation for
- details on replacement APIs.
- Learn more at docs/theming.md#migration-guide-themers-to-theming-extensions
- */
-@interface MDCSnackbarTypographyThemer : NSObject
-@end
-
-@interface MDCSnackbarTypographyThemer (Deprecated)
-
-/**
- Applies a typography scheme's properties to all snackbar messages.
-
- @param typographyScheme The typography scheme to apply to all snackbar messages.
-
- @warning This API will eventually be deprecated. There is no replacement yet.
- Track progress here: https://github.com/material-components/material-components-ios/issues/7172
- Learn more at docs/theming.md#migration-guide-themers-to-theming-extensions
- */
-+ (void)applyTypographyScheme:(nonnull id)typographyScheme;
-
-@end
diff --git a/components/Snackbar/src/TypographyThemer/MDCSnackbarTypographyThemer.m b/components/Snackbar/src/TypographyThemer/MDCSnackbarTypographyThemer.m
deleted file mode 100644
index 28af0da1565..00000000000
--- a/components/Snackbar/src/TypographyThemer/MDCSnackbarTypographyThemer.m
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018-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 "MDCSnackbarTypographyThemer.h"
-
-@implementation MDCSnackbarTypographyThemer
-
-+ (void)applyTypographyScheme:(nonnull id)typographyScheme {
- MDCSnackbarManager.buttonFont = typographyScheme.button;
- MDCSnackbarManager.messageFont = typographyScheme.body2;
-}
-
-@end
diff --git a/components/Snackbar/src/TypographyThemer/MaterialSnackbar+TypographyThemer.h b/components/Snackbar/src/TypographyThemer/MaterialSnackbar+TypographyThemer.h
deleted file mode 100644
index 084d717079f..00000000000
--- a/components/Snackbar/src/TypographyThemer/MaterialSnackbar+TypographyThemer.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018-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 "MDCSnackbarTypographyThemer.h"
diff --git a/components/Snackbar/tests/unit/MDCSnackbarFontThemerTests.m b/components/Snackbar/tests/unit/MDCSnackbarFontThemerTests.m
deleted file mode 100644
index 9c802357d3e..00000000000
--- a/components/Snackbar/tests/unit/MDCSnackbarFontThemerTests.m
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018-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
-
-#import "MaterialSnackbar+FontThemer.h"
-#import "MaterialSnackbar.h"
-#import "MaterialThemes.h"
-
-@interface MDCSnackbarFontThemerTests : XCTestCase
-@end
-
-@implementation MDCSnackbarFontThemerTests
-
-- (void)tearDown {
- [MDCSnackbarManager dismissAndCallCompletionBlocksWithCategory:nil];
-
- [super tearDown];
-}
-
-- (void)testSnackbarFontThemerUsingUIAppearance {
- MDCSnackbarMessage *message = [[MDCSnackbarMessage alloc] init];
- message.text = @"How much wood would a woodchuck chuck if a woodchuck could chuck wood?";
- [MDCSnackbarManager showMessage:message];
- MDCBasicFontScheme *fontScheme = [[MDCBasicFontScheme alloc] init];
- fontScheme.button = [UIFont boldSystemFontOfSize:12];
- fontScheme.body2 = [UIFont systemFontOfSize:13];
- [MDCSnackbarFontThemer applyFontScheme:fontScheme
- toSnackbarMessageView:[MDCSnackbarMessageView appearance]];
- XCTAssertEqualObjects([MDCSnackbarMessageView appearance].messageFont, fontScheme.body2);
- XCTAssertEqualObjects([MDCSnackbarMessageView appearance].buttonFont, fontScheme.button);
-}
-
-- (void)testSnackbarFontThemer {
- MDCSnackbarMessage *message = [[MDCSnackbarMessage alloc] init];
- message.text = @"How much wood would a woodchuck chuck if a woodchuck could chuck wood?";
- [MDCSnackbarManager showMessage:message];
- MDCBasicFontScheme *fontScheme = [[MDCBasicFontScheme alloc] init];
- fontScheme.button = [UIFont boldSystemFontOfSize:12];
- fontScheme.body2 = [UIFont systemFontOfSize:13];
- [MDCSnackbarFontThemer applyFontScheme:fontScheme];
- XCTAssertEqualObjects(MDCSnackbarManager.messageFont, fontScheme.body2);
- XCTAssertEqualObjects(MDCSnackbarManager.buttonFont, fontScheme.button);
-}
-
-@end
diff --git a/components/Snackbar/tests/unit/MDCSnackbarTypographyThemerTests.swift b/components/Snackbar/tests/unit/MDCSnackbarTypographyThemerTests.swift
deleted file mode 100644
index dfb53a1ad98..00000000000
--- a/components/Snackbar/tests/unit/MDCSnackbarTypographyThemerTests.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2018-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 XCTest
-import MaterialComponents.MaterialSnackbar
-import MaterialComponents.MaterialSnackbar_TypographyThemer
-
-class MDCSnackbarTypographyThemerTests: XCTestCase {
-
- override func tearDown() {
- MDCSnackbarManager.dismissAndCallCompletionBlocks(withCategory: nil)
-
- super.tearDown()
- }
-
- func testSnackbarTypographyThemer() {
- // Given
- let typographyScheme = MDCTypographyScheme()
- let message = MDCSnackbarMessage()
- message.text = "How much wood would a woodchuck chuck if a woodchuck could chuck wood?"
- typographyScheme.button = UIFont.boldSystemFont(ofSize: 12)
- typographyScheme.body2 = UIFont.systemFont(ofSize: 19)
- MDCSnackbarManager.buttonFont = UIFont.systemFont(ofSize: 21)
- MDCSnackbarManager.messageFont = UIFont.systemFont(ofSize: 30)
-
- // When
- MDCSnackbarTypographyThemer.applyTypographyScheme(typographyScheme)
-
- MDCSnackbarManager.show(message)
-
- // Then
- XCTAssertEqual(MDCSnackbarManager.buttonFont, typographyScheme.button)
- XCTAssertEqual(MDCSnackbarManager.messageFont, typographyScheme.body2)
- }
-
-}
diff --git a/components/Tabs/README.md b/components/Tabs/README.md
index f55f7a6d040..88fe39175e4 100644
--- a/components/Tabs/README.md
+++ b/components/Tabs/README.md
@@ -24,19 +24,18 @@ Tabs are bars of buttons used to navigate between groups of content.
## Table of contents
diff --git a/components/Tabs/src/TabBarViewTheming/MDCTabBarView+MaterialTheming.h b/components/Tabs/src/TabBarViewTheming/MDCTabBarView+MaterialTheming.h
new file mode 100644
index 00000000000..11dedd791f3
--- /dev/null
+++ b/components/Tabs/src/TabBarViewTheming/MDCTabBarView+MaterialTheming.h
@@ -0,0 +1,38 @@
+// 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 "MaterialTabs+TabBarView.h"
+#import "MaterialContainerScheme.h"
+
+/**
+ This category is used to style MDCTabBarView instances to a specific Material style which can be
+ found within the [Material Guidelines](https://material.io/design/components/tabs.html).
+ */
+@interface MDCTabBarView (MaterialTheming)
+
+/**
+ Applies the Material Primary Theme to the receiver.
+
+ @param scheme A container scheme used for theming.
+ */
+- (void)applyPrimaryThemeWithScheme:(nonnull id)scheme;
+
+/**
+ Applies the Material Surface Theme to the receiver.
+
+ @param scheme A container scheme used for theming.
+ */
+- (void)applySurfaceThemeWithScheme:(nonnull id)scheme;
+
+@end
diff --git a/components/Tabs/src/TabBarViewTheming/MDCTabBarView+MaterialTheming.m b/components/Tabs/src/TabBarViewTheming/MDCTabBarView+MaterialTheming.m
new file mode 100644
index 00000000000..a6a8a552630
--- /dev/null
+++ b/components/Tabs/src/TabBarViewTheming/MDCTabBarView+MaterialTheming.m
@@ -0,0 +1,81 @@
+// 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 "MaterialTabs+TabBarView.h"
+#import "MDCTabBarView+MaterialTheming.h"
+
+#import
+#import
+
+#import "MaterialColorScheme.h"
+#import "MaterialContainerScheme.h"
+#import "MaterialTypographyScheme+Scheming.h"
+
+static const CGFloat kPrimaryThemeUnselectedOpacity = 0.74f;
+static const CGFloat kPrimaryThemeBottomDividerOpacity = 0.12f;
+static const CGFloat kSurfaceThemeUnselectedOpacity = 0.6f;
+
+@implementation MDCTabBarView (MaterialTheming)
+
+- (void)applyPrimaryThemeWithScheme:(nonnull id)scheme {
+ id colorScheme = scheme.colorScheme;
+ [self applyPrimaryThemeWithColorScheme:colorScheme];
+
+ id typographyScheme = scheme.typographyScheme;
+ [self applyThemeWithTypographyScheme:typographyScheme];
+}
+
+- (void)applySurfaceThemeWithScheme:(nonnull id)scheme {
+ id colorScheme = scheme.colorScheme;
+ [self applySurfaceThemeWithColorScheme:colorScheme];
+
+ id typographyScheme = scheme.typographyScheme;
+ [self applyThemeWithTypographyScheme:typographyScheme];
+}
+
+- (void)applyPrimaryThemeWithColorScheme:(id)colorScheme {
+ self.barTintColor = colorScheme.primaryColor;
+ self.bottomDividerColor =
+ [colorScheme.onPrimaryColor colorWithAlphaComponent:kPrimaryThemeBottomDividerOpacity];
+ self.selectionIndicatorStrokeColor = colorScheme.onPrimaryColor;
+ [self setTitleColor:colorScheme.onPrimaryColor forState:UIControlStateSelected];
+ [self setImageTintColor:colorScheme.onPrimaryColor forState:UIControlStateSelected];
+ UIColor *unselectedTitleColor =
+ [colorScheme.onPrimaryColor colorWithAlphaComponent:kPrimaryThemeUnselectedOpacity];
+ UIColor *unselectedImageColor =
+ [colorScheme.onPrimaryColor colorWithAlphaComponent:kPrimaryThemeUnselectedOpacity];
+ [self setTitleColor:unselectedTitleColor forState:UIControlStateNormal];
+ [self setImageTintColor:unselectedImageColor forState:UIControlStateNormal];
+}
+
+- (void)applySurfaceThemeWithColorScheme:(id)colorScheme {
+ self.barTintColor = colorScheme.surfaceColor;
+ self.bottomDividerColor = colorScheme.onSurfaceColor;
+ self.selectionIndicatorStrokeColor = colorScheme.primaryColor;
+ [self setTitleColor:colorScheme.primaryColor forState:UIControlStateSelected];
+ [self setImageTintColor:colorScheme.primaryColor forState:UIControlStateSelected];
+ UIColor *unselectedTitleColor =
+ [colorScheme.onSurfaceColor colorWithAlphaComponent:kSurfaceThemeUnselectedOpacity];
+ UIColor *unselectedImageColor =
+ [colorScheme.onSurfaceColor colorWithAlphaComponent:kSurfaceThemeUnselectedOpacity];
+ [self setTitleColor:unselectedTitleColor forState:UIControlStateNormal];
+ [self setImageTintColor:unselectedImageColor forState:UIControlStateNormal];
+}
+
+- (void)applyThemeWithTypographyScheme:(id)typographyScheme {
+ [self setTitleFont:typographyScheme.button forState:UIControlStateNormal];
+ [self setTitleFont:typographyScheme.button forState:UIControlStateSelected];
+}
+
+@end
diff --git a/components/Snackbar/src/FontThemer/MaterialSnackbar+FontThemer.h b/components/Tabs/src/TabBarViewTheming/MaterialTabs+TabBarViewTheming.h
similarity index 80%
rename from components/Snackbar/src/FontThemer/MaterialSnackbar+FontThemer.h
rename to components/Tabs/src/TabBarViewTheming/MaterialTabs+TabBarViewTheming.h
index 8c6e0d0c6ae..85d1ffa7f66 100644
--- a/components/Snackbar/src/FontThemer/MaterialSnackbar+FontThemer.h
+++ b/components/Tabs/src/TabBarViewTheming/MaterialTabs+TabBarViewTheming.h
@@ -1,4 +1,5 @@
-// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved.
+// 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.
@@ -12,4 +13,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "MDCSnackbarFontThemer.h"
+#import "MDCTabBarView+MaterialTheming.h"
diff --git a/components/Tabs/tests/unit/TabBarView/MDCTabBarViewThemingTests.m b/components/Tabs/tests/unit/TabBarView/MDCTabBarViewThemingTests.m
new file mode 100644
index 00000000000..f7a9bf09b3d
--- /dev/null
+++ b/components/Tabs/tests/unit/TabBarView/MDCTabBarViewThemingTests.m
@@ -0,0 +1,200 @@
+// 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 "MaterialTabs+TabBarViewTheming.h"
+
+#import
+
+#import "MaterialTabs+TabBarView.h"
+#import "MaterialColorScheme.h"
+#import "MaterialContainerScheme.h"
+#import "MaterialTypographyScheme.h"
+
+static const CGFloat kPrimaryThemeUnselectedOpacity = 0.74f;
+static const CGFloat kPrimaryThemeBottomDividerOpacity = 0.12f;
+static const CGFloat kSurfaceThemeUnselectedOpacity = 0.6f;
+
+/** Tests to confirm @c MDCTabBarView theming extension has the correct mappings. */
+@interface MDCTabBarViewThemingTest : XCTestCase
+/** The @c MDCTabBarView being tested. */
+@property(nonatomic, strong) MDCTabBarView *tabBar;
+/** The @c MDCSemanticColorScheme used to color the tabBar*/
+@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
+/** The @c MDCTypographyScheme used to provide fonts to the tabBar*/
+@property(nonatomic, strong) MDCTypographyScheme *typographyScheme;
+/** The @c MDCContainerScheme used to hold the colorScheme and typographyScheme*/
+@property(nonatomic, strong) MDCContainerScheme *containerScheme;
+@end
+
+@implementation MDCTabBarViewThemingTest
+
+- (void)setUp {
+ [super setUp];
+
+ self.tabBar = [[MDCTabBarView alloc] init];
+ self.colorScheme =
+ [[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804];
+ self.typographyScheme =
+ [[MDCTypographyScheme alloc] initWithDefaults:MDCTypographySchemeDefaultsMaterial201804];
+ self.containerScheme = [[MDCContainerScheme alloc] init];
+ self.containerScheme.colorScheme = self.colorScheme;
+ self.containerScheme.typographyScheme = self.typographyScheme;
+}
+
+- (void)tearDown {
+ self.containerScheme = nil;
+ self.typographyScheme = nil;
+ self.colorScheme = nil;
+ self.tabBar = nil;
+
+ [super tearDown];
+}
+
+- (void)testTabBarPrimaryThemingDefault {
+ // When
+ [self.tabBar applyPrimaryThemeWithScheme:self.containerScheme];
+
+ // Then
+ [self verifyTabBarPrimaryTheming];
+}
+
+- (void)testTabBarPrimaryThemingCustom {
+ // Given
+ self.colorScheme = [self customColorScheme];
+ self.typographyScheme = [self customTypographyScheme];
+ self.containerScheme.colorScheme = self.colorScheme;
+ self.containerScheme.typographyScheme = self.typographyScheme;
+
+ // When
+ [self.tabBar applyPrimaryThemeWithScheme:self.containerScheme];
+
+ // Then
+ [self verifyTabBarPrimaryTheming];
+}
+
+- (void)testTabBarSurfaceVariantThemingDefault {
+ // When
+ [self.tabBar applySurfaceThemeWithScheme:self.containerScheme];
+
+ // Then
+ [self verifyTabBarSurfaceVariantTheming];
+}
+
+- (void)testTabBarSurfaceVariantThemingCustom {
+ // Given
+ self.colorScheme = [self customColorScheme];
+ self.typographyScheme = [self customTypographyScheme];
+ self.containerScheme.colorScheme = self.colorScheme;
+ self.containerScheme.typographyScheme = self.typographyScheme;
+
+ // When
+ [self.tabBar applySurfaceThemeWithScheme:self.containerScheme];
+
+ // Then
+ [self verifyTabBarSurfaceVariantTheming];
+}
+
+#pragma mark - Test helpers
+
+- (MDCSemanticColorScheme *)customColorScheme {
+ MDCSemanticColorScheme *colorScheme =
+ [[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804];
+
+ colorScheme.primaryColor = [UIColor colorWithWhite:(CGFloat)0.9 alpha:0];
+ colorScheme.primaryColorVariant = [UIColor colorWithWhite:(CGFloat)0.8 alpha:(CGFloat)0.1];
+ colorScheme.secondaryColor = [UIColor colorWithWhite:(CGFloat)0.7 alpha:(CGFloat)0.2];
+ colorScheme.errorColor = [UIColor colorWithWhite:(CGFloat)0.6 alpha:(CGFloat)0.3];
+ colorScheme.surfaceColor = [UIColor colorWithWhite:(CGFloat)0.5 alpha:(CGFloat)0.4];
+ colorScheme.backgroundColor = [UIColor colorWithWhite:(CGFloat)0.4 alpha:(CGFloat)0.5];
+ colorScheme.onPrimaryColor = [UIColor colorWithWhite:(CGFloat)0.3 alpha:(CGFloat)0.6];
+ colorScheme.onSecondaryColor = [UIColor colorWithWhite:(CGFloat)0.2 alpha:(CGFloat)0.7];
+ colorScheme.onSurfaceColor = [UIColor colorWithWhite:(CGFloat)0.1 alpha:(CGFloat)0.8];
+ colorScheme.onBackgroundColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.9];
+
+ return colorScheme;
+}
+
+- (MDCTypographyScheme *)customTypographyScheme {
+ MDCTypographyScheme *typographyScheme = [[MDCTypographyScheme alloc] init];
+
+ typographyScheme.headline1 = [UIFont systemFontOfSize:1];
+ typographyScheme.headline2 = [UIFont systemFontOfSize:2];
+ typographyScheme.headline3 = [UIFont systemFontOfSize:3];
+ typographyScheme.headline4 = [UIFont systemFontOfSize:4];
+ typographyScheme.headline5 = [UIFont systemFontOfSize:5];
+ typographyScheme.headline6 = [UIFont systemFontOfSize:6];
+ typographyScheme.subtitle1 = [UIFont systemFontOfSize:7];
+ typographyScheme.subtitle2 = [UIFont systemFontOfSize:8];
+ typographyScheme.body1 = [UIFont systemFontOfSize:9];
+ typographyScheme.body2 = [UIFont systemFontOfSize:10];
+ typographyScheme.caption = [UIFont systemFontOfSize:11];
+ typographyScheme.button = [UIFont systemFontOfSize:12];
+ typographyScheme.overline = [UIFont systemFontOfSize:13];
+
+ return typographyScheme;
+}
+
+- (void)verifyTabBarPrimaryTheming {
+ // Color
+ XCTAssertEqualObjects(self.tabBar.barTintColor, self.colorScheme.primaryColor);
+ UIColor *bottomDividerColor =
+ [self.colorScheme.onPrimaryColor colorWithAlphaComponent:kPrimaryThemeBottomDividerOpacity];
+ XCTAssertEqualObjects(self.tabBar.bottomDividerColor, bottomDividerColor);
+ XCTAssertEqualObjects(self.tabBar.selectionIndicatorStrokeColor, self.colorScheme.onPrimaryColor);
+ XCTAssertEqualObjects([self.tabBar titleColorForState:UIControlStateSelected],
+ self.colorScheme.onPrimaryColor);
+ XCTAssertEqualObjects([self.tabBar imageTintColorForState:UIControlStateSelected],
+ self.colorScheme.onPrimaryColor);
+ UIColor *unselectedTitleColor =
+ [self.colorScheme.onPrimaryColor colorWithAlphaComponent:kPrimaryThemeUnselectedOpacity];
+ UIColor *unselectedImageColor =
+ [self.colorScheme.onPrimaryColor colorWithAlphaComponent:kPrimaryThemeUnselectedOpacity];
+ XCTAssertEqualObjects([self.tabBar titleColorForState:UIControlStateNormal],
+ unselectedTitleColor);
+ XCTAssertEqualObjects([self.tabBar imageTintColorForState:UIControlStateNormal],
+ unselectedImageColor);
+
+ // Typography
+ XCTAssertEqualObjects([self.tabBar titleFontForState:UIControlStateNormal],
+ self.typographyScheme.button);
+ XCTAssertEqualObjects([self.tabBar titleFontForState:UIControlStateSelected],
+ self.typographyScheme.button);
+}
+
+- (void)verifyTabBarSurfaceVariantTheming {
+ // Color
+ XCTAssertEqualObjects(self.tabBar.barTintColor, self.colorScheme.surfaceColor);
+ XCTAssertEqualObjects(self.tabBar.bottomDividerColor, self.colorScheme.onSurfaceColor);
+ XCTAssertEqualObjects(self.tabBar.selectionIndicatorStrokeColor, self.colorScheme.primaryColor);
+ XCTAssertEqualObjects([self.tabBar titleColorForState:UIControlStateSelected],
+ self.colorScheme.primaryColor);
+ XCTAssertEqualObjects([self.tabBar imageTintColorForState:UIControlStateSelected],
+ self.colorScheme.primaryColor);
+ UIColor *unselectedTitleColor =
+ [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kSurfaceThemeUnselectedOpacity];
+ UIColor *unselectedImageColor =
+ [self.colorScheme.onSurfaceColor colorWithAlphaComponent:kSurfaceThemeUnselectedOpacity];
+ XCTAssertEqualObjects([self.tabBar titleColorForState:UIControlStateNormal],
+ unselectedTitleColor);
+ XCTAssertEqualObjects([self.tabBar imageTintColorForState:UIControlStateNormal],
+ unselectedImageColor);
+
+ // Typography
+ XCTAssertEqualObjects([self.tabBar titleFontForState:UIControlStateNormal],
+ self.typographyScheme.button);
+ XCTAssertEqualObjects([self.tabBar titleFontForState:UIControlStateSelected],
+ self.typographyScheme.button);
+}
+
+@end
diff --git a/components/TextControls/examples/supplemental/MDCTextControlTextAreaContentViewController.m b/components/TextControls/examples/supplemental/MDCTextControlTextAreaContentViewController.m
index 63544706acc..84ddc8d8b03 100644
--- a/components/TextControls/examples/supplemental/MDCTextControlTextAreaContentViewController.m
+++ b/components/TextControls/examples/supplemental/MDCTextControlTextAreaContentViewController.m
@@ -27,6 +27,7 @@
#import "MDCOutlinedTextArea+MaterialTheming.h"
@interface MDCTextControlTextAreaContentViewController ()
+@property(nonatomic, assign) BOOL shouldAddDebugBorder;
@end
@implementation MDCTextControlTextAreaContentViewController
@@ -39,6 +40,9 @@ - (MDCFilledTextArea *)createMaterialFilledTextArea {
textArea.labelBehavior = MDCTextControlLabelBehaviorFloats;
textArea.label.text = @"Phone number";
textArea.leadingAssistiveLabel.text = @"This is a string.";
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextArea:textArea];
+ }
[textArea applyThemeWithScheme:self.containerScheme];
return textArea;
}
@@ -48,6 +52,9 @@ - (MDCOutlinedTextArea *)createMaterialOutlinedTextArea {
textArea.textView.delegate = self;
textArea.label.text = @"Phone number";
[textArea applyThemeWithScheme:self.containerScheme];
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextArea:textArea];
+ }
return textArea;
}
@@ -55,6 +62,9 @@ - (MDCBaseTextArea *)createDefaultBaseTextArea {
MDCBaseTextArea *textArea = [[MDCBaseTextArea alloc] init];
textArea.textView.delegate = self;
textArea.label.text = @"This is a floating label";
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextArea:textArea];
+ }
return textArea;
}
@@ -62,11 +72,18 @@ - (void)textViewDidChange:(UITextView *)textView {
[self.view setNeedsLayout];
}
+- (void)addBorderToTextArea:(MDCBaseTextArea *)textArea {
+ textArea.layer.borderColor = UIColor.redColor.CGColor;
+ textArea.layer.borderWidth = 1;
+}
+
#pragma mark Overrides
- (void)initializeScrollViewSubviewsArray {
[super initializeScrollViewSubviewsArray];
+ self.shouldAddDebugBorder = NO;
+
MDCFilledTextArea *filledTextAreaWithoutFloatingLabel = [self createMaterialFilledTextArea];
filledTextAreaWithoutFloatingLabel.labelBehavior = MDCTextControlLabelBehaviorDisappears;
MDCOutlinedTextArea *outlinedTextAreaWithoutFloatingLabel = [self createMaterialOutlinedTextArea];
diff --git a/components/TextControls/examples/supplemental/MDCTextControlTextFieldContentViewController.m b/components/TextControls/examples/supplemental/MDCTextControlTextFieldContentViewController.m
index 3d466c1fe11..30a395dfc81 100644
--- a/components/TextControls/examples/supplemental/MDCTextControlTextFieldContentViewController.m
+++ b/components/TextControls/examples/supplemental/MDCTextControlTextFieldContentViewController.m
@@ -30,7 +30,8 @@
#import "MDCUnderlinedTextField+MaterialTheming.h"
@interface MDCTextControlTextFieldContentViewController ()
-@property(nonatomic, assign) BOOL shouldAddLeadingView;
+@property(nonatomic, assign) BOOL shouldAddDebugLeadingView;
+@property(nonatomic, assign) BOOL shouldAddDebugBorder;
@end
@implementation MDCTextControlTextFieldContentViewController
@@ -44,9 +45,12 @@ - (MDCFilledTextField *)createMaterialFilledTextField {
textField.label.text = @"Phone number";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.leadingAssistiveLabel.text = @"This is a string.";
- if (self.shouldAddLeadingView) {
+ if (self.shouldAddDebugLeadingView) {
[self addLeadingViewToTextField:textField];
}
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextField:textField];
+ }
[textField applyThemeWithScheme:self.containerScheme];
return textField;
}
@@ -57,9 +61,12 @@ - (MDCUnderlinedTextField *)createMaterialUnderlinedTextField {
textField.label.text = @"Phone number";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.leadingAssistiveLabel.text = @"This is a string.";
- if (self.shouldAddLeadingView) {
+ if (self.shouldAddDebugLeadingView) {
[self addLeadingViewToTextField:textField];
}
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextField:textField];
+ }
[textField applyThemeWithScheme:self.containerScheme];
return textField;
}
@@ -69,9 +76,12 @@ - (MDCOutlinedTextField *)createMaterialOutlinedTextField {
textField.placeholder = @"555-555-5555";
textField.label.text = @"Phone number";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
- if (self.shouldAddLeadingView) {
+ if (self.shouldAddDebugLeadingView) {
[self addLeadingViewToTextField:textField];
}
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextField:textField];
+ }
[textField applyThemeWithScheme:self.containerScheme];
return textField;
}
@@ -82,12 +92,20 @@ - (MDCBaseTextField *)createDefaultBaseTextField {
textField.label.text = @"This is a floating label";
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.borderStyle = UITextBorderStyleRoundedRect;
- if (self.shouldAddLeadingView) {
+ if (self.shouldAddDebugLeadingView) {
[self addLeadingViewToTextField:textField];
}
+ if (self.shouldAddDebugBorder) {
+ [self addBorderToTextField:textField];
+ }
return textField;
}
+- (void)addBorderToTextField:(MDCBaseTextField *)textField {
+ textField.layer.borderColor = UIColor.redColor.CGColor;
+ textField.layer.borderWidth = 1;
+}
+
- (void)addLeadingViewToTextField:(MDCBaseTextField *)textField {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
imageView.tintColor = self.containerScheme.colorScheme.primaryColor;
@@ -103,6 +121,9 @@ - (void)addLeadingViewToTextField:(MDCBaseTextField *)textField {
- (void)initializeScrollViewSubviewsArray {
[super initializeScrollViewSubviewsArray];
+ self.shouldAddDebugBorder = NO;
+ self.shouldAddDebugLeadingView = NO;
+
MDCFilledTextField *filledTextFieldWithoutFloatingLabel = [self createMaterialFilledTextField];
filledTextFieldWithoutFloatingLabel.labelBehavior = MDCTextControlLabelBehaviorDisappears;
MDCOutlinedTextField *outlinedTextFieldWithoutFloatingLabel =
diff --git a/components/TextControls/src/BaseTextAreas/MDCBaseTextArea.m b/components/TextControls/src/BaseTextAreas/MDCBaseTextArea.m
index fb434de3a59..e177fc5386f 100644
--- a/components/TextControls/src/BaseTextAreas/MDCBaseTextArea.m
+++ b/components/TextControls/src/BaseTextAreas/MDCBaseTextArea.m
@@ -199,7 +199,16 @@ - (void)postLayoutSubviews {
if (![self validateHeight]) {
[self invalidateIntrinsicContentSize];
}
- [self.textView scrollRangeToVisible:self.textView.selectedRange];
+ [self scrollToVisibleText];
+}
+
+- (void)scrollToVisibleText {
+ // This method was added to address b/161887902, with help from
+ // https://stackoverflow.com/a/49631521
+ NSRange range = NSMakeRange(self.textView.text.length - 1, 1);
+ [self.textView scrollRangeToVisible:range];
+ self.textView.scrollEnabled = NO;
+ self.textView.scrollEnabled = YES;
}
- (MDCBaseTextAreaLayout *)calculateLayoutWithSize:(CGSize)size {
@@ -234,7 +243,8 @@ - (MDCBaseTextAreaLayout *)calculateLayoutWithSize:(CGSize)size {
self.normalFont.leading)
numberOfTextRows:self.numberOfLinesOfVisibleText
density:0
- preferredContainerHeight:self.preferredContainerHeight];
+ preferredContainerHeight:self.preferredContainerHeight
+ isMultilineTextControl:YES];
}
- (id)createHorizontalPositioningReference {
@@ -285,7 +295,7 @@ - (void)layoutGradientLayers {
}
- (CGFloat)numberOfLinesOfText {
- // For more context on measurinig the lines in a UITextView see here:
+ // For more context on measuring the lines in a UITextView see here:
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html
NSLayoutManager *layoutManager = self.textView.layoutManager;
NSUInteger numberOfGlyphs = layoutManager.numberOfGlyphs;
@@ -298,8 +308,10 @@ - (CGFloat)numberOfLinesOfText {
numberOfLines += 1;
}
- if (self.textView.text.length > 0 &&
- [self.textView.text characterAtIndex:self.textView.text.length - 1] == '\n') {
+ BOOL textEndsInNewLine =
+ self.textView.text.length > 0 &&
+ [self.textView.text characterAtIndex:self.textView.text.length - 1] == '\n';
+ if (textEndsInNewLine) {
numberOfLines += 1;
}
return (CGFloat)numberOfLines;
@@ -453,10 +465,18 @@ - (void)applyColorViewModel:(MDCTextControlColorViewModel *)colorViewModel
} else if (labelPosition == MDCTextControlLabelPositionFloating) {
labelColor = colorViewModel.floatingLabelColor;
}
- self.textView.textColor = colorViewModel.textColor;
- self.leadingAssistiveLabel.textColor = colorViewModel.leadingAssistiveLabelColor;
- self.trailingAssistiveLabel.textColor = colorViewModel.trailingAssistiveLabelColor;
- self.label.textColor = labelColor;
+ if (![self.textView.textColor isEqual:colorViewModel.textColor]) {
+ self.textView.textColor = colorViewModel.textColor;
+ }
+ if (![self.leadingAssistiveLabel.textColor isEqual:colorViewModel.leadingAssistiveLabelColor]) {
+ self.leadingAssistiveLabel.textColor = colorViewModel.leadingAssistiveLabelColor;
+ }
+ if (![self.trailingAssistiveLabel.textColor isEqual:colorViewModel.trailingAssistiveLabelColor]) {
+ self.trailingAssistiveLabel.textColor = colorViewModel.trailingAssistiveLabelColor;
+ }
+ if (![self.label.textColor isEqual:labelColor]) {
+ self.label.textColor = labelColor;
+ }
}
- (void)setTextControlColorViewModel:(MDCTextControlColorViewModel *)TextControlColorViewModel
diff --git a/components/TextControls/src/BaseTextFields/MDCBaseTextField.h b/components/TextControls/src/BaseTextFields/MDCBaseTextField.h
index 796ac9375d0..99533f4fb74 100644
--- a/components/TextControls/src/BaseTextFields/MDCBaseTextField.h
+++ b/components/TextControls/src/BaseTextFields/MDCBaseTextField.h
@@ -165,6 +165,16 @@
*/
@property(nullable, nonatomic, strong) NSNumber *trailingEdgePaddingOverride;
+/**
+ This property allows the user to override the default height of the container. The container is the
+ region above the the assistive labels within the text field. If there is no assistive label text,
+ the container's frame will be equal to the frame of the text field itself.
+
+ If this property is set to a value that's smaller than the
+ default height of the container it will be ignored.
+ */
+@property(nonatomic, assign) CGFloat preferredContainerHeight;
+
@end
@interface MDCBaseTextField (UIAccessibility)
diff --git a/components/TextControls/src/BaseTextFields/MDCBaseTextField.m b/components/TextControls/src/BaseTextFields/MDCBaseTextField.m
index 76fe9450360..9d9ea3bc859 100644
--- a/components/TextControls/src/BaseTextFields/MDCBaseTextField.m
+++ b/components/TextControls/src/BaseTextFields/MDCBaseTextField.m
@@ -47,7 +47,6 @@ @implementation MDCBaseTextField
@synthesize containerStyle = _containerStyle;
@synthesize assistiveLabelDrawPriority = _assistiveLabelDrawPriority;
@synthesize customAssistiveLabelDrawPriority = _customAssistiveLabelDrawPriority;
-@synthesize preferredContainerHeight = _preferredContainerHeight;
#pragma mark Object Lifecycle
@@ -258,7 +257,8 @@ - (MDCBaseTextFieldLayout *)calculateLayoutWithTextFieldSize:(CGSize)textFieldSi
textRowHeight:self.normalFont.lineHeight
numberOfTextRows:self.numberOfLinesOfVisibleText
density:0
- preferredContainerHeight:self.preferredContainerHeight];
+ preferredContainerHeight:self.preferredContainerHeight
+ isMultilineTextControl:NO];
}
- (CGFloat)clampedCustomAssistiveLabelDrawPriority:(CGFloat)customPriority {
@@ -622,10 +622,18 @@ - (void)applyColorViewModel:(MDCTextControlColorViewModel *)colorViewModel
} else if (labelPosition == MDCTextControlLabelPositionFloating) {
labelColor = colorViewModel.floatingLabelColor;
}
- self.textColor = colorViewModel.textColor;
- self.leadingAssistiveLabel.textColor = colorViewModel.leadingAssistiveLabelColor;
- self.trailingAssistiveLabel.textColor = colorViewModel.trailingAssistiveLabelColor;
- self.label.textColor = labelColor;
+ if (![self.textColor isEqual:colorViewModel.textColor]) {
+ self.textColor = colorViewModel.textColor;
+ }
+ if (![self.leadingAssistiveLabel.textColor isEqual:colorViewModel.leadingAssistiveLabelColor]) {
+ self.leadingAssistiveLabel.textColor = colorViewModel.leadingAssistiveLabelColor;
+ }
+ if (![self.trailingAssistiveLabel.textColor isEqual:colorViewModel.trailingAssistiveLabelColor]) {
+ self.trailingAssistiveLabel.textColor = colorViewModel.trailingAssistiveLabelColor;
+ }
+ if (![self.label.textColor isEqual:labelColor]) {
+ self.label.textColor = labelColor;
+ }
}
- (void)setTextControlColorViewModel:(MDCTextControlColorViewModel *)colorViewModel
diff --git a/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.h b/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.h
index 5423dd0bf12..ed88c7fdea1 100644
--- a/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.h
+++ b/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.h
@@ -26,6 +26,30 @@ __attribute__((objc_subclassing_restricted)) @interface MDCUnderlinedTextField :
*/
@property(nonatomic, assign) UITextBorderStyle borderStyle NS_UNAVAILABLE;
+/**
+The thickness of the underline that shows in the normal and disabled states. The default is 1.
+*/
+@property(nonatomic, assign) CGFloat normalUnderlineThickness;
+
+/**
+The thickness of the underline that shows in the editing state. The default is 2.
+*/
+@property(nonatomic, assign) CGFloat editingUnderlineThickness;
+
+/**
+Sets the normal underline thickness.
+@param thickness The thickness of the underline.
+@param animated Determines whether or not the change is animated.
+*/
+- (void)setNormalUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated;
+
+/**
+Sets the editing underline thickness.
+@param thickness The thickness of the underline.
+@param animated Determines whether or not the change is animated.
+*/
+- (void)setEditingUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated;
+
/**
Sets the underline color for a given state.
@param underlineColor The UIColor for the given state.
diff --git a/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m b/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m
index c3a3e034410..a5566a05d31 100644
--- a/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m
+++ b/components/TextControls/src/UnderlinedTextFields/MDCUnderlinedTextField.m
@@ -48,11 +48,13 @@ - (void)commonMDCUnderlinedTextFieldInit {
self.containerStyle = [[MDCTextControlStyleUnderlined alloc] init];
}
+#pragma mark MDCTextControlTextField methods
+
- (MDCTextControlTextFieldSideViewAlignment)sideViewAlignment {
return MDCTextControlTextFieldSideViewAlignmentAlignedWithText;
}
-#pragma mark Stateful Color APIs
+#pragma mark Accessors
- (void)setUnderlineColor:(UIColor *)underlineColor forState:(MDCTextControlState)state {
[self.underlinedStyle setUnderlineColor:underlineColor forState:state];
@@ -63,8 +65,28 @@ - (UIColor *)underlineColorForState:(MDCTextControlState)state {
return [self.underlinedStyle underlineColorForState:state];
}
-- (void)layoutSubviews {
- [super layoutSubviews];
+- (void)setNormalUnderlineThickness:(CGFloat)normalUnderlineThickness {
+ self.underlinedStyle.normalUnderlineThickness = normalUnderlineThickness;
+}
+
+- (void)setEditingUnderlineThickness:(CGFloat)editingUnderlineThickness {
+ self.underlinedStyle.editingUnderlineThickness = editingUnderlineThickness;
+}
+
+- (CGFloat)normalUnderlineThickness {
+ return self.underlinedStyle.normalUnderlineThickness;
+}
+
+- (CGFloat)editingUnderlineThickness {
+ return self.underlinedStyle.editingUnderlineThickness;
+}
+
+- (void)setNormalUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated {
+ [self.underlinedStyle setNormalUnderlineThickness:thickness animated:animated];
+}
+
+- (void)setEditingUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated {
+ [self.underlinedStyle setEditingUnderlineThickness:thickness animated:animated];
}
#pragma mark Private Helpers
diff --git a/components/TextControls/tests/snapshot/MDCBaseTextFieldSnapshotTests.m b/components/TextControls/tests/snapshot/MDCBaseTextFieldSnapshotTests.m
index fc4ccb23a23..db067102fdc 100644
--- a/components/TextControls/tests/snapshot/MDCBaseTextFieldSnapshotTests.m
+++ b/components/TextControls/tests/snapshot/MDCBaseTextFieldSnapshotTests.m
@@ -210,4 +210,28 @@ - (void)testTextFieldWithScaledFontsAndXXXLargeContentSize {
[self validateTextField:textField];
}
+- (void)testTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCBaseTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
+- (void)testTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCBaseTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
@end
diff --git a/components/TextControls/tests/snapshot/MDCFilledTextFieldSnapshotTests.m b/components/TextControls/tests/snapshot/MDCFilledTextFieldSnapshotTests.m
index 625c09e329a..0f9661210a9 100644
--- a/components/TextControls/tests/snapshot/MDCFilledTextFieldSnapshotTests.m
+++ b/components/TextControls/tests/snapshot/MDCFilledTextFieldSnapshotTests.m
@@ -16,10 +16,11 @@
#import
-#import "MaterialTextControls+FilledTextFieldsTheming.h"
-#import "MaterialTextControls+OutlinedTextFieldsTheming.h"
#import "supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h"
#import "supplemental/MDCTextControlSnapshotTestHelpers.h"
+#import "MaterialTextControls+FilledTextFields.h"
+#import "MaterialTextControls+FilledTextFieldsTheming.h"
+#import "MaterialTextControls+OutlinedTextFieldsTheming.h"
@interface MDCFilledTextFieldTestsSnapshotTests : MDCSnapshotTestCase
@property(strong, nonatomic) MDCFilledTextField *textField;
@@ -210,4 +211,28 @@ - (void)testTextFieldWithScaledFontsAndXXXLargeContentSize {
[self validateTextField:textField];
}
+- (void)testTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCFilledTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
+- (void)testTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCFilledTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
@end
diff --git a/components/TextControls/tests/snapshot/MDCOutlinedTextFieldSnapshotTests.m b/components/TextControls/tests/snapshot/MDCOutlinedTextFieldSnapshotTests.m
index a938bcd2388..6814c09aa3d 100644
--- a/components/TextControls/tests/snapshot/MDCOutlinedTextFieldSnapshotTests.m
+++ b/components/TextControls/tests/snapshot/MDCOutlinedTextFieldSnapshotTests.m
@@ -16,10 +16,11 @@
#import
-#import "MaterialTextControls+FilledTextFieldsTheming.h"
-#import "MaterialTextControls+OutlinedTextFieldsTheming.h"
#import "supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h"
#import "supplemental/MDCTextControlSnapshotTestHelpers.h"
+#import "MaterialTextControls+FilledTextFieldsTheming.h"
+#import "MaterialTextControls+OutlinedTextFields.h"
+#import "MaterialTextControls+OutlinedTextFieldsTheming.h"
@interface MDCOutlinedTextFieldTestsSnapshotTests : MDCSnapshotTestCase
@property(strong, nonatomic) MDCOutlinedTextField *textField;
@@ -210,4 +211,28 @@ - (void)testTextFieldWithScaledFontsAndXXXLargeContentSize {
[self validateTextField:textField];
}
+- (void)testTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCOutlinedTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
+- (void)testTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCOutlinedTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
@end
diff --git a/components/TextControls/tests/snapshot/MDCUnderlinedTextFieldSnapshotTests.m b/components/TextControls/tests/snapshot/MDCUnderlinedTextFieldSnapshotTests.m
index fa0d82c4c77..5ec9e62d0b6 100644
--- a/components/TextControls/tests/snapshot/MDCUnderlinedTextFieldSnapshotTests.m
+++ b/components/TextControls/tests/snapshot/MDCUnderlinedTextFieldSnapshotTests.m
@@ -18,7 +18,9 @@
#import "supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h"
#import "supplemental/MDCTextControlSnapshotTestHelpers.h"
+
#import "MaterialTextControls+FilledTextFieldsTheming.h"
+#import "MaterialTextControls+UnderlinedTextFields.h"
#import "MaterialTextControls+UnderlinedTextFieldsTheming.h"
@interface MDCUnderlinedTextFieldTestsSnapshotTests : MDCSnapshotTestCase
@@ -210,4 +212,28 @@ - (void)testTextFieldWithScaledFontsAndXXXLargeContentSize {
[self validateTextField:textField];
}
+- (void)testTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCUnderlinedTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
+- (void)testTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing {
+ // Given
+ MDCUnderlinedTextField *textField = self.textField;
+
+ // When
+ [MDCBaseTextFieldTestsSnapshotTestHelpers
+ configureTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing:textField];
+
+ // Then
+ [self validateTextField:textField];
+}
+
@end
diff --git a/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h b/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h
index 6a74fbca727..3cc5fed41ba 100644
--- a/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h
+++ b/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.h
@@ -41,5 +41,9 @@
+ (void)configureTextFieldWithColoredAssistiveLabelTextWhileEditing:(MDCBaseTextField *)textField;
+ (void)configureTextFieldWithColoredAssistiveLabelTextWhileDisabled:(MDCBaseTextField *)textField;
+ (void)configureTextFieldWithScaledFontsAndAXXXLargeContentSize:(MDCBaseTextField *)textField;
++ (void)configureTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing:
+ (MDCBaseTextField *)textField;
++ (void)configureTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing:
+ (MDCBaseTextField *)textField;
@end
diff --git a/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.m b/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.m
index 96fb122a055..5e68e5c85b3 100644
--- a/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.m
+++ b/components/TextControls/tests/snapshot/supplemental/MDCBaseTextFieldTestsSnapshotTestHelpers.m
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "MaterialSnapshot.h"
+#import "MDCBaseTextFieldTestsSnapshotTestHelpers.h"
#import
-#import "MDCBaseTextFieldTestsSnapshotTestHelpers.h"
+#import "MaterialTextControls+BaseTextFields.h"
#import "MDCTextControlSnapshotTestHelpers.h"
+#import "MaterialSnapshot.h"
@interface MDCBaseTextField (AnimationDuration)
@property(nonatomic, assign) NSTimeInterval animationDuration;
@@ -192,6 +193,21 @@ + (void)configureTextFieldWithScaledFontsAndAXXXLargeContentSize:(MDCBaseTextFie
[textField becomeFirstResponder];
}
++ (void)configureTextFieldWithTextAndLabelTextAndPreferredContainerHeightWhileEditing:
+ (MDCBaseTextField *)textField {
+ textField.text = @"Some text";
+ textField.label.text = @"Label text";
+ textField.preferredContainerHeight = 100;
+ [textField becomeFirstResponder];
+}
+
++ (void)configureTextFieldWithTextAndNoLabelTextAndPreferredContainerHeightWhileEditing:
+ (MDCBaseTextField *)textField {
+ textField.text = @"Some text";
+ textField.preferredContainerHeight = 100;
+ [textField becomeFirstResponder];
+}
+
#pragma mark Helpers
+ (UIView *)createBlueSideView {
diff --git a/components/TextFields/README.md b/components/TextFields/README.md
index 52e35a8fb4f..7e6169f1000 100644
--- a/components/TextFields/README.md
+++ b/components/TextFields/README.md
@@ -18,31 +18,30 @@ For more information on text field styles, and animated images of each style in
## Table of contents
diff --git a/components/Typography/README.md b/components/Typography/README.md
index a5a24c51c72..ddd94d06cee 100644
--- a/components/Typography/README.md
+++ b/components/Typography/README.md
@@ -19,8 +19,8 @@ from the Material Design specifications.
## Installation
diff --git a/components/Typography/docs/README.md b/components/Typography/docs/README.md
index 12f7b0f8b68..17d355de294 100644
--- a/components/Typography/docs/README.md
+++ b/components/Typography/docs/README.md
@@ -17,8 +17,8 @@ from the Material Design specifications.
## Installation
diff --git a/components/private/Snapshot/src/MaterialSnapshot.h b/components/private/Snapshot/src/MaterialSnapshot.h
index 0fb81c98d11..ba5f95a6749 100644
--- a/components/private/Snapshot/src/MaterialSnapshot.h
+++ b/components/private/Snapshot/src/MaterialSnapshot.h
@@ -12,6 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "SnapshotTestCase/MDCSnapshotTestCase.h"
-#import "SnapshotUtilities/UIImage+MDCSnapshot.h"
-#import "SnapshotUtilities/UIView+MDCSnapshot.h"
+#import "SnapshotTestCase/MDCSnapshotTestCase.h" // IWYU pragma: export
+#import "SnapshotUtilities/UIImage+MDCSnapshot.h" // IWYU pragma: export
+#import "SnapshotUtilities/UIView+MDCSnapshot.h" // IWYU pragma: export
diff --git a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m
index 4d42d49ebd6..c9e7ccfecbf 100644
--- a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m
+++ b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlStyleBase.m
@@ -43,14 +43,16 @@ - (void)removeStyleFrom:(id)textControl {
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
return [[MDCTextControlVerticalPositioningReferenceBase alloc]
initWithFloatingFontLineHeight:floatingLabelHeight
normalFontLineHeight:normalFontLineHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
- (MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
diff --git a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.h b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.h
index a2b4b711cd0..4176095ee27 100644
--- a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.h
+++ b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.h
@@ -24,6 +24,7 @@
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl;
@end
diff --git a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.m b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.m
index 76f8af23bb1..0ddded57e98 100644
--- a/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.m
+++ b/components/private/TextControlsPrivate/src/BaseStyle/MDCTextControlVerticalPositioningReferenceBase.m
@@ -16,8 +16,8 @@
#import "MaterialMath.h"
#import "MDCTextControlVerticalPositioningReference.h"
-static const CGFloat kMinPaddingAroundTextWhenNoLabel = 6.0f;
-static const CGFloat kMaxPaddingAroundTextWhenNoLabel = 10.0f;
+static const CGFloat kMinPaddingAroundTextWhenNoFloatingLabel = 6.0f;
+static const CGFloat kMaxPaddingAroundTextWhenNoFloatingLabel = 10.0f;
static const CGFloat kMinPaddingBetweenContainerTopAndFloatingLabel = (CGFloat)6.0;
static const CGFloat kMaxPaddingBetweenContainerTopAndFloatingLabel = (CGFloat)10.0;
static const CGFloat kMinPaddingBetweenFloatingLabelAndEditingText = (CGFloat)3.0;
@@ -51,7 +51,8 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
self = [super init];
if (self) {
[self calculatePaddingValuesWithFoatingFontLineHeight:floatingLabelHeight
@@ -59,7 +60,8 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
return self;
}
@@ -69,13 +71,10 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
- BOOL isMultiline = numberOfTextRows > 1 || numberOfTextRows == 0;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(CGFloat)isMultilineTextControl {
CGFloat clampedDensity = MDCTextControlClampDensity(density);
- _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
- kMinPaddingAroundTextWhenNoLabel, kMaxPaddingAroundTextWhenNoLabel, clampedDensity);
-
_paddingBetweenContainerTopAndFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
kMinPaddingBetweenContainerTopAndFloatingLabel,
kMaxPaddingBetweenContainerTopAndFloatingLabel, clampedDensity);
@@ -90,78 +89,72 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
_paddingAboveAssistiveLabels = MDCTextControlPaddingValueWithMinimumPadding(
kMinPaddingAboveAssistiveLabels, kMaxPaddingAboveAssistiveLabels, clampedDensity);
-
_paddingBelowAssistiveLabels = MDCTextControlPaddingValueWithMinimumPadding(
kMinPaddingBelowAssistiveLabels, kMaxPaddingBelowAssistiveLabels, clampedDensity);
- // The container height below is the "default" container height, given the density. This height
- // will be used if the client has not specified a preferredContainerHeight.
- CGFloat containerHeightWithPaddingsDeterminedByDensity =
+ CGFloat defaultContainerHeightForFloatingLabel =
MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
floatingLabelHeight, textRowHeight, numberOfTextRows,
_paddingBetweenContainerTopAndFloatingLabel, _paddingBetweenFloatingLabelAndEditingText,
_paddingBetweenEditingTextAndContainerBottom);
- BOOL clientHasSpecifiedValidPreferredContainerHeight =
- preferredContainerHeight > containerHeightWithPaddingsDeterminedByDensity;
- if (clientHasSpecifiedValidPreferredContainerHeight && !isMultiline) {
- // modify the previously computed padding values so that they ultimately result in a container
- // with the preferred container height.
- CGFloat difference = preferredContainerHeight - containerHeightWithPaddingsDeterminedByDensity;
- CGFloat sumOfPaddingValues = _paddingBetweenContainerTopAndFloatingLabel +
- _paddingBetweenFloatingLabelAndEditingText +
- _paddingBetweenEditingTextAndContainerBottom;
- _paddingBetweenContainerTopAndFloatingLabel =
- _paddingBetweenContainerTopAndFloatingLabel +
- ((_paddingBetweenContainerTopAndFloatingLabel / sumOfPaddingValues) * difference);
- _paddingBetweenFloatingLabelAndEditingText =
- _paddingBetweenFloatingLabelAndEditingText +
- ((_paddingBetweenFloatingLabelAndEditingText / sumOfPaddingValues) * difference);
- _paddingBetweenEditingTextAndContainerBottom =
- _paddingBetweenEditingTextAndContainerBottom +
- ((_paddingBetweenEditingTextAndContainerBottom / sumOfPaddingValues) * difference);
+ BOOL preferredContainerHeightIsValidForFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForFloatingLabel;
+ if (preferredContainerHeightIsValidForFloatingLabel) {
+ _containerHeightWithFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat difference = preferredContainerHeight - defaultContainerHeightForFloatingLabel;
+ CGFloat sumOfPaddingValues = _paddingBetweenContainerTopAndFloatingLabel +
+ _paddingBetweenFloatingLabelAndEditingText +
+ _paddingBetweenEditingTextAndContainerBottom;
+ _paddingBetweenContainerTopAndFloatingLabel =
+ _paddingBetweenContainerTopAndFloatingLabel +
+ ((_paddingBetweenContainerTopAndFloatingLabel / sumOfPaddingValues) * difference);
+ _paddingBetweenFloatingLabelAndEditingText =
+ _paddingBetweenFloatingLabelAndEditingText +
+ ((_paddingBetweenFloatingLabelAndEditingText / sumOfPaddingValues) * difference);
+ _paddingBetweenEditingTextAndContainerBottom =
+ _paddingBetweenEditingTextAndContainerBottom +
+ ((_paddingBetweenEditingTextAndContainerBottom / sumOfPaddingValues) * difference);
+ }
+ } else {
+ _containerHeightWithFloatingLabel = defaultContainerHeightForFloatingLabel;
}
- if (clientHasSpecifiedValidPreferredContainerHeight) {
- _containerHeightWithFloatingLabel = preferredContainerHeight;
+ _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingAroundTextWhenNoFloatingLabel, kMaxPaddingAroundTextWhenNoFloatingLabel,
+ clampedDensity);
+
+ CGFloat defaultContainerHeightForNoFloatingLabel =
+ MDCTextControlCalculateContainerHeightWhenNoFloatingLabelWithTextRowHeight(
+ textRowHeight, numberOfTextRows, _paddingAroundTextWhenNoFloatingLabel);
+ BOOL preferredContainerHeightIsValidForNoFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForNoFloatingLabel;
+ if (preferredContainerHeightIsValidForNoFloatingLabel) {
+ _containerHeightWithoutFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat difference = preferredContainerHeight - defaultContainerHeightForNoFloatingLabel;
+ CGFloat sumOfPaddingValues = _paddingAroundTextWhenNoFloatingLabel * 2.0f;
+ _paddingAroundTextWhenNoFloatingLabel =
+ _paddingAroundTextWhenNoFloatingLabel +
+ ((_paddingAroundTextWhenNoFloatingLabel / sumOfPaddingValues) * difference);
+ }
} else {
- _containerHeightWithFloatingLabel = containerHeightWithPaddingsDeterminedByDensity;
+ _containerHeightWithoutFloatingLabel = defaultContainerHeightForNoFloatingLabel;
}
CGFloat halfOfNormalFontLineHeight = (CGFloat)0.5 * normalFontLineHeight;
- if (isMultiline) {
- // For multiline text controls the normal label (i.e. the label when it's not floating) should
- // be positioned where it would be positioned if it were single-line.
+ if (isMultilineTextControl) {
CGFloat heightWithOneRow = MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
floatingLabelHeight, textRowHeight, 1, _paddingBetweenContainerTopAndFloatingLabel,
_paddingBetweenFloatingLabelAndEditingText, _paddingBetweenEditingTextAndContainerBottom);
CGFloat halfOfHeightWithOneRow = (CGFloat)0.5 * heightWithOneRow;
_paddingBetweenContainerTopAndNormalLabel = halfOfHeightWithOneRow - halfOfNormalFontLineHeight;
} else {
- // For single-line text controls the normal label (i.e. the label when it's not floating) should
- // be vertically centered.
CGFloat halfOfContainerHeight = (CGFloat)0.5 * _containerHeightWithFloatingLabel;
_paddingBetweenContainerTopAndNormalLabel = halfOfContainerHeight - halfOfNormalFontLineHeight;
}
-
- CGFloat containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity =
- MDCTextControlCalculateContainerHeightWhenNoFloatingLabelWithTextRowHeight(
- textRowHeight, numberOfTextRows, _paddingAroundTextWhenNoFloatingLabel);
- BOOL clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel =
- preferredContainerHeight > containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity;
- if (clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel && !isMultiline) {
- CGFloat difference = preferredContainerHeight - containerHeightWithPaddingsDeterminedByDensity;
- CGFloat sumOfPaddingValues = _paddingAroundTextWhenNoFloatingLabel * 2.0f;
- _paddingAroundTextWhenNoFloatingLabel =
- _paddingAroundTextWhenNoFloatingLabel +
- ((_paddingAroundTextWhenNoFloatingLabel / sumOfPaddingValues) * difference);
- }
-
- if (clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel) {
- _containerHeightWithoutFloatingLabel = preferredContainerHeight;
- } else {
- _containerHeightWithoutFloatingLabel =
- containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity;
- }
}
- (CGFloat)paddingBetweenContainerTopAndFloatingLabel {
diff --git a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m
index 8902d73f678..cb0efb1fe7a 100644
--- a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m
+++ b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlStyleFilled.m
@@ -102,14 +102,16 @@ - (void)removeStyleFrom:(id)textControl {
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
return [[MDCTextControlVerticalPositioningReferenceFilled alloc]
initWithFloatingFontLineHeight:floatingLabelHeight
normalFontLineHeight:normalFontLineHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
- (nonnull MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
diff --git a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.h b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.h
index 1e2185dda38..986d06b9a85 100644
--- a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.h
+++ b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.h
@@ -19,14 +19,14 @@
#import "MDCTextControlVerticalPositioningReferenceUnderlined.h"
@interface MDCTextControlVerticalPositioningReferenceFilled
- : MDCTextControlVerticalPositioningReferenceUnderlined <
- MDCTextControlVerticalPositioningReference>
+ : NSObject
- (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
normalFontLineHeight:(CGFloat)normalFontLineHeight
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl;
@end
diff --git a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.m b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.m
index 91966e7c3f8..d24b311b23c 100644
--- a/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.m
+++ b/components/private/TextControlsPrivate/src/FilledStyle/MDCTextControlVerticalPositioningReferenceFilled.m
@@ -15,44 +15,126 @@
#import "MDCTextControlVerticalPositioningReferenceFilled.h"
#import "MDCTextControlVerticalPositioningReference.h"
+static const CGFloat kMinPaddingAroundTextWhenNoFloatingLabel = 6.0f;
+static const CGFloat kMaxPaddingAroundTextWhenNoFloatingLabel = 10.0f;
+static const CGFloat kMinPaddingBetweenContainerTopAndFloatingLabel = 6.0f;
+static const CGFloat kMaxPaddingBetweenContainerTopAndFloatingLabel = 10.0f;
+static const CGFloat kMinPaddingBetweenFloatingLabelAndEditingText = 3.0f;
+static const CGFloat kMaxPaddingBetweenFloatingLabelAndEditingText = 6.0f;
+static const CGFloat kMinPaddingBetweenEditingTextAndContainerBottom = 6.0f;
+static const CGFloat kMaxPaddingBetweenEditingTextAndContainerBottom = 10.0f;
+static const CGFloat kMinPaddingAroundAssistiveLabels = 3.0f;
+static const CGFloat kMaxPaddingAroundAssistiveLabels = 6.0f;
+
/**
For slightly more context on what this class is doing look at
MDCTextControlVerticalPositioningReferenceBase. It's very similar and has some comments. Maybe at
some point all the positioning references should be refactored to share a superclass, because
there's currently a lot of duplicated code among the three of them.
*/
-@interface MDCTextControlVerticalPositioningReferenceFilled ()
-@end
-
@implementation MDCTextControlVerticalPositioningReferenceFilled
+@synthesize paddingBetweenContainerTopAndFloatingLabel =
+ _paddingBetweenContainerTopAndFloatingLabel;
@synthesize paddingBetweenContainerTopAndNormalLabel = _paddingBetweenContainerTopAndNormalLabel;
+@synthesize paddingBetweenFloatingLabelAndEditingText = _paddingBetweenFloatingLabelAndEditingText;
+@synthesize paddingBetweenEditingTextAndContainerBottom =
+ _paddingBetweenEditingTextAndContainerBottom;
+@synthesize paddingAboveAssistiveLabels = _paddingAboveAssistiveLabels;
+@synthesize paddingBelowAssistiveLabels = _paddingBelowAssistiveLabels;
+@synthesize containerHeightWithFloatingLabel = _containerHeightWithFloatingLabel;
+@synthesize containerHeightWithoutFloatingLabel = _containerHeightWithoutFloatingLabel;
+@synthesize paddingAroundTextWhenNoFloatingLabel = _paddingAroundTextWhenNoFloatingLabel;
- (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
normalFontLineHeight:(CGFloat)normalFontLineHeight
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
- self = [super initWithFloatingFontLineHeight:floatingLabelHeight
- normalFontLineHeight:normalFontLineHeight
- textRowHeight:textRowHeight
- numberOfTextRows:numberOfTextRows
- density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
+ self = [super init];
if (self) {
- BOOL isMultiline = numberOfTextRows > 1 || numberOfTextRows == 0;
+ CGFloat clampedDensity = MDCTextControlClampDensity(density);
+
+ _paddingBetweenContainerTopAndFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingBetweenContainerTopAndFloatingLabel,
+ kMaxPaddingBetweenContainerTopAndFloatingLabel, clampedDensity);
+
+ _paddingBetweenFloatingLabelAndEditingText = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingBetweenFloatingLabelAndEditingText,
+ kMaxPaddingBetweenFloatingLabelAndEditingText, clampedDensity);
+
+ _paddingBetweenEditingTextAndContainerBottom = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingBetweenEditingTextAndContainerBottom,
+ kMaxPaddingBetweenEditingTextAndContainerBottom, clampedDensity);
+
+ _paddingAboveAssistiveLabels = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingAroundAssistiveLabels, kMaxPaddingAroundAssistiveLabels, clampedDensity);
+ _paddingBelowAssistiveLabels = _paddingAboveAssistiveLabels;
+
+ CGFloat defaultContainerHeightForFloatingLabel =
+ MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
+ floatingLabelHeight, textRowHeight, numberOfTextRows,
+ _paddingBetweenContainerTopAndFloatingLabel, _paddingBetweenFloatingLabelAndEditingText,
+ _paddingBetweenEditingTextAndContainerBottom);
+ BOOL preferredContainerHeightIsValidForFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForFloatingLabel;
+ if (preferredContainerHeightIsValidForFloatingLabel) {
+ _containerHeightWithFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat difference = preferredContainerHeight - defaultContainerHeightForFloatingLabel;
+ CGFloat sumOfPaddingValues = _paddingBetweenContainerTopAndFloatingLabel +
+ _paddingBetweenFloatingLabelAndEditingText +
+ _paddingBetweenEditingTextAndContainerBottom;
+ _paddingBetweenContainerTopAndFloatingLabel =
+ _paddingBetweenContainerTopAndFloatingLabel +
+ ((_paddingBetweenContainerTopAndFloatingLabel / sumOfPaddingValues) * difference);
+ _paddingBetweenFloatingLabelAndEditingText =
+ _paddingBetweenFloatingLabelAndEditingText +
+ ((_paddingBetweenFloatingLabelAndEditingText / sumOfPaddingValues) * difference);
+ _paddingBetweenEditingTextAndContainerBottom =
+ _paddingBetweenEditingTextAndContainerBottom +
+ ((_paddingBetweenEditingTextAndContainerBottom / sumOfPaddingValues) * difference);
+ }
+ } else {
+ _containerHeightWithFloatingLabel = defaultContainerHeightForFloatingLabel;
+ }
+
+ _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingAroundTextWhenNoFloatingLabel, kMaxPaddingAroundTextWhenNoFloatingLabel,
+ clampedDensity);
+
+ CGFloat defaultContainerHeightForNoFloatingLabel =
+ MDCTextControlCalculateContainerHeightWhenNoFloatingLabelWithTextRowHeight(
+ textRowHeight, numberOfTextRows, _paddingAroundTextWhenNoFloatingLabel);
+ BOOL preferredContainerHeightIsValidForNoFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForNoFloatingLabel;
+ if (preferredContainerHeightIsValidForNoFloatingLabel) {
+ _containerHeightWithoutFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat difference = preferredContainerHeight - defaultContainerHeightForNoFloatingLabel;
+ CGFloat sumOfPaddingValues = _paddingAroundTextWhenNoFloatingLabel * 2.0f;
+ _paddingAroundTextWhenNoFloatingLabel =
+ _paddingAroundTextWhenNoFloatingLabel +
+ ((_paddingAroundTextWhenNoFloatingLabel / sumOfPaddingValues) * difference);
+ }
+ } else {
+ _containerHeightWithoutFloatingLabel = defaultContainerHeightForNoFloatingLabel;
+ }
+
CGFloat halfOfNormalFontLineHeight = (CGFloat)0.5 * normalFontLineHeight;
- if (isMultiline) {
+ if (isMultilineTextControl) {
CGFloat heightWithOneRow = MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
- floatingLabelHeight, textRowHeight, 1, self.paddingBetweenContainerTopAndFloatingLabel,
- self.paddingBetweenFloatingLabelAndEditingText,
- self.paddingBetweenEditingTextAndContainerBottom);
+ floatingLabelHeight, textRowHeight, 1, _paddingBetweenContainerTopAndFloatingLabel,
+ _paddingBetweenFloatingLabelAndEditingText, _paddingBetweenEditingTextAndContainerBottom);
CGFloat halfOfHeightWithOneRow = (CGFloat)0.5 * heightWithOneRow;
_paddingBetweenContainerTopAndNormalLabel =
halfOfHeightWithOneRow - halfOfNormalFontLineHeight;
} else {
- CGFloat halfOfContainerHeight = (CGFloat)0.5 * self.containerHeightWithFloatingLabel;
+ CGFloat halfOfContainerHeight = (CGFloat)0.5 * _containerHeightWithFloatingLabel;
_paddingBetweenContainerTopAndNormalLabel =
halfOfContainerHeight - halfOfNormalFontLineHeight;
}
@@ -60,8 +142,40 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
return self;
}
+- (CGFloat)paddingBetweenContainerTopAndFloatingLabel {
+ return _paddingBetweenContainerTopAndFloatingLabel;
+}
+
- (CGFloat)paddingBetweenContainerTopAndNormalLabel {
return _paddingBetweenContainerTopAndNormalLabel;
}
+- (CGFloat)paddingBetweenFloatingLabelAndEditingText {
+ return _paddingBetweenFloatingLabelAndEditingText;
+}
+
+- (CGFloat)paddingBetweenEditingTextAndContainerBottom {
+ return _paddingBetweenEditingTextAndContainerBottom;
+}
+
+- (CGFloat)paddingAboveAssistiveLabels {
+ return _paddingAboveAssistiveLabels;
+}
+
+- (CGFloat)paddingBelowAssistiveLabels {
+ return _paddingBelowAssistiveLabels;
+}
+
+- (CGFloat)containerHeightWithFloatingLabel {
+ return _containerHeightWithFloatingLabel;
+}
+
+- (CGFloat)containerHeightWithoutFloatingLabel {
+ return _containerHeightWithoutFloatingLabel;
+}
+
+- (CGFloat)paddingAroundTextWhenNoFloatingLabel {
+ return _paddingAroundTextWhenNoFloatingLabel;
+}
+
@end
diff --git a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m
index e8d488c1f2a..29bc68d37d3 100644
--- a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m
+++ b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlStyleOutlined.m
@@ -122,14 +122,16 @@ - (void)removeStyleFrom:(id)TextControl {
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
return [[MDCTextControlVerticalPositioningReferenceOutlined alloc]
initWithFloatingFontLineHeight:floatingLabelHeight
normalFontLineHeight:normalFontLineHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
- (MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
diff --git a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.h b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.h
index cff33a4f1a2..31c6a48f371 100644
--- a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.h
+++ b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.h
@@ -25,6 +25,7 @@
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl;
@end
diff --git a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.m b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.m
index afbeb7e6c89..0504659cf11 100644
--- a/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.m
+++ b/components/private/TextControlsPrivate/src/OutlinedStyle/MDCTextControlVerticalPositioningReferenceOutlined.m
@@ -20,8 +20,8 @@
achieve the look and feel of the textfields at
https://material.io/design/components/text-fields.html.
*/
-static const CGFloat kMinPaddingAroundTextWhenNoLabel = 6.0f;
-static const CGFloat kMaxPaddingAroundTextWhenNoLabel = 10.0f;
+static const CGFloat kMinPaddingAroundTextWhenNoFloatingLabel = 6.0f;
+static const CGFloat kMaxPaddingAroundTextWhenNoFloatingLabel = 10.0f;
static const CGFloat kMinPaddingBetweenFloatingLabelAndEditingText = (CGFloat)8.0;
static const CGFloat kMaxPaddingBetweenFloatingLabelAndEditingText = (CGFloat)12.0;
static const CGFloat kMinPaddingAroundAssistiveLabels = (CGFloat)3.0;
@@ -32,11 +32,11 @@ @interface MDCTextControlVerticalPositioningReferenceOutlined ()
@end
/**
- For slightly more context on what this class is doing look at
- MDCTextControlVerticalPositioningReferenceBase. It's very similar and has some comments. Maybe at
- some point all the positioning references should be refactored to share a superclass, because
- there's currently a lot of duplicated code among the three of them.
- */
+ For slightly more context on what this class is doing look at
+ MDCTextControlVerticalPositioningReferenceBase. It's very similar and has some comments. Maybe at
+ some point all the positioning references should be refactored to share a superclass, because
+ there's currently a lot of duplicated code among the three of them.
+*/
@implementation MDCTextControlVerticalPositioningReferenceOutlined
@synthesize paddingBetweenContainerTopAndFloatingLabel =
_paddingBetweenContainerTopAndFloatingLabel;
@@ -53,7 +53,8 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
self = [super init];
if (self) {
[self calculatePaddingValuesWithFoatingFontLineHeight:floatingLabelHeight
@@ -61,7 +62,8 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
return self;
}
@@ -71,11 +73,10 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
- BOOL isMultiline = numberOfTextRows > 1 || numberOfTextRows == 0;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
CGFloat clampedDensity = MDCTextControlClampDensity(density);
CGFloat halfOfFloatingLabelHeight = ((CGFloat)0.5 * floatingLabelHeight);
-
_paddingBetweenContainerTopAndFloatingLabel = (CGFloat)0 - halfOfFloatingLabelHeight;
_paddingBetweenFloatingLabelAndEditingText = MDCTextControlPaddingValueWithMinimumPadding(
@@ -89,37 +90,41 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
_paddingAroundAssistiveLabels = MDCTextControlPaddingValueWithMinimumPadding(
kMinPaddingAroundAssistiveLabels, kMaxPaddingAroundAssistiveLabels, clampedDensity);
- CGFloat containerHeightWithPaddingsDeterminedByDensity =
+ CGFloat defaultContainerHeightForFloatingLabel =
MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
floatingLabelHeight, textRowHeight, numberOfTextRows,
_paddingBetweenContainerTopAndFloatingLabel, _paddingBetweenFloatingLabelAndEditingText,
_paddingBetweenEditingTextAndContainerBottom);
- BOOL clientHasSpecifiedValidPreferredContainerHeight =
- preferredContainerHeight > containerHeightWithPaddingsDeterminedByDensity;
- if (clientHasSpecifiedValidPreferredContainerHeight && !isMultiline) {
- CGFloat difference = preferredContainerHeight - containerHeightWithPaddingsDeterminedByDensity;
- CGFloat sumOfPaddingValues = _paddingBetweenContainerTopAndFloatingLabel +
- _paddingBetweenFloatingLabelAndEditingText +
- _paddingBetweenEditingTextAndContainerBottom;
- _paddingBetweenContainerTopAndFloatingLabel =
- _paddingBetweenContainerTopAndFloatingLabel +
- ((_paddingBetweenContainerTopAndFloatingLabel / sumOfPaddingValues) * difference);
- _paddingBetweenFloatingLabelAndEditingText =
- _paddingBetweenFloatingLabelAndEditingText +
- ((_paddingBetweenFloatingLabelAndEditingText / sumOfPaddingValues) * difference);
- _paddingBetweenEditingTextAndContainerBottom =
- _paddingBetweenEditingTextAndContainerBottom +
- ((_paddingBetweenEditingTextAndContainerBottom / sumOfPaddingValues) * difference);
- }
-
- if (clientHasSpecifiedValidPreferredContainerHeight) {
+ BOOL preferredContainerHeightIsValidForFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForFloatingLabel;
+ if (preferredContainerHeightIsValidForFloatingLabel) {
_containerHeightWithFloatingLabel = preferredContainerHeight;
} else {
- _containerHeightWithFloatingLabel = containerHeightWithPaddingsDeterminedByDensity;
+ _containerHeightWithFloatingLabel = defaultContainerHeightForFloatingLabel;
}
+ _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingAroundTextWhenNoFloatingLabel, kMaxPaddingAroundTextWhenNoFloatingLabel,
+ clampedDensity);
+
+ CGFloat defaultContainerHeightForNoFloatingLabel =
+ MDCTextControlCalculateContainerHeightWhenNoFloatingLabelWithTextRowHeight(
+ textRowHeight, numberOfTextRows, _paddingAroundTextWhenNoFloatingLabel);
+ BOOL preferredContainerHeightIsValidForNoFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForNoFloatingLabel;
CGFloat halfOfNormalFontLineHeight = (CGFloat)0.5 * normalFontLineHeight;
- if (isMultiline) {
+ if (preferredContainerHeightIsValidForNoFloatingLabel) {
+ _containerHeightWithoutFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat halfOfContainerHeight = (CGFloat)0.5 * _containerHeightWithoutFloatingLabel;
+ _paddingAroundTextWhenNoFloatingLabel = halfOfContainerHeight - halfOfNormalFontLineHeight;
+ }
+ } else {
+ _containerHeightWithoutFloatingLabel = defaultContainerHeightForNoFloatingLabel;
+ }
+
+ if (isMultilineTextControl) {
CGFloat heightWithOneRow = MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
floatingLabelHeight, textRowHeight, 1, _paddingBetweenContainerTopAndFloatingLabel,
_paddingBetweenFloatingLabelAndEditingText, _paddingBetweenEditingTextAndContainerBottom);
@@ -128,29 +133,8 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
} else {
CGFloat halfOfContainerHeight = (CGFloat)0.5 * _containerHeightWithFloatingLabel;
_paddingBetweenContainerTopAndNormalLabel = halfOfContainerHeight - halfOfNormalFontLineHeight;
- }
-
- _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
- kMinPaddingAroundTextWhenNoLabel, kMaxPaddingAroundTextWhenNoLabel, clampedDensity);
-
- CGFloat containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity =
- MDCTextControlCalculateContainerHeightWhenNoFloatingLabelWithTextRowHeight(
- textRowHeight, numberOfTextRows, _paddingAroundTextWhenNoFloatingLabel);
- BOOL clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel =
- preferredContainerHeight > containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity;
- if (clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel && !isMultiline) {
- CGFloat difference = preferredContainerHeight - containerHeightWithPaddingsDeterminedByDensity;
- CGFloat sumOfPaddingValues = _paddingAroundTextWhenNoFloatingLabel * 2.0f;
- _paddingAroundTextWhenNoFloatingLabel =
- _paddingAroundTextWhenNoFloatingLabel +
- ((_paddingAroundTextWhenNoFloatingLabel / sumOfPaddingValues) * difference);
- }
-
- if (clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel) {
- _containerHeightWithoutFloatingLabel = preferredContainerHeight;
- } else {
- _containerHeightWithoutFloatingLabel =
- containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity;
+ _paddingBetweenFloatingLabelAndEditingText =
+ _paddingBetweenContainerTopAndNormalLabel - halfOfFloatingLabelHeight;
}
}
diff --git a/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h b/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h
index 09d79b15517..188d812f36c 100644
--- a/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h
+++ b/components/private/TextControlsPrivate/src/Shared/MDCTextControl.h
@@ -182,7 +182,8 @@ FOUNDATION_EXTERN const CGFloat kMDCTextControlDefaultAnimationDuration;
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl;
/**
This method returns an object that tells the view where to position its views
diff --git a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.h b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.h
index a6da0fd9705..c21b5266654 100644
--- a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.h
+++ b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.h
@@ -21,6 +21,30 @@ This style object is used by MDCTextControls adopting the Material Underlined st
*/
@interface MDCTextControlStyleUnderlined : NSObject
+/**
+The thickness of the underline that shows in the normal and disabled states.
+*/
+@property(nonatomic, assign) CGFloat normalUnderlineThickness;
+
+/**
+The thickness of the underline that shows in the editing state.
+*/
+@property(nonatomic, assign) CGFloat editingUnderlineThickness;
+
+/**
+Sets the normal underline thickness.
+@param thickness The thickness of the underline.
+@param animated Determines whether or not the change is animated.
+*/
+- (void)setNormalUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated;
+
+/**
+Sets the editing underline thickness.
+@param thickness The thickness of the underline.
+@param animated Determines whether or not the change is animated.
+*/
+- (void)setEditingUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated;
+
/**
Sets the underline color color for a given state.
@param underlineColor The UIColor for the given state.
diff --git a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m
index f5d2f18bf2b..fdaea78bfc4 100644
--- a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m
+++ b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlStyleUnderlined.m
@@ -17,29 +17,36 @@
#import
#include "MaterialAvailability.h"
+#import "MDCTextControlState.h"
#import "MDCTextControl.h"
#import "UIBezierPath+MDCTextControlStyle.h"
#import "MDCTextControlVerticalPositioningReferenceUnderlined.h"
-static const CGFloat kUnderlinedContainerStyleUnderlineWidthThin = 1.0f;
-static const CGFloat kUnderlinedContainerStyleUnderlineWidthThick = 2.0f;
+static const CGFloat kUnderlinedContainerStyleUnderlineThicknessNormal = 1.0f;
+static const CGFloat kUnderlinedContainerStyleUnderlineThicknessEditing = 2.0f;
static const CGFloat kUnderlinedFloatingLabelScaleFactor = 0.75f;
static const CGFloat kUnderlinedHorizontalEdgePaddingDefault = 2;
@interface MDCTextControlStyleUnderlined ()
-@property(strong, nonatomic) CAShapeLayer *thinUnderlineLayer;
-@property(strong, nonatomic) CAShapeLayer *thickUnderlineLayer;
+@property(strong, nonatomic) CAShapeLayer *normalUnderlineLayer;
+@property(strong, nonatomic) CAShapeLayer *editingUnderlineLayer;
-@property(strong, nonatomic, readonly, class) NSString *thickUnderlineGrowKey;
-@property(strong, nonatomic, readonly, class) NSString *thickUnderlineShrinkKey;
-@property(strong, nonatomic, readonly, class) NSString *thinUnderlineGrowKey;
-@property(strong, nonatomic, readonly, class) NSString *thinUnderlineShrinkKey;
+@property(strong, nonatomic, readonly, class) NSString *editingUnderlineGrowKey;
+@property(strong, nonatomic, readonly, class) NSString *editingUnderlineShrinkKey;
+@property(strong, nonatomic, readonly, class) NSString *normalUnderlineGrowKey;
+@property(strong, nonatomic, readonly, class) NSString *normalUnderlineShrinkKey;
+
+@property(strong, nonatomic, readonly, class) NSString *editingUnderlineThicknessKey;
+@property(strong, nonatomic, readonly, class) NSString *normalUnderlineThicknessKey;
@property(strong, nonatomic) NSMutableDictionary *underlineColors;
@property(nonatomic, assign) CGRect mostRecentBounds;
+@property(nonatomic, assign) BOOL isEditing;
+@property(nonatomic, assign) CGFloat containerHeight;
+@property(nonatomic, assign) NSTimeInterval animationDuration;
@end
@@ -58,9 +65,11 @@ - (instancetype)init {
#pragma mark Setup
- (void)commonMDCTextControlStyleUnderlinedInit {
+ self.mostRecentBounds = CGRectZero;
+ self.normalUnderlineThickness = kUnderlinedContainerStyleUnderlineThicknessNormal;
+ self.editingUnderlineThickness = kUnderlinedContainerStyleUnderlineThicknessEditing;
[self setUpUnderlineColors];
[self setUpUnderlineSublayers];
- self.mostRecentBounds = CGRectZero;
}
- (void)setUpUnderlineColors {
@@ -77,8 +86,8 @@ - (void)setUpUnderlineColors {
}
- (void)setUpUnderlineSublayers {
- self.thinUnderlineLayer = [[CAShapeLayer alloc] init];
- self.thickUnderlineLayer = [[CAShapeLayer alloc] init];
+ self.normalUnderlineLayer = [[CAShapeLayer alloc] init];
+ self.editingUnderlineLayer = [[CAShapeLayer alloc] init];
}
#pragma mark Accessors
@@ -91,6 +100,46 @@ - (void)setUnderlineColor:(nonnull UIColor *)underlineColor forState:(MDCTextCon
self.underlineColors[@(state)] = underlineColor;
}
+- (void)setEditingUnderlineThickness:(CGFloat)editingUnderlineThickness {
+ [self setEditingUnderlineThickness:editingUnderlineThickness animated:NO];
+}
+
+- (void)setNormalUnderlineThickness:(CGFloat)normalUnderlineThickness {
+ [self setNormalUnderlineThickness:normalUnderlineThickness animated:NO];
+}
+
+- (void)setEditingUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated {
+ _editingUnderlineThickness = thickness;
+
+ UIBezierPath *targetUnderlineBezier = [self targetEditingUnderlineBezier];
+ [self.editingUnderlineLayer removeAllAnimations];
+ if (animated) {
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:self.animationDuration];
+ [self.editingUnderlineLayer addAnimation:[self pathAnimationTo:targetUnderlineBezier]
+ forKey:self.class.editingUnderlineThicknessKey];
+ [CATransaction commit];
+ } else {
+ self.editingUnderlineLayer.path = targetUnderlineBezier.CGPath;
+ }
+}
+
+- (void)setNormalUnderlineThickness:(CGFloat)thickness animated:(BOOL)animated {
+ _normalUnderlineThickness = thickness;
+
+ UIBezierPath *targetUnderlineBezier = [self targetNormalUnderlineBezier];
+ [self.normalUnderlineLayer removeAllAnimations];
+ if (animated) {
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:self.animationDuration];
+ [self.normalUnderlineLayer addAnimation:[self pathAnimationTo:targetUnderlineBezier]
+ forKey:self.class.normalUnderlineThicknessKey];
+ [CATransaction commit];
+ } else {
+ self.normalUnderlineLayer.path = targetUnderlineBezier.CGPath;
+ }
+}
+
#pragma mark MDCTextControl
- (void)applyStyleToTextControl:(UIView *)textControl
@@ -102,8 +151,8 @@ - (void)applyStyleToTextControl:(UIView *)textControl
}
- (void)removeStyleFrom:(id)textControl {
- [self.thinUnderlineLayer removeFromSuperlayer];
- [self.thickUnderlineLayer removeFromSuperlayer];
+ [self.normalUnderlineLayer removeFromSuperlayer];
+ [self.editingUnderlineLayer removeFromSuperlayer];
}
- (id)
@@ -112,14 +161,16 @@ - (void)removeStyleFrom:(id)textControl {
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
return [[MDCTextControlVerticalPositioningReferenceUnderlined alloc]
initWithFloatingFontLineHeight:floatingLabelHeight
normalFontLineHeight:normalFontLineHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
- (UIFont *)floatingFontWithNormalFont:(UIFont *)font {
@@ -148,158 +199,133 @@ - (void)applyUnderlineStyleToView:(UIView *)view
self.mostRecentBounds = view.bounds;
}
- self.thinUnderlineLayer.fillColor = [self.underlineColors[@(state)] CGColor];
- self.thickUnderlineLayer.fillColor = [self.underlineColors[@(state)] CGColor];
+ self.containerHeight = CGRectGetMaxY(containerFrame);
+ self.isEditing = state == MDCTextControlStateEditing;
+ self.animationDuration = animationDuration;
- CGFloat containerHeight = CGRectGetMaxY(containerFrame);
+ self.normalUnderlineLayer.fillColor = [self.underlineColors[@(state)] CGColor];
+ self.editingUnderlineLayer.fillColor = [self.underlineColors[@(state)] CGColor];
BOOL styleIsBeingAppliedToView = NO;
- if (self.thickUnderlineLayer.superlayer != view.layer) {
- [view.layer insertSublayer:self.thickUnderlineLayer atIndex:0];
+ if (self.editingUnderlineLayer.superlayer != view.layer) {
+ [view.layer insertSublayer:self.editingUnderlineLayer atIndex:0];
styleIsBeingAppliedToView = YES;
}
- if (self.thinUnderlineLayer.superlayer != view.layer) {
- [view.layer insertSublayer:self.thinUnderlineLayer atIndex:0];
+ if (self.normalUnderlineLayer.superlayer != view.layer) {
+ [view.layer insertSublayer:self.normalUnderlineLayer atIndex:0];
styleIsBeingAppliedToView = YES;
}
- BOOL shouldShowThickUnderline = [self shouldShowThickUnderlineWithState:state];
- CGFloat viewWidth = CGRectGetWidth(view.bounds);
- CGFloat thickUnderlineThickness = shouldShowThickUnderline ? viewWidth : 0;
- UIBezierPath *targetThickUnderlineBezier =
- [self filledSublayerUnderlinePathWithViewBounds:view.bounds
- containerHeight:containerHeight
- underlineThickness:kUnderlinedContainerStyleUnderlineWidthThick
- underlineWidth:thickUnderlineThickness];
- CGFloat thinUnderlineThickness =
- shouldShowThickUnderline ? 0 : kUnderlinedContainerStyleUnderlineWidthThin;
- UIBezierPath *targetThinUnderlineBezier =
- [self filledSublayerUnderlinePathWithViewBounds:view.bounds
- containerHeight:containerHeight
- underlineThickness:thinUnderlineThickness
- underlineWidth:viewWidth];
-
+ UIBezierPath *targetEditingUnderlineBezier = [self targetEditingUnderlineBezier];
+ UIBezierPath *targetNormalUnderlineBezier = [self targetNormalUnderlineBezier];
if (animationDuration <= 0 || styleIsBeingAppliedToView || didChangeBounds) {
- [self.thinUnderlineLayer removeAllAnimations];
- [self.thickUnderlineLayer removeAllAnimations];
- self.thinUnderlineLayer.path = targetThinUnderlineBezier.CGPath;
- self.thickUnderlineLayer.path = targetThickUnderlineBezier.CGPath;
+ [self.normalUnderlineLayer removeAllAnimations];
+ [self.editingUnderlineLayer removeAllAnimations];
+ self.normalUnderlineLayer.path = targetNormalUnderlineBezier.CGPath;
+ self.editingUnderlineLayer.path = targetEditingUnderlineBezier.CGPath;
return;
}
- CABasicAnimation *preexistingThickUnderlineShrinkAnimation =
- (CABasicAnimation *)[self.thickUnderlineLayer
- animationForKey:self.class.thickUnderlineShrinkKey];
- CABasicAnimation *preexistingThickUnderlineGrowAnimation =
- (CABasicAnimation *)[self.thickUnderlineLayer
- animationForKey:self.class.thickUnderlineGrowKey];
+ CABasicAnimation *preexistingEditingUnderlineShrinkAnimation =
+ (CABasicAnimation *)[self.editingUnderlineLayer
+ animationForKey:self.class.editingUnderlineShrinkKey];
+ CABasicAnimation *preexistingEditingUnderlineGrowAnimation =
+ (CABasicAnimation *)[self.editingUnderlineLayer
+ animationForKey:self.class.editingUnderlineGrowKey];
- CABasicAnimation *preexistingThinUnderlineGrowAnimation =
- (CABasicAnimation *)[self.thinUnderlineLayer animationForKey:self.class.thinUnderlineGrowKey];
- CABasicAnimation *preexistingThinUnderlineShrinkAnimation =
- (CABasicAnimation *)[self.thinUnderlineLayer
- animationForKey:self.class.thinUnderlineShrinkKey];
+ CABasicAnimation *preexistingNormalUnderlineGrowAnimation =
+ (CABasicAnimation *)[self.normalUnderlineLayer
+ animationForKey:self.class.normalUnderlineGrowKey];
+ CABasicAnimation *preexistingNormalUnderlineShrinkAnimation =
+ (CABasicAnimation *)[self.normalUnderlineLayer
+ animationForKey:self.class.normalUnderlineShrinkKey];
[CATransaction begin];
{
[CATransaction setAnimationDuration:animationDuration];
- if (shouldShowThickUnderline) {
- if (preexistingThickUnderlineShrinkAnimation) {
- [self.thickUnderlineLayer removeAnimationForKey:self.class.thickUnderlineShrinkKey];
+ if (self.isEditing) {
+ if (preexistingEditingUnderlineShrinkAnimation) {
+ [self.editingUnderlineLayer removeAnimationForKey:self.class.editingUnderlineShrinkKey];
}
- BOOL needsThickUnderlineGrowAnimation = NO;
- if (preexistingThickUnderlineGrowAnimation) {
- CGPathRef toValue = (__bridge CGPathRef)preexistingThickUnderlineGrowAnimation.toValue;
- if (!CGPathEqualToPath(toValue, targetThickUnderlineBezier.CGPath)) {
- [self.thickUnderlineLayer removeAnimationForKey:self.class.thickUnderlineGrowKey];
- needsThickUnderlineGrowAnimation = YES;
- self.thickUnderlineLayer.path = targetThickUnderlineBezier.CGPath;
+ BOOL needsEditingUnderlineGrowAnimation = NO;
+ if (preexistingEditingUnderlineGrowAnimation) {
+ CGPathRef toValue = (__bridge CGPathRef)preexistingEditingUnderlineGrowAnimation.toValue;
+ if (!CGPathEqualToPath(toValue, targetEditingUnderlineBezier.CGPath)) {
+ [self.editingUnderlineLayer removeAnimationForKey:self.class.editingUnderlineGrowKey];
+ needsEditingUnderlineGrowAnimation = YES;
+ self.editingUnderlineLayer.path = targetEditingUnderlineBezier.CGPath;
}
} else {
- needsThickUnderlineGrowAnimation = YES;
+ needsEditingUnderlineGrowAnimation = YES;
}
- if (needsThickUnderlineGrowAnimation) {
- [self.thickUnderlineLayer addAnimation:[self pathAnimationTo:targetThickUnderlineBezier]
- forKey:self.class.thickUnderlineGrowKey];
+ if (needsEditingUnderlineGrowAnimation) {
+ [self.editingUnderlineLayer addAnimation:[self pathAnimationTo:targetEditingUnderlineBezier]
+ forKey:self.class.editingUnderlineGrowKey];
}
- if (preexistingThinUnderlineGrowAnimation) {
- [self.thinUnderlineLayer removeAnimationForKey:self.class.thinUnderlineGrowKey];
+ if (preexistingNormalUnderlineGrowAnimation) {
+ [self.normalUnderlineLayer removeAnimationForKey:self.class.normalUnderlineGrowKey];
}
- BOOL needsThinUnderlineShrinkAnimation = NO;
- if (preexistingThinUnderlineShrinkAnimation) {
- CGPathRef toValue = (__bridge CGPathRef)preexistingThinUnderlineShrinkAnimation.toValue;
- if (!CGPathEqualToPath(toValue, targetThinUnderlineBezier.CGPath)) {
- [self.thinUnderlineLayer removeAnimationForKey:self.class.thinUnderlineShrinkKey];
- needsThinUnderlineShrinkAnimation = YES;
- self.thinUnderlineLayer.path = targetThinUnderlineBezier.CGPath;
+ BOOL needsNormalUnderlineShrinkAnimation = NO;
+ if (preexistingNormalUnderlineShrinkAnimation) {
+ CGPathRef toValue = (__bridge CGPathRef)preexistingNormalUnderlineShrinkAnimation.toValue;
+ if (!CGPathEqualToPath(toValue, targetNormalUnderlineBezier.CGPath)) {
+ [self.normalUnderlineLayer removeAnimationForKey:self.class.normalUnderlineShrinkKey];
+ needsNormalUnderlineShrinkAnimation = YES;
+ self.normalUnderlineLayer.path = targetNormalUnderlineBezier.CGPath;
}
} else {
- needsThinUnderlineShrinkAnimation = YES;
+ needsNormalUnderlineShrinkAnimation = YES;
}
- if (needsThinUnderlineShrinkAnimation) {
- [self.thinUnderlineLayer addAnimation:[self pathAnimationTo:targetThinUnderlineBezier]
- forKey:self.class.thinUnderlineShrinkKey];
+ if (needsNormalUnderlineShrinkAnimation) {
+ [self.normalUnderlineLayer addAnimation:[self pathAnimationTo:targetNormalUnderlineBezier]
+ forKey:self.class.normalUnderlineShrinkKey];
}
} else {
- if (preexistingThickUnderlineGrowAnimation) {
- [self.thickUnderlineLayer removeAnimationForKey:self.class.thickUnderlineGrowKey];
+ if (preexistingEditingUnderlineGrowAnimation) {
+ [self.editingUnderlineLayer removeAnimationForKey:self.class.editingUnderlineGrowKey];
}
- BOOL needsThickUnderlineShrinkAnimation = NO;
- if (preexistingThickUnderlineShrinkAnimation) {
- CGPathRef toValue = (__bridge CGPathRef)preexistingThickUnderlineShrinkAnimation.toValue;
- if (!CGPathEqualToPath(toValue, targetThickUnderlineBezier.CGPath)) {
- [self.thickUnderlineLayer removeAnimationForKey:self.class.thickUnderlineShrinkKey];
- needsThickUnderlineShrinkAnimation = YES;
- self.thickUnderlineLayer.path = targetThickUnderlineBezier.CGPath;
+ BOOL needsEditingUnderlineShrinkAnimation = NO;
+ if (preexistingEditingUnderlineShrinkAnimation) {
+ CGPathRef toValue = (__bridge CGPathRef)preexistingEditingUnderlineShrinkAnimation.toValue;
+ if (!CGPathEqualToPath(toValue, targetEditingUnderlineBezier.CGPath)) {
+ [self.editingUnderlineLayer removeAnimationForKey:self.class.editingUnderlineShrinkKey];
+ needsEditingUnderlineShrinkAnimation = YES;
+ self.editingUnderlineLayer.path = targetEditingUnderlineBezier.CGPath;
}
} else {
- needsThickUnderlineShrinkAnimation = YES;
+ needsEditingUnderlineShrinkAnimation = YES;
}
- if (needsThickUnderlineShrinkAnimation) {
- [self.thickUnderlineLayer addAnimation:[self pathAnimationTo:targetThickUnderlineBezier]
- forKey:self.class.thickUnderlineShrinkKey];
+ if (needsEditingUnderlineShrinkAnimation) {
+ [self.editingUnderlineLayer addAnimation:[self pathAnimationTo:targetEditingUnderlineBezier]
+ forKey:self.class.editingUnderlineShrinkKey];
}
- if (preexistingThinUnderlineShrinkAnimation) {
- [self.thinUnderlineLayer removeAnimationForKey:self.class.thinUnderlineShrinkKey];
+ if (preexistingNormalUnderlineShrinkAnimation) {
+ [self.normalUnderlineLayer removeAnimationForKey:self.class.normalUnderlineShrinkKey];
}
- BOOL needsThickUnderlineGrowAnimation = NO;
- if (preexistingThinUnderlineGrowAnimation) {
- CGPathRef toValue = (__bridge CGPathRef)preexistingThinUnderlineGrowAnimation.toValue;
- if (!CGPathEqualToPath(toValue, targetThinUnderlineBezier.CGPath)) {
- [self.thinUnderlineLayer removeAnimationForKey:self.class.thinUnderlineGrowKey];
- needsThickUnderlineGrowAnimation = YES;
- self.thinUnderlineLayer.path = targetThinUnderlineBezier.CGPath;
+ BOOL needsEditingUnderlineGrowAnimation = NO;
+ if (preexistingNormalUnderlineGrowAnimation) {
+ CGPathRef toValue = (__bridge CGPathRef)preexistingNormalUnderlineGrowAnimation.toValue;
+ if (!CGPathEqualToPath(toValue, targetNormalUnderlineBezier.CGPath)) {
+ [self.normalUnderlineLayer removeAnimationForKey:self.class.normalUnderlineGrowKey];
+ needsEditingUnderlineGrowAnimation = YES;
+ self.normalUnderlineLayer.path = targetNormalUnderlineBezier.CGPath;
}
} else {
- needsThickUnderlineGrowAnimation = YES;
+ needsEditingUnderlineGrowAnimation = YES;
}
- if (needsThickUnderlineGrowAnimation) {
- [self.thinUnderlineLayer addAnimation:[self pathAnimationTo:targetThinUnderlineBezier]
- forKey:self.class.thinUnderlineGrowKey];
+ if (needsEditingUnderlineGrowAnimation) {
+ [self.normalUnderlineLayer addAnimation:[self pathAnimationTo:targetNormalUnderlineBezier]
+ forKey:self.class.normalUnderlineGrowKey];
}
}
}
[CATransaction commit];
}
-- (BOOL)shouldShowThickUnderlineWithState:(MDCTextControlState)state {
- BOOL shouldShow = NO;
- switch (state) {
- case MDCTextControlStateEditing:
- shouldShow = YES;
- break;
- case MDCTextControlStateNormal:
- case MDCTextControlStateDisabled:
- default:
- break;
- }
- return shouldShow;
-}
-
#pragma mark Animation
- (CABasicAnimation *)pathAnimationTo:(UIBezierPath *)path {
@@ -330,44 +356,87 @@ - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
CABasicAnimation *animation = (CABasicAnimation *)anim;
CGPathRef toValue = (__bridge CGPathRef)animation.toValue;
- CABasicAnimation *thickGrowAnimation = (CABasicAnimation *)[self.thickUnderlineLayer
- animationForKey:self.class.thickUnderlineGrowKey];
- CABasicAnimation *thickShrinkAnimation = (CABasicAnimation *)[self.thickUnderlineLayer
- animationForKey:self.class.thickUnderlineShrinkKey];
- CABasicAnimation *thinGrowAnimation =
- (CABasicAnimation *)[self.thinUnderlineLayer animationForKey:self.class.thinUnderlineGrowKey];
- CABasicAnimation *thinShrinkAnimation = (CABasicAnimation *)[self.thinUnderlineLayer
- animationForKey:self.class.thinUnderlineShrinkKey];
+ CABasicAnimation *thickGrowAnimation = (CABasicAnimation *)[self.editingUnderlineLayer
+ animationForKey:self.class.editingUnderlineGrowKey];
+ CABasicAnimation *thickShrinkAnimation = (CABasicAnimation *)[self.editingUnderlineLayer
+ animationForKey:self.class.editingUnderlineShrinkKey];
+ CABasicAnimation *thinGrowAnimation = (CABasicAnimation *)[self.normalUnderlineLayer
+ animationForKey:self.class.normalUnderlineGrowKey];
+ CABasicAnimation *thinShrinkAnimation = (CABasicAnimation *)[self.normalUnderlineLayer
+ animationForKey:self.class.normalUnderlineShrinkKey];
+
+ CABasicAnimation *editingUnderlineAnimation = (CABasicAnimation *)[self.editingUnderlineLayer
+ animationForKey:self.class.editingUnderlineThicknessKey];
+ CABasicAnimation *normalUnderlineAnimation = (CABasicAnimation *)[self.normalUnderlineLayer
+ animationForKey:self.class.normalUnderlineThicknessKey];
if (flag) {
if ((animation == thickGrowAnimation) || (animation == thickShrinkAnimation)) {
- self.thickUnderlineLayer.path = toValue;
+ self.editingUnderlineLayer.path = toValue;
}
if ((animation == thinGrowAnimation) || (animation == thinShrinkAnimation)) {
- self.thinUnderlineLayer.path = toValue;
+ self.normalUnderlineLayer.path = toValue;
+ }
+ if (animation == editingUnderlineAnimation) {
+ self.editingUnderlineLayer.path = toValue;
+ }
+ if (animation == normalUnderlineAnimation) {
+ self.normalUnderlineLayer.path = toValue;
}
}
}
-+ (NSString *)thinUnderlineShrinkKey {
- return @"thinUnderlineShrinkKey";
++ (NSString *)normalUnderlineShrinkKey {
+ return @"normalUnderlineShrinkKey";
}
-+ (NSString *)thinUnderlineGrowKey {
- return @"thinUnderlineGrowKey";
+
++ (NSString *)normalUnderlineGrowKey {
+ return @"normalUnderlineGrowKey";
}
-+ (NSString *)thickUnderlineShrinkKey {
- return @"thickUnderlineShrinkKey";
+
++ (NSString *)editingUnderlineShrinkKey {
+ return @"editingUnderlineShrinkKey";
}
-+ (NSString *)thickUnderlineGrowKey {
- return @"thickUnderlineGrowKey";
+
++ (NSString *)editingUnderlineGrowKey {
+ return @"editingUnderlineGrowKey";
+}
+
++ (NSString *)normalUnderlineThicknessKey {
+ return @"normalUnderlineThicknessKey";
+}
+
++ (NSString *)editingUnderlineThicknessKey {
+ return @"editingUnderlineThicknessKey";
}
#pragma mark Path Drawing
-- (UIBezierPath *)filledSublayerUnderlinePathWithViewBounds:(CGRect)viewBounds
- containerHeight:(CGFloat)containerHeight
- underlineThickness:(CGFloat)underlineThickness
- underlineWidth:(CGFloat)underlineWidth {
+- (UIBezierPath *)targetEditingUnderlineBezier {
+ CGFloat editingUnderlineWidth = self.isEditing ? CGRectGetWidth(self.mostRecentBounds) : 0;
+ CGFloat editingUnderlineThickness = self.isEditing ? self.editingUnderlineThickness : 0;
+ UIBezierPath *targetEditingUnderlineBezier =
+ [self underlinePathWithViewBounds:self.mostRecentBounds
+ containerHeight:self.containerHeight
+ underlineThickness:editingUnderlineThickness
+ underlineWidth:editingUnderlineWidth];
+ return targetEditingUnderlineBezier;
+}
+
+- (UIBezierPath *)targetNormalUnderlineBezier {
+ CGFloat normalUnderlineThickness = self.isEditing ? 0 : self.normalUnderlineThickness;
+ UIBezierPath *targetNormalUnderlineBezier =
+ [self underlinePathWithViewBounds:self.mostRecentBounds
+ containerHeight:self.containerHeight
+ underlineThickness:normalUnderlineThickness
+ underlineWidth:CGRectGetWidth(self.mostRecentBounds)];
+ return targetNormalUnderlineBezier;
+}
+
+- (UIBezierPath *)underlinePathWithViewBounds:(CGRect)viewBounds
+ containerHeight:(CGFloat)containerHeight
+ underlineThickness:(CGFloat)underlineThickness
+ underlineWidth:(CGFloat)underlineWidth {
UIBezierPath *path = [[UIBezierPath alloc] init];
CGFloat viewWidth = CGRectGetWidth(viewBounds);
CGFloat halfViewWidth = 0.5f * viewWidth;
diff --git a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.h b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.h
index bb5d82b7bd6..9349cd89f1b 100644
--- a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.h
+++ b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.h
@@ -25,6 +25,7 @@
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl;
@end
diff --git a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.m b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.m
index 6f2f056a718..f735032a48c 100644
--- a/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.m
+++ b/components/private/TextControlsPrivate/src/UnderlinedStyle/MDCTextControlVerticalPositioningReferenceUnderlined.m
@@ -21,8 +21,8 @@
https://material.io/design/components/text-fields.html.
*/
-static const CGFloat kMinPaddingAroundTextWhenNoLabel = 6.0f;
-static const CGFloat kMaxPaddingAroundTextWhenNoLabel = 10.0f;
+static const CGFloat kMinPaddingAroundTextWhenNoFloatingLabel = 6.0f;
+static const CGFloat kMaxPaddingAroundTextWhenNoFloatingLabel = 10.0f;
static const CGFloat kMinPaddingBetweenContainerTopAndFloatingLabel = 6.0f;
static const CGFloat kMaxPaddingBetweenContainerTopAndFloatingLabel = 10.0f;
static const CGFloat kMinPaddingBetweenFloatingLabelAndEditingText = 3.0f;
@@ -59,7 +59,8 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
self = [super init];
if (self) {
[self calculatePaddingValuesWithFoatingFontLineHeight:floatingLabelHeight
@@ -67,7 +68,8 @@ - (instancetype)initWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
- preferredContainerHeight:preferredContainerHeight];
+ preferredContainerHeight:preferredContainerHeight
+ isMultilineTextControl:isMultilineTextControl];
}
return self;
}
@@ -77,13 +79,10 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
- preferredContainerHeight:(CGFloat)preferredContainerHeight {
- BOOL isMultiline = numberOfTextRows > 1 || numberOfTextRows == 0;
+ preferredContainerHeight:(CGFloat)preferredContainerHeight
+ isMultilineTextControl:(BOOL)isMultilineTextControl {
CGFloat clampedDensity = MDCTextControlClampDensity(density);
- _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
- kMinPaddingAroundTextWhenNoLabel, kMaxPaddingAroundTextWhenNoLabel, clampedDensity);
-
_paddingBetweenContainerTopAndFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
kMinPaddingBetweenContainerTopAndFloatingLabel,
kMaxPaddingBetweenContainerTopAndFloatingLabel, clampedDensity);
@@ -103,54 +102,68 @@ - (void)calculatePaddingValuesWithFoatingFontLineHeight:(CGFloat)floatingLabelHe
floatingLabelHeight +
_paddingBetweenFloatingLabelAndEditingText;
- CGFloat containerHeightWithPaddingsDeterminedByDensity =
+ CGFloat defaultContainerHeightForFloatingLabel =
MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
floatingLabelHeight, textRowHeight, numberOfTextRows,
_paddingBetweenContainerTopAndFloatingLabel, _paddingBetweenFloatingLabelAndEditingText,
_paddingBetweenEditingTextAndContainerBottom);
-
- BOOL clientHasSpecifiedValidPreferredContainerHeight =
- preferredContainerHeight > containerHeightWithPaddingsDeterminedByDensity;
- if (clientHasSpecifiedValidPreferredContainerHeight && !isMultiline) {
- CGFloat difference = preferredContainerHeight - containerHeightWithPaddingsDeterminedByDensity;
- CGFloat sumOfPaddingValues = _paddingBetweenContainerTopAndFloatingLabel +
- _paddingBetweenFloatingLabelAndEditingText +
- _paddingBetweenEditingTextAndContainerBottom;
- _paddingBetweenContainerTopAndFloatingLabel =
- _paddingBetweenContainerTopAndFloatingLabel +
- ((_paddingBetweenContainerTopAndFloatingLabel / sumOfPaddingValues) * difference);
- _paddingBetweenFloatingLabelAndEditingText =
- _paddingBetweenFloatingLabelAndEditingText +
- ((_paddingBetweenFloatingLabelAndEditingText / sumOfPaddingValues) * difference);
- _paddingBetweenEditingTextAndContainerBottom =
- _paddingBetweenEditingTextAndContainerBottom +
- ((_paddingBetweenEditingTextAndContainerBottom / sumOfPaddingValues) * difference);
- }
-
- if (clientHasSpecifiedValidPreferredContainerHeight) {
+ BOOL preferredContainerHeightIsValidForFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForFloatingLabel;
+ if (preferredContainerHeightIsValidForFloatingLabel) {
_containerHeightWithFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat difference = preferredContainerHeight - defaultContainerHeightForFloatingLabel;
+ CGFloat sumOfPaddingValues = _paddingBetweenContainerTopAndFloatingLabel +
+ _paddingBetweenFloatingLabelAndEditingText +
+ _paddingBetweenEditingTextAndContainerBottom;
+ _paddingBetweenContainerTopAndFloatingLabel =
+ _paddingBetweenContainerTopAndFloatingLabel +
+ ((_paddingBetweenContainerTopAndFloatingLabel / sumOfPaddingValues) * difference);
+ _paddingBetweenFloatingLabelAndEditingText =
+ _paddingBetweenFloatingLabelAndEditingText +
+ ((_paddingBetweenFloatingLabelAndEditingText / sumOfPaddingValues) * difference);
+ _paddingBetweenEditingTextAndContainerBottom =
+ _paddingBetweenEditingTextAndContainerBottom +
+ ((_paddingBetweenEditingTextAndContainerBottom / sumOfPaddingValues) * difference);
+ _paddingBetweenContainerTopAndNormalLabel = _paddingBetweenContainerTopAndFloatingLabel +
+ floatingLabelHeight +
+ _paddingBetweenFloatingLabelAndEditingText;
+ }
} else {
- _containerHeightWithFloatingLabel = containerHeightWithPaddingsDeterminedByDensity;
+ _containerHeightWithFloatingLabel = defaultContainerHeightForFloatingLabel;
}
- CGFloat containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity =
+ _paddingAroundTextWhenNoFloatingLabel = MDCTextControlPaddingValueWithMinimumPadding(
+ kMinPaddingAroundTextWhenNoFloatingLabel, kMaxPaddingAroundTextWhenNoFloatingLabel,
+ clampedDensity);
+
+ CGFloat defaultContainerHeightForNoFloatingLabel =
MDCTextControlCalculateContainerHeightWhenNoFloatingLabelWithTextRowHeight(
textRowHeight, numberOfTextRows, _paddingAroundTextWhenNoFloatingLabel);
- BOOL clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel =
- preferredContainerHeight > containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity;
- if (clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel && !isMultiline) {
- CGFloat difference = preferredContainerHeight - containerHeightWithPaddingsDeterminedByDensity;
- CGFloat sumOfPaddingValues = _paddingAroundTextWhenNoFloatingLabel * 2.0f;
- _paddingAroundTextWhenNoFloatingLabel =
- _paddingAroundTextWhenNoFloatingLabel +
- ((_paddingAroundTextWhenNoFloatingLabel / sumOfPaddingValues) * difference);
- }
-
- if (clientHasSpecifiedValidPreferredContainerHeightWhenNoFloatingLabel) {
+ BOOL preferredContainerHeightIsValidForNoFloatingLabel =
+ preferredContainerHeight > defaultContainerHeightForNoFloatingLabel;
+ if (preferredContainerHeightIsValidForNoFloatingLabel) {
_containerHeightWithoutFloatingLabel = preferredContainerHeight;
+ BOOL shouldUpdatePaddingValuesToMeetMinimumHeight = !isMultilineTextControl;
+ if (shouldUpdatePaddingValuesToMeetMinimumHeight) {
+ CGFloat difference = preferredContainerHeight - defaultContainerHeightForNoFloatingLabel;
+ CGFloat sumOfPaddingValues = _paddingAroundTextWhenNoFloatingLabel * 2.0f;
+ _paddingAroundTextWhenNoFloatingLabel =
+ _paddingAroundTextWhenNoFloatingLabel +
+ ((_paddingAroundTextWhenNoFloatingLabel / sumOfPaddingValues) * difference);
+ }
} else {
- _containerHeightWithoutFloatingLabel =
- containerHeightWhenNoFloatingLabelWithPaddingsDeterminedByDensity;
+ _containerHeightWithoutFloatingLabel = defaultContainerHeightForNoFloatingLabel;
+ }
+
+ if (isMultilineTextControl) {
+ CGFloat heightWithOneRow = MDCTextControlCalculateContainerHeightWithFloatingLabelHeight(
+ floatingLabelHeight, textRowHeight, 1, _paddingBetweenContainerTopAndFloatingLabel,
+ _paddingBetweenFloatingLabelAndEditingText, _paddingBetweenEditingTextAndContainerBottom);
+ CGFloat halfOfHeightWithOneRow = (CGFloat)0.5 * heightWithOneRow;
+ CGFloat halfOfNormalFontLineHeight = (CGFloat)0.5 * normalFontLineHeight;
+ _paddingBetweenContainerTopAndNormalLabel = halfOfHeightWithOneRow - halfOfNormalFontLineHeight;
}
}
diff --git a/components/private/ThumbTrack/src/MDCThumbTrack.m b/components/private/ThumbTrack/src/MDCThumbTrack.m
index 952242be4f5..16f5e60b672 100644
--- a/components/private/ThumbTrack/src/MDCThumbTrack.m
+++ b/components/private/ThumbTrack/src/MDCThumbTrack.m
@@ -153,6 +153,7 @@ - (instancetype)initWithFrame:(CGRect)frame onTintColor:(UIColor *)onTintColor {
_trackOnLayer = [CALayer layer];
[_trackView.layer addSublayer:_trackOnLayer];
+ _trackView.layer.masksToBounds = YES;
[self addSubview:_trackView];
@@ -674,11 +675,19 @@ - (void)updateThumbTrackAnimated:(BOOL)animated
delay:0
options:options
animations:^{
- self.value = self.filledTrackAnchorValue;
+ // Set _value ivar instead of property to avoid conflicts with logic that
+ // sends UIControlEventValueChanged.
+
+ // Setting self.value programmatically here causes _lastDispatchedValue to
+ // be updated to the new value before sendDiscreteChangeAction executes.
+ // sendDiscreteChangeAction uses _lastDispatchedValue to ensure that
+ // UIControlEventValueChanged actions aren't sent as a result of
+ // programmatic changes to the value property.
+ _value = self.filledTrackAnchorValue;
[self updateViewsMainIsAnimated:animated
withDuration:animationDurationToAnchor
animationOptions:options];
- self.value = currentValue;
+ _value = currentValue;
}
completion:afterCrossingAnchorAnimation];
} else {
@@ -742,11 +751,11 @@ - (void)updateViewsNoAnimation {
[self updateTrackMask];
_thumbView.backgroundColor = _thumbEnabledColor;
- _thumbView.layer.borderColor = _thumbEnabledColor.CGColor;
+ _thumbView.borderColor = _thumbEnabledColor;
}
} else {
_thumbView.backgroundColor = _thumbDisabledColor;
- _thumbView.layer.borderColor = _clearColor.CGColor;
+ _thumbView.borderColor = _clearColor;
if (_thumbIsSmallerWhenDisabled) {
[self setDisplayThumbRadius:_thumbRadius - _trackHeight];
@@ -888,7 +897,7 @@ - (void)updateViewsForThumbAfterMoveIsAnimated:(BOOL)animated
[self updateTrackMask];
_thumbView.backgroundColor = _clearColor;
- _thumbView.layer.borderColor = _trackOffColor.CGColor;
+ _thumbView.borderColor = _trackOffColor;
}
CGFloat radius;
@@ -903,7 +912,7 @@ - (void)updateViewsForThumbAfterMoveIsAnimated:(BOOL)animated
radius = _thumbRadius;
}
- if (radius == _thumbView.layer.cornerRadius || !_thumbGrowsWhenDragging) {
+ if (radius == _thumbView.cornerRadius || !_thumbGrowsWhenDragging) {
// No need to change anything
return;
}
@@ -912,7 +921,7 @@ - (void)updateViewsForThumbAfterMoveIsAnimated:(BOOL)animated
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
anim.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
- anim.fromValue = [NSNumber numberWithDouble:_thumbView.layer.cornerRadius];
+ anim.fromValue = [NSNumber numberWithDouble:_thumbView.cornerRadius];
anim.toValue = [NSNumber numberWithDouble:radius];
anim.duration = duration;
anim.delegate = self;
@@ -949,7 +958,7 @@ - (void)updateTrackMask {
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, maskFrame);
- CGFloat radius = _thumbView.layer.cornerRadius;
+ CGFloat radius = _thumbView.cornerRadius;
if (_thumbView.layer.presentationLayer != NULL) {
// If we're animating (growing or shrinking) lean on the side of the smaller radius, to prevent
// a gap from appearing between the thumb and the track in the intermediate frames.
diff --git a/components/private/ThumbTrack/src/MDCThumbView.h b/components/private/ThumbTrack/src/MDCThumbView.h
index 7fa574a6937..d3c5cee690f 100644
--- a/components/private/ThumbTrack/src/MDCThumbView.h
+++ b/components/private/ThumbTrack/src/MDCThumbView.h
@@ -28,9 +28,22 @@
/** The border width of the thumbview layer. */
@property(nonatomic, assign) CGFloat borderWidth;
+/** The border color of the thumbview layer. */
+@property(nullable, nonatomic) UIColor *borderColor;
+
/** The corner radius of the thumbview layer. */
@property(nonatomic, assign) CGFloat cornerRadius;
+/**
+ A Boolean value that determines whether the visible area is centered in the bounds of the view.
+
+ If set to YES, the visible area is centered in the bounds of the view, which is often used to
+ configure invisible tappable area. If set to NO, the visible area fills its bounds.
+
+ The default value is @c NO.
+*/
+@property(nonatomic, assign) BOOL centerVisibleArea;
+
/** Set the @c icon shown on the thumb. */
- (void)setIcon:(nullable UIImage *)icon;
diff --git a/components/private/ThumbTrack/src/MDCThumbView.m b/components/private/ThumbTrack/src/MDCThumbView.m
index 5f5cf581625..73b0baf1cbd 100644
--- a/components/private/ThumbTrack/src/MDCThumbView.m
+++ b/components/private/ThumbTrack/src/MDCThumbView.m
@@ -15,18 +15,25 @@
#import "MDCThumbView.h"
#import "MaterialShadowElevations.h"
-#import "MaterialShadowLayer.h"
+#import "MaterialShapeLibrary.h"
+#import "MaterialShapes.h"
+#import "MaterialMath.h"
@interface MDCThumbView ()
@property(nonatomic, strong) UIImageView *iconView;
+// The shape generator used to define the shape of the thumb view.
+@property(nullable, nonatomic, strong) MDCRectangleShapeGenerator *shapeGenerator;
+@property(nonatomic, readonly, strong) MDCShapedShadowLayer *layer;
@end
@implementation MDCThumbView
+@dynamic layer;
+
+ (Class)layerClass {
- return [MDCShadowLayer class];
+ return [MDCShapedShadowLayer class];
}
- (instancetype)initWithFrame:(CGRect)frame {
@@ -35,39 +42,89 @@ - (instancetype)initWithFrame:(CGRect)frame {
// TODO: Remove once MDCShadowLayer is rasterized by default.
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
+
_shadowColor = UIColor.blackColor;
+ _shapeGenerator = [[MDCRectangleShapeGenerator alloc] init];
+ self.layer.shadowColor = _shadowColor.CGColor;
+ self.layer.shapeGenerator = _shapeGenerator;
}
return self;
}
- (void)setBorderWidth:(CGFloat)borderWidth {
- _borderWidth = borderWidth;
- self.layer.borderWidth = borderWidth;
+ self.layer.shapedBorderWidth = borderWidth;
+}
+
+- (CGFloat)borderWidth {
+ return self.layer.shapedBorderWidth;
+}
+
+- (void)setBorderColor:(UIColor *)borderColor {
+ self.layer.shapedBorderColor = borderColor;
+}
+
+- (UIColor *)borderColor {
+ return self.layer.shapedBorderColor;
}
- (void)setCornerRadius:(CGFloat)cornerRadius {
+ if (cornerRadius == _cornerRadius) {
+ return;
+ }
_cornerRadius = cornerRadius;
- self.layer.cornerRadius = cornerRadius;
+
+ [self configureShapeGeneratorWithCornerRadius:cornerRadius
+ centerVisibleArea:self.centerVisibleArea];
+
[self setNeedsLayout];
}
-- (MDCShadowElevation)elevation {
- return [self shadowLayer].elevation;
+- (void)setCenterVisibleArea:(BOOL)centerVisibleArea {
+ if (centerVisibleArea == _centerVisibleArea) {
+ return;
+ }
+ _centerVisibleArea = centerVisibleArea;
+
+ [self configureShapeGeneratorWithCornerRadius:self.cornerRadius
+ centerVisibleArea:centerVisibleArea];
+
+ [self setNeedsLayout];
}
-- (void)setElevation:(MDCShadowElevation)elevation {
- [self shadowLayer].elevation = elevation;
+- (void)configureShapeGeneratorWithCornerRadius:(CGFloat)cornerRadius
+ centerVisibleArea:(BOOL)centerVisibleArea {
+ MDCCornerTreatment *cornerTreatment =
+ [[MDCRoundedCornerTreatment alloc] initWithRadius:cornerRadius];
+ [self.shapeGenerator setCorners:cornerTreatment];
+
+ if (centerVisibleArea) {
+ UIEdgeInsets visibleAreaInsets = UIEdgeInsetsZero;
+ CGSize visibleAreaSize = CGSizeMake(cornerRadius * 2, cornerRadius * 2);
+ CGFloat additionalRequiredHeight =
+ MAX(0, CGRectGetHeight(self.bounds) - visibleAreaSize.height);
+ CGFloat additionalRequiredWidth = MAX(0, CGRectGetWidth(self.bounds) - visibleAreaSize.width);
+ visibleAreaInsets.top = MDCCeil(additionalRequiredHeight * 0.5f);
+ visibleAreaInsets.bottom = additionalRequiredHeight - visibleAreaInsets.top;
+ visibleAreaInsets.left = MDCCeil(additionalRequiredWidth * 0.5f);
+ visibleAreaInsets.right = additionalRequiredWidth - visibleAreaInsets.left;
+
+ self.shapeGenerator.topLeftCornerOffset =
+ CGPointMake(visibleAreaInsets.left, visibleAreaInsets.top);
+ self.shapeGenerator.topRightCornerOffset =
+ CGPointMake(-visibleAreaInsets.right, visibleAreaInsets.top);
+ self.shapeGenerator.bottomLeftCornerOffset =
+ CGPointMake(visibleAreaInsets.left, -visibleAreaInsets.bottom);
+ self.shapeGenerator.bottomRightCornerOffset =
+ CGPointMake(-visibleAreaInsets.right, -visibleAreaInsets.bottom);
+ }
}
-- (MDCShadowLayer *)shadowLayer {
- return (MDCShadowLayer *)self.layer;
+- (MDCShadowElevation)elevation {
+ return self.layer.elevation;
}
-- (void)layoutSubviews {
- [super layoutSubviews];
- self.layer.shadowPath =
- [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:_cornerRadius].CGPath;
- self.layer.shadowColor = self.shadowColor.CGColor;
+- (void)setElevation:(MDCShadowElevation)elevation {
+ self.layer.elevation = elevation;
}
- (void)setIcon:(nullable UIImage *)icon {
@@ -93,4 +150,12 @@ - (void)setShadowColor:(UIColor *)shadowColor {
self.layer.shadowColor = shadowColor.CGColor;
}
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
+ self.layer.shapedBackgroundColor = backgroundColor;
+}
+
+- (UIColor *)backgroundColor {
+ return self.layer.shapedBackgroundColor;
+}
+
@end
diff --git a/components/private/ThumbTrack/tests/snapshot/MDCThumbTrackSnapshotTests.m b/components/private/ThumbTrack/tests/snapshot/MDCThumbTrackSnapshotTests.m
new file mode 100644
index 00000000000..2536b098dac
--- /dev/null
+++ b/components/private/ThumbTrack/tests/snapshot/MDCThumbTrackSnapshotTests.m
@@ -0,0 +1,99 @@
+// 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
+
+#import "MaterialSnapshot.h"
+#import "MaterialThumbTrack.h"
+
+@interface MDCThumbTrackSnapshotTests : MDCSnapshotTestCase
+
+@property(nonatomic, strong) MDCThumbTrack *thumbTrack;
+
+@end
+
+@implementation MDCThumbTrackSnapshotTests
+
+- (void)setUp {
+ [super setUp];
+
+ // Uncomment below to recreate all the goldens (or add the following line to the specific
+ // test you wish to recreate the golden for).
+ // self.recordMode = YES;
+
+ self.thumbTrack = [[MDCThumbTrack alloc] initWithFrame:CGRectMake(0, 0, 120, 30)];
+ self.thumbTrack.thumbRadius = 10;
+ self.thumbTrack.trackHeight = 3;
+ self.thumbTrack.trackOnColor = UIColor.blueColor;
+ self.thumbTrack.trackOffColor = UIColor.redColor;
+ self.thumbTrack.value = 0;
+ self.thumbTrack.minimumValue = -10;
+ self.thumbTrack.maximumValue = 10;
+ self.thumbTrack.backgroundColor = UIColor.whiteColor;
+ self.thumbTrack.thumbEnabledColor = UIColor.blackColor;
+}
+
+- (void)tearDown {
+ self.thumbTrack = nil;
+
+ [super tearDown];
+}
+
+- (void)generateSnapshotWithBorderAndVerifyForView:(MDCThumbTrack *)view {
+ // TODO(b/161927075): Refactor when this functionality is available in MaterialSnapshot.
+ UIView *thumbTrackFrameView = [[UIView alloc] initWithFrame:view.bounds];
+ [view addSubview:thumbTrackFrameView];
+ thumbTrackFrameView.layer.borderColor = UIColor.yellowColor.CGColor;
+ thumbTrackFrameView.layer.borderWidth = 0.5f;
+
+ UIView *thumbViewFrameView = [[UIView alloc] initWithFrame:view.thumbView.bounds];
+ [view.thumbView addSubview:thumbViewFrameView];
+ thumbViewFrameView.layer.borderColor = UIColor.yellowColor.CGColor;
+ thumbViewFrameView.layer.borderWidth = 0.5f;
+
+ UIView *snapshotView = [view mdc_addToBackgroundView];
+ [self snapshotVerifyView:snapshotView];
+
+ [thumbViewFrameView removeFromSuperview];
+ [thumbTrackFrameView removeFromSuperview];
+}
+
+#pragma mark - Tests
+
+- (void)testThumbTrackAtMidpoint {
+ // When
+ self.thumbTrack.value = self.thumbTrack.minimumValue +
+ (self.thumbTrack.maximumValue - self.thumbTrack.minimumValue) / 2;
+
+ // Then
+ [self generateSnapshotWithBorderAndVerifyForView:self.thumbTrack];
+}
+
+- (void)testThumbTrackAtMinimum {
+ // When
+ self.thumbTrack.value = self.thumbTrack.minimumValue;
+
+ // Then
+ [self generateSnapshotWithBorderAndVerifyForView:self.thumbTrack];
+}
+
+- (void)testThumbTrackAtMaximum {
+ // When
+ self.thumbTrack.value = self.thumbTrack.maximumValue;
+
+ // Then
+ [self generateSnapshotWithBorderAndVerifyForView:self.thumbTrack];
+}
+
+@end
diff --git a/components/private/ThumbTrack/tests/snapshot/MDCThumbViewSnapshotTests.m b/components/private/ThumbTrack/tests/snapshot/MDCThumbViewSnapshotTests.m
new file mode 100644
index 00000000000..4c80692fb8c
--- /dev/null
+++ b/components/private/ThumbTrack/tests/snapshot/MDCThumbViewSnapshotTests.m
@@ -0,0 +1,77 @@
+// 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
+
+#import "MaterialSnapshot.h"
+#import "MaterialThumbTrack.h"
+
+@interface MDCThumbViewSnapshotTests : MDCSnapshotTestCase
+
+@property(nonatomic, strong) MDCThumbView *thumbView;
+
+@end
+
+@implementation MDCThumbViewSnapshotTests
+
+- (void)setUp {
+ [super setUp];
+
+ // Uncomment below to recreate all the goldens (or add the following line to the specific
+ // test you wish to recreate the golden for).
+ // self.recordMode = YES;
+
+ self.thumbView = [[MDCThumbView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
+ self.thumbView.backgroundColor = UIColor.blueColor;
+}
+
+- (void)tearDown {
+ self.thumbView = nil;
+
+ [super tearDown];
+}
+
+- (void)generateSnapshotWithBorderAndVerifyForView:(MDCThumbView *)view {
+ // TODO(b/161927075): Refactor when this functionality is available in MaterialSnapshot.
+ UIView *thumbViewFrameView = [[UIView alloc] initWithFrame:view.bounds];
+ [view addSubview:thumbViewFrameView];
+ thumbViewFrameView.layer.borderColor = UIColor.yellowColor.CGColor;
+ thumbViewFrameView.layer.borderWidth = 0.5f;
+
+ UIView *snapshotView = [view mdc_addToBackgroundView];
+ [self snapshotVerifyView:snapshotView];
+
+ [thumbViewFrameView removeFromSuperview];
+}
+
+#pragma mark - Tests
+
+- (void)testThumbViewFullyRoundedDefaults {
+ // When
+ self.thumbView.cornerRadius = CGRectGetWidth(self.thumbView.bounds) / 2;
+
+ // Then
+ [self generateSnapshotWithBorderAndVerifyForView:self.thumbView];
+}
+
+- (void)testThumbViewWhenCenterVisibleAreaIsTrue {
+ // When
+ self.thumbView.centerVisibleArea = YES;
+ self.thumbView.cornerRadius = 10;
+
+ // Then
+ [self generateSnapshotWithBorderAndVerifyForView:self.thumbView];
+}
+
+@end
diff --git a/components/schemes/Shape/examples/MDCShapeWithBorderExample.m b/components/schemes/Shape/examples/MDCShapeWithBorderExample.m
new file mode 100644
index 00000000000..0295c0fa874
--- /dev/null
+++ b/components/schemes/Shape/examples/MDCShapeWithBorderExample.m
@@ -0,0 +1,73 @@
+// 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 "MaterialCards.h"
+#import "MaterialCards+Theming.h"
+#import "MaterialShapeLibrary.h"
+#import "MaterialShapes.h"
+
+@interface MDCShapeWithBorderExample : UIViewController
+@property(strong, nonatomic) MDCCard *card;
+@end
+
+@implementation MDCShapeWithBorderExample
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.backgroundColor = UIColor.whiteColor;
+ self.card = [[MDCCard alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
+ self.card.center = self.view.center;
+ self.card.backgroundColor = UIColor.blueColor;
+
+ MDCRectangleShapeGenerator *rectangleShape = [[MDCRectangleShapeGenerator alloc] init];
+ [rectangleShape setCorners:[MDCCornerTreatment cornerWithRadius:8]];
+ self.card.shapeGenerator = rectangleShape;
+ [self.card setBorderColor:UIColor.blackColor forState:UIControlStateNormal];
+ [self.card setBorderColor:UIColor.blackColor forState:UIControlStateHighlighted];
+ [self.card setBorderWidth:1 forState:UIControlStateNormal];
+ [self.card setBorderWidth:4 forState:UIControlStateHighlighted];
+
+ [self.view addSubview:self.card];
+}
+
+@end
+
+#pragma mark - Catalog by convention
+@implementation MDCShapeWithBorderExample (CatlogByConvention)
+
++ (NSDictionary *)catalogMetadata {
+ return @{
+ @"breadcrumbs" : @[ @"Shape", @"Card with Border example" ],
+ @"primaryDemo" : @NO,
+ @"presentable" : @YES,
+ };
+}
+
+@end
+
+@implementation MDCShapeWithBorderExample (SnapshotTestingByConvention)
+
+- (void)testTappingMultipleTimesOnCard {
+ // When
+ self.card.highlighted = YES;
+ self.card.highlighted = YES;
+ self.card.highlighted = YES;
+ self.card.highlighted = YES;
+ self.card.highlighted = YES;
+ self.card.highlighted = YES;
+ self.card.highlighted = YES;
+}
+
+@end
diff --git a/contributing/site_content_update.md b/contributing/site_content_update.md
deleted file mode 100644
index fcc2a83e9f1..00000000000
--- a/contributing/site_content_update.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# Site Content Update
-
-## Overview
-Material Components for iOS site consists of 2 parts:
-[document site](https://material.io/components/ios/) and API reference site of each
-components (e.g,
-[AppBar API](https://material.io/components/ios/catalog/app-bars/api-docs/Classes/MDCAppBarContainerViewController.html), etc...)
-
-This document will walk you through the process for updating the contents on
-document site and API reference, or adding new sections to the document site.
-You only need to edit markdown files in most cases, however, if you wish to make
-further changes to the templates, please read to [Site
-Development](./site_development.md).
-
-
-## Updating Content
-
-### Document Site and GitHub README.md
-
-#### Syntax
-
-The Material Components site uses [Jekyll](https://jekyllrb.com/) to help
-transform Markdown files into static HTML. This means although it is consistent
-with GitHub Flavored Markdown for most cases, we do have some style classes and
-special javascript to handle complicate rendering for the website. Please refer
-to [Writing READMES](./writing_readmes.md) for the syntax we use.
-
-#### Structure
-
-The document site and GitHub README.md have the exact 1:1 mapping structure, except for the homepage.
-
-- homepage -> site-index.md
-- docs -> docs/README.md
-- docs/[tutorial_name] -> docs/[tutorial_name]/README.md
-- components -> components/README.md
-- components/[component_name] -> components/[component_name]/README.md
-- contributing -> contributing/README.md
-
-#### Add new sections
-
-If you are going to add new sections to the website, you need to modify the configuration file, so that the build system will be aware of the modified structure during build process.
-
-To set it up, run
-
-```
-scripts/build_site.sh --setup
-
-# Modify the navigation data file
-vim site-source/jekyll-site-src/_data/navigation.yaml
-```
-
-In this case, you should change your working directory into `site-source` and run any git commands in that folder.
-
-```
-cd site-source
-# and do git commit & push here...
-```
-
-This relates to how the site is organized. If you are curious about that, you may read the appendix at the end of [Site Development](./site_development.md).
-
-### API Reference
-
-Material Components for iOS uses [jazzy](https://github.com/realm/jazzy) to transform the inline comments in the header files and make it into API reference document. See [their syntax](https://github.com/realm/jazzy#supported-keywords) for updating inline comments in components header files.
-
-## Build and Preview locally
-
-Run the following command and follow the instructions on the command line.
-
- scripts/build_site.sh
-
-The site should be served at [127.0.0.1:4000](http://127.0.0.1:4000) after build by default.
-
-## Deploy to production
-
-You need to be one of the Material Components for iOS core members in order to deploy the site for the moment. However, we will incorporate the changes to the site for every weekly cut release as well.
-
-If you are able to deploy the site, run
-
-```bash
-# Run these to install gsutil for the first time
-curl https://sdk.cloud.google.com | bash
-exec -l $SHELL
-#Set up the gsutil authentication information, it doesn't matter which app engine project you choose.
-gcloud init
-
-# Deploy it!
-scripts/build_site.sh --deploy production
-```
-
-Open the [Material Components for iOS](https://material.io/components/ios/) site
-and make sure your modification is there.
diff --git a/contributing/site_development.md b/contributing/site_development.md
deleted file mode 100644
index b5b97435b32..00000000000
--- a/contributing/site_development.md
+++ /dev/null
@@ -1,243 +0,0 @@
-# Site Development
-
-**This document talks about Material Components for iOS site development. If you are trying to
-updating the contents of site, please refer to [Site Content Update](./site_content_update.md)
-instead.**
-
-
-## Overview
-
-Material Components for iOS site consists of 2 parts: [document
-site](https://material.io/components/ios/) and API reference site of each components
-(e.g, [AppBar
-API](https://material.io/components/ios/catalog/app-bars/api-docs/Classes/MDCAppBarContainerViewController.html),
-etc...)
-
-This document will walk you through how you get the sources, modify and deploy the site. If you are
-curious about how the sources are organized, you may also read the [in depth
-explanation](#appendix-how) at the last of this document.
-
-
-## Get the sources of the site
-
-**If you have ever built the site (no matter run it locally or deploy to the production), you may
-skip this step.**
-
-Make sure you are under **develop** branch and have the latest code pulled. Then run the following
-script.
-
-```scripts/build_site.sh --setup```
-
-The sources of the site will be pulled under a folder called *site-source* under your root folder of
-the repository.
-
-
-## Develop the site
-
-### Develop the document site
-
-Material component for ios uses [jekyll](https://jekyllrb.com/) to help transform the markdowns into
-static HTML.
-
-The sources *site-source/jekyll-site-src* respect the [directory structure
-requirements](https://jekyllrb.com/docs/structure/) of jekyll with a few exceptions. Here is an
-overview of the directory structure for mdc:
-
-- _includes & _layout: the template used by the document site with [liquid
- tag](https://github.com/Shopify/liquid/wiki)
-
-- _sass: the style of the document site
-
- - base.scss: the basic style of the default html tags. Most of the styles respect [material
- design guideline](https://material.io/guidelines/)
-
- - _globals.scss: the variables and responsive grid definitions.
-
- - _layout: the style defined for document site. You will modify this file for most of the cases.
-
- - _layout-api: the style defined for API reference site. This is a bit confusing but API
- reference is actually built in as part of the document site and we want to use the syle we
-have for the document site.
-
- - _icons & _step-sequence & _codemirror-syntax-highlighting: These are the utility class for all
- icons, step by step guidance and code renderer for example code.
-
-- css: the css that specify which above styles should be included
-
-- _data: the definition of the navigation side bar
-
-- other assets: images, js, thirdparty
-
-Attention should be paid that *components*, *contributing*, *docs* are all copied files and will be
-override at the time when document site is built. So if you are trying to modify the content of
-these files, please read [Site Content Update](/site_content_update.md).
-
-Since the document site uses the feature of jekyll like [Front
-Matter](https://jekyllrb.com/docs/frontmatter/),
-[Configure](https://jekyllrb.com/docs/configuration/), we suggest you to read jekyll's document if
-you are going to develop the site.
-
-
-### Develop the API reference site
-
-Material component for ios uses [jazzy](https://github.com/realm/jazzy) to transform the inline
-comments in the header files and make it into API reference document.
-
-The sources *site-source/apidocs-site-src* contains the theme and assets. When you build the API
-reference, the API reference will be generated and copied to each components folder under
-*jekyll-site-src/components* and later be build as part of the document site. Because of that, if
-you are trying to modify the styling of the site, we suggest you to modify
-*site-source/jekyll-site-src/_scss/_layout-api.scss* instead of css in *theme/assets/css* folder.
-
-
-## Deploy the site
-
-### Serve the site locally
-
-Run the following command and follow the hint in the command line.
-
- scripts/build_site.sh
-
-The site should be served at [127.0.0.1:4000](http://127.0.0.1:4000) after build by default.
-
-
-### Commit the changes
-
-You should change your working directory into *site-source* and run git command in that folder
-
-```
-cd site-source
-# and do git commit here...
-```
-
-
-We recommend you to read appendix at the last of this document if you want to know how the sources
-are organized.
-
-
-### Deploy the site to production
-
-You need to be one of the Material component core members in order to deploy the site for the
-moment. However, we will incorporate the changes to the site for every weekly cut release as well.
-
-If you are able to deploy the site, run
-
-```
-# Run these to install gsutil for the first time
-curl https://sdk.cloud.google.com | bash
-exec -l $SHELL
-# Set up the gsutil authentication information, it doesn't matter which app engine project you
-# choose.
-gcloud init
-
-# Deploy it!
-scripts/build_site.sh --deploy production
-```
-
-Open [Material Component site](https://material.io/components/ios/) and make
-sure your modification is there.
-
-
-## TODO: Modify the site build script
-
-## Appendix - How are the sources organized?
-
-### Overview & Architecture Graph
-
-Material component ios repository is the place where all components and their documents live. Just
-like most of other open source projects, core developers and community members will be actively
-improving the components on master/develop branch. Meanwhile, we also want our website get those
-updates as soon as the changes are pushed and merged, but leave the process as simple as pull out a
-rabbit from the magic hat, without you switching between branches.
-
-The secret sauce we use is "locally nested structure", where contents and templates are separated in
-2 branches (master/develop and site-source) but locally nested one under the other. It is easily to
-understand with the following graph:
-
-![mdc local folder and branch structure](./mdc-local-folder-and-branch-structure.png)
-
-It is pretty clear that the site should be thought as 2 parts: the contents and the templates. In
-our case, the contents are documents of the components.
-
-As shown in the graph, the document lives together with the component it is documenting, so that
-whoever update the component will be able to quickly find the document needed to update and commit
-together with the code. On the other hand, the templates live on another branch called "site-source"
-and they describe what the website should be look like. Since we separate contents and templates
-into branches, it decouples site and components development.
-
-But then how can we let the developers preview their updates to make sure they are not breaking the
-site? The answer lies in the local folder structure. In the repository folder, we cloned another
-repository, put it in a folder called site-source and switch the branch in that folder to
-site-source. Let's actually look at the build process to see what is happening there. But before we
-dive into that, we should also re-emphasize that this folder is gitignored so git won't ever notice
-nor ever commit you local build and previews.
-
-### Build Process
-
-The build process has 2 steps:
-
-- Build the api reference
-- Build the document site
-
-Since the api reference is part of the contents of the document site, so step 1 need to be carried
-out before step 2.
-
-In step 1, we build the api reference from the inline comments of the components using
-[Jazzy](./site_content_update.md#api-reference). The api reference are output to the jekyll folder
-in preparation for step 2.
-
-In step 2, the documents sepcified in ```site-source/jekyll-site-src/_data/navigation.yaml``` are
-copied to the corresponding place and rename to index.md for servering purpose. Jekyll processes
-these files and generates static html into ```site-source/site-build```.
-
-After build, local server might run servering the static html for developer to preview or it will be
-deployed to the production server. We would like to add a git hook, so that when update get merged
-into master branch, the site will be rebuild and pushed at the same time secretly from the
-developer.
-
-
-### Intent behind this
-
-Although the structure seems hard to understand at first glance, it have several benefits once you
-get the gists.
-
-- Benefits of decoupling
-
- The components developers and site developers' work should not ever interfere with each other. We
-need to ensure in the worst scenario that if one of them need to roll back several commits, the
-other team should not even notice that happened, which obviously cannot be achieved if the contents
-and templates lives in the same branch.
-
-- Single source of truth and clear responsibility
-
- There is only a single source of the truth for both the contents and templates. While the site
-developer has the authority for styling the site as they like, they probably won't have insights of
-the detailed contents that should be put on the website, component developers vise versa. We barely
-defined the syntax as the protocol for communication, so each party can work on its own thing and
-hold their absolute authority on that.
-
-- Preserve document on github
-
- Since we only have one set of truth for content and all the other are clearly copies of them, we
-preserve the README.md in the place that github can still find them, so you can read them directly
-on github and even if you are not linked to the internet, you will be able to see all document in
-your local repository
-
-- Benefit of non-blocking
-
- The component developers should never be blocked by the site developers when updating the content,
-nor should bother the site developers to stop what they are doing and help them with merely an
-content update. In this case, we are not only separating the authority but also make it possible for
-each party to update on their own pace.
-
-- Intuitively find what you need to update
-
- The site has exactly 1:1 map onto the source that generate the page. For example, if you are
-updating the components/Button/ page, you can find the document right at
-components/Button/README.md, which is also right besides the Button.h and Button.m file
-
-- Simply works
-
- You actually don't need to understand all the structure if you are not curious. You can write the
-components, update the documents and preview, or you can update the style and see without switch
-branches. You don't need to understand all the nitty-gritty about all these, it simply works.
diff --git a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
index c8120b615d4..7a0d3b84a8d 100644
--- a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
+++ b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "RemoteImageServiceForMDCDemos"
- s.version = "111.0.0"
+ s.version = "112.0.0"
s.summary = "A helper image class for the MDC demos."
s.description = "This spec is made for use in the MDC demos. It gets images via url."
s.homepage = "https://github.com/material-components/material-components-ios"