diff --git a/CHANGELOG.md b/CHANGELOG.md index 09dad94d9ef..7c75e6e096d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +# 119.3.0 + +In this minor release we added a new method to the `MDCBaseTextFieldDelegate` protocol and fixed bugs in NavigationDrawer, TextControls, and Tabs. + +## API changes + +*new* method: `-baseTextField:didUpdateIntrinsicHeight:` in `MDCBaseTextFieldDelegate` + +## Component changes + +### BottomSheet + +* [Replace bottom sheet docs](https://github.com/material-components/material-components-ios/commit/82c03655498bd13cdf0229d71d562fed97d48813) (Andrew Overton) + +### NavigationDrawer + +* [Added `presentingViewYOffset` to `contentHeightSurplus` in `MDCBottomDrawerContainerViewController`](https://github.com/material-components/material-components-ios/commit/8ce4ebebd6877abce65e9b36123f39d97deded30) (Nobody) + +### Tabs + +* [Add call to update fonts from setItems to make sure tabBarView fonts are applied to new items.](https://github.com/material-components/material-components-ios/commit/0807c24b05bb588acede2152f2fca7d591ed0a1c) (Alyssa Weiss) +* [Add missing div tag](https://github.com/material-components/material-components-ios/commit/7de109ee8160c8853d274f9a9dfdb7d0188b465f) (Andrew Overton) +* [Fix an issue where the indicator was shown moving in, rather than already at the selected item, when the tab bar appears for the first time.](https://github.com/material-components/material-components-ios/commit/75a2ad15c78b7b5f504b6701cc0f0ce67deb1792) (Hao Sun) +* [Fix an issue where the indicator was shown moving in, rather than already at the selected item, when the tab bar items are recreated and the tab selected.](https://github.com/material-components/material-components-ios/commit/8343349fd944320eea78f3e1af50a3e0d65641c8) (Nobody) + +### TextControls + +* [Rethink sizing behavior with sizing delegate method](https://github.com/material-components/material-components-ios/commit/178fee91ff1965e75acc1c73214cb1f200598bf5) (Andrew Overton) + +## Multi-component changes + +* [Change component README ToCs to bold paragraph style](https://github.com/material-components/material-components-ios/commit/fb4d874f62a3aa5c59c54df829617e97ea9957bd) (Andrew Overton) + +--- + # 119.2.0 In this minor release we deprecated hitAreaInsets APIs in Buttons, FloatingButtons, and Chips, and introduced minor bug fixes and documentation updates. diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec index 21a7a2641ea..156cd5b580c 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 = "119.2.0" + mdc.version = "119.3.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/MaterialComponentsEarlGreyTests.podspec b/MaterialComponentsEarlGreyTests.podspec index d2c446ea4b2..2b3bd60ae6e 100644 --- a/MaterialComponentsEarlGreyTests.podspec +++ b/MaterialComponentsEarlGreyTests.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsEarlGreyTests" - s.version = "119.2.0" + s.version = "119.3.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 290ab1b2de4..ca3dbc5158a 100644 --- a/MaterialComponentsExamples.podspec +++ b/MaterialComponentsExamples.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MaterialComponentsExamples" - s.version = "119.2.0" + s.version = "119.3.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 f4f672ed3c6..b170974b749 100644 --- a/MaterialComponentsSnapshotTests.podspec +++ b/MaterialComponentsSnapshotTests.podspec @@ -53,7 +53,7 @@ end Pod::Spec.new do |s| s.name = "MaterialComponentsSnapshotTests" - s.version = "119.2.0" + s.version = "119.3.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 f5f94e41315..c2b04563bda 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -119.2.0 +119.3.0 diff --git a/catalog/MDCCatalog/Info.plist b/catalog/MDCCatalog/Info.plist index f9548f95e0a..bc10c76c81f 100644 --- a/catalog/MDCCatalog/Info.plist +++ b/catalog/MDCCatalog/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 119.2.0 + 119.3.0 CFBundleSignature ???? CFBundleVersion - 119.2.0 + 119.3.0 LSRequiresIPhoneOS UIAppFonts diff --git a/catalog/MDCDragons/Info.plist b/catalog/MDCDragons/Info.plist index 339fd394634..a804cabf07c 100644 --- a/catalog/MDCDragons/Info.plist +++ b/catalog/MDCDragons/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 119.2.0 + 119.3.0 CFBundleVersion - 119.2.0 + 119.3.0 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/catalog/MaterialCatalog/MaterialCatalog.podspec b/catalog/MaterialCatalog/MaterialCatalog.podspec index cb7e1591ab7..2bfe0fcdec7 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 = "119.2.0" + s.version = "119.3.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/AppBar/README.md b/components/AppBar/README.md index e3068bb8b3d..d95838cedb5 100644 --- a/components/AppBar/README.md +++ b/components/AppBar/README.md @@ -17,7 +17,7 @@ information and actions relating to the current screen. ![Image showing a typical top app bar](docs/assets/appbar-hero.png) -## Contents +**Contents** * [Using top app bars](#using-top-app-bars) * [Regular top app bar](#regular-top-app-bar) diff --git a/components/Banner/README.md b/components/Banner/README.md index ebd35ca9d88..6416f481861 100644 --- a/components/Banner/README.md +++ b/components/Banner/README.md @@ -16,7 +16,7 @@ A [banner](https://material.io/components/banners) displays a prominent message ![Hero image of a banner showing a transaction error message](docs/assets/banner-hero.png) -## Contents +**Contents** * [Using banners](#using-banners) * [Banner](#banner) diff --git a/components/BottomAppBar/README.md b/components/BottomAppBar/README.md index fbd68c039f2..132ace98bf0 100644 --- a/components/BottomAppBar/README.md +++ b/components/BottomAppBar/README.md @@ -16,7 +16,7 @@ api_doc_root: true ![Bottom app bar hero](docs/assets/bottom-app-bar-hero.png) -## Contents +**Contents** * [Using bottom app bars](#using-bottom-app-bars) * [Bottom app bar](#bottom-app-bar) diff --git a/components/BottomNavigation/README.md b/components/BottomNavigation/README.md index 7248bae1101..1e5a6d0965f 100644 --- a/components/BottomNavigation/README.md +++ b/components/BottomNavigation/README.md @@ -17,7 +17,7 @@ navigation icon takes you directly to the associated view or refreshes the curre ![A generic bottom navigation with the "music" item selected](docs/assets/bottom-nav-hero.png) -## Contents +**Contents** * [Using bottom navigation](#using-bottom-navigation) * [Bottom navigation bar](#bottom-navigation-bar) diff --git a/components/BottomSheet/README.md b/components/BottomSheet/README.md index a804ca401bc..3dc6952a970 100644 --- a/components/BottomSheet/README.md +++ b/components/BottomSheet/README.md @@ -8,65 +8,99 @@ path: /catalog/bottom-sheet/ api_doc_root: true --> - - # Sheets: bottom -Bottom sheets slide up from the bottom of the screen to reveal more content. Bottom sheets integrate with the app to display supporting content or present deep-linked content from other apps. +[Bottom sheets](https://material.io/components/sheets-bottom) are surfaces containing supplementary content that are anchored to the bottom of the screen. -
- Bottom Sheet -
+![Bottom sheet with 4 options.](docs/assets/bottom-sheet-hero.png) -## Design & API Documentation +**Contents** - +* [Using bottom sheets](#using-bottom-sheets) +* [Standard bottom sheet](#standard-bottom-sheet) +* [Modal bottom sheet](#modal-bottom-sheet) +* [Expanding bottom sheet](#expanding-bottom-sheet) +* [Theming](#theming) - - - -## Installation +## Using bottom sheets + +Bottom sheets are supplementary surfaces primarily used on mobile. -### Installation with CocoaPods +### Installing -To add this component to your Xcode project using CocoaPods, add the following to your `Podfile`: +In order to install with [Cocoapods](https://guides.cocoapods.org/using/getting-started.html), first add the component to your `Podfile`: ```bash pod 'MaterialComponents/BottomSheet' ``` - -Then, run the following command: +Then run the installer: ```bash pod install ``` -- - - - -## Usage - -### Importing - -Before using Bottom Sheet, you'll need to import it: +From there, import the relevant target or file. + #### Swift ```swift import MaterialComponents.MaterialBottomSheet ``` #### Objective-C - ```objc #import "MaterialBottomSheet.h" ``` -## Examples +### Making bottom sheets accessible + +As a user of the bottom sheet component, it is up to you to determine that its contents are accessible. The bottom sheet ccomponent does not have any specific APIs for managing the accessibility of a bottom sheet's contents. `MDCBottomSheetController` does, however, have such APIs for the scrim: + +* `isScrimAccessibilityElement` +* `scrimAccessibilityLabel` +* `scrimAccessibilityHint` +* `scrimAccessibilityTraits` + +We recommend giving all of these properties appropriate values for your use case. + +## Types + +There are three types suitable for different use cases: +1. [Standard bottom sheets](#standard-bottom-sheet) display content that complements the screen’s primary content and remain visible while users interact with the primary content +1. [Modal bottom sheets](#modal-bottom-sheet) are an alternative to inline menus or simple dialogs on mobile and provide room for additional items, longer descriptions, and iconography, and must be dismissed in order to interact with the underlying content +1. [Expanding bottom sheets](#expanding-bottom-sheet) provide a small, collapsed surface that can be expanded by the user to access a key feature or task to offer the persistent access of a standard sheet with the space and focus of a modal sheet. + +![Composite image of bottom sheet types](docs/assets/bottom-sheet-types.png) + +## Standard bottom sheet + +Standard bottom sheets coexist with the screen’s main UI region and allow for simultaneously viewing and interacting with both regions. They are commonly used to keep a feature or secondary content visible on screen when content in main UI region is frequently scrolled or panned. + +There is no standard bottom sheet implementation on iOS. This is because the iOS bottom sheet implementation makes use of custom view controller transitions, which do not allow interaction with the presenting view controller, even when the presented view controller does not take up the whole screen. + +## Modal bottom sheet -Create a view controller that the bottom sheet will hold and initialize the bottom sheet with that view controller. After the bottom sheet is created, it is ready to be presented on the current view controller. +Modal bottom sheets present a set of choices while blocking interaction with the rest of the screen. They are an alternative to inline menus and simple dialogs on mobile, providing additional room for content, iconography, and actions. + +Modal bottom sheets are used in mobile apps only. + +### Modal bottom sheet examples + +#### Basic modal sheet example + +Use `MDCBottomSheetController` and its accompanying presentation controller class, `MDCBottomSheetPresentationController`, to achieve a modal bottom sheet on iOS. + +* [`MDCBottomSheetController`GitHub source](https://github.com/material-components/material-components-ios/blob/develop/components/BottomSheet/src/MDCBottomSheetController.h) +* [`MDCBottomSheetPresentationController` GitHub source](https://github.com/material-components/material-components-ios/blob/develop/components/BottomSheet/src/MDCBottomSheetPresentationController.h) + +![Bottom sheet with grey slider on top of a grey background](docs/assets/modal-bottom-sheet.png) + +Something like the above example can be achieved using the code below. #### Swift @@ -75,6 +109,7 @@ Create a view controller that the bottom sheet will hold and initialize the bott let viewController: ViewController = ViewController() // Initialize the bottom sheet with the view controller just created let bottomSheet: MDCBottomSheetController = MDCBottomSheetController(contentViewController: viewController) +// At this point perform any customizations, like adding a slider, for example. // Present the bottom sheet present(bottomSheet, animated: true, completion: nil) ``` @@ -85,31 +120,14 @@ present(bottomSheet, animated: true, completion: nil) ViewController *viewController = [[ViewController alloc] init]; // Initialize the bottom sheet with the view controller just created MDCBottomSheetController *bottomSheet = [[MDCBottomSheetController alloc] initWithContentViewController:viewController]; +// At this point perform any customizations, like adding a slider, for example. // Present the bottom sheet [self presentViewController:bottomSheet animated:true completion:nil]; ``` - +#### Behavioral customizations -Create a button that will call the code above. - - -#### Swift -```swift -let button = UIButton(frame: .zero) -button.addTarget(self, action: #selector(presentBottomSheet), for: .touchUpInside) -``` - -#### Objective-C -```objc -_button = [[UIButton alloc] initWithFrame:CGRectZero]; -[_button addTarget:self action:@selector(presentBottomSheet) forControlEvents:UIControlEventTouchUpInside]; -``` - - -### Behavioral Customizations - -You can also choose to have your bottom sheet not be dismissable when dragged downwards by using the dismissOnDraggingDownSheet property on MDCBottomSheetController. +You can also choose to have your bottom sheet not be dismissable when dragged downwards by using the `dismissOnDraggingDownSheet` property on `MDCBottomSheetController`. #### Swift @@ -133,21 +151,94 @@ bottomSheet.dismissOnDraggingDownSheet = NO; ``` +### Anatomy and key properties + +The following shows the anatomy of a modal bottom sheet: + +![Modal bottom sheet anatomy](docs/assets/modal-bottom-sheet-anatomy.png) + +1. Sheet +2. Contents +3. Scrim + +_**Note: A bottom sheet similar to the one shown above is easily attainable with the [ActionSheet](https://github.com/material-components/material-components-ios/tree/develop/components/ActionSheet) component, which makes use of `MDCBottomSheetPresentationController`.**_ + +#### Sheet properties + +  | **Attribute** | **Related methods** | **Default value** +----------------------| --------------------------| ------------------------------------------------------ | ----------------- +**Sheet height** | `preferredSheetHeight` | `-[MDCBottomSheetPresentationController setPreferredSheetHeight:]`
`-[MDCBottomSheetPresentationController preferredSheetHeight]` | N/A +**Elevation** | `elevation` | `-[MDCBottomSheetPresentationController setElevation:]`
`-[MDCBottomSheetPresentationController elevation]`| 16 + +#### Contents properties -## Extensions +  | **Attribute** | **Related methods** | **Default value** +----------------------| --------------------------| ------------------------------------------------------ | ----------------- +**Contents** | `contentViewController` | `-[MDCBottomSheetController initWithContentViewController:]`
`-[MDCBottomSheetController contentViewController]` | N/A +**Elevation** | `elevation` | `-[MDCBottomSheetPresentationController setElevation:]`
`-[MDCBottomSheetPresentationController elevation]`| 16 +**Title text** | `title` | `-[MDCActionSheetComtroller setTitle:]`
`-[MDCActionSheetComtroller title]` | `nil` +**Message text** | `message` | `-[MDCActionSheetComtroller setMessage:]`
`-[MDCActionSheetComtroller message]` | `nil` +**Title font** | `titleFont` | `-[MDCActionSheetComtroller setTitleFont:]`
`-[MDCActionSheetComtroller titleFont]` | `nil` +**Message font** | `messageFont` | `-[MDCActionSheetComtroller setMessageFont:]`
`-[MDCActionSheetComtroller messageFont]` | `nil` +**Action font** | `actionFont` | `-[MDCActionSheetComtroller setActionFont:]`
`-[MDCActionSheetComtroller actionFont]` | `nil` +**Ripple color** | `rippleColor` | `-[MDCActionSheetComtroller setRippleColor:]`
`-[MDCActionSheetComtroller rippleColor]` | `nil` +**Background color** | `backgroundColor` | `-[MDCActionSheetComtroller -setBackgroundColor:]`
`-[MDCActionSheetComtroller backgroundColor]` | `nil` +**Title text color** | `titleTextColor` | `-[MDCActionSheetComtroller -setTitleTextColor:]`
`-[MDCActionSheetComtroller titleTextColor]` | `nil` +**Message text color** | `messageTextColor` | `-[MDCActionSheetComtroller -setMessageTextColor:]`
`-[MDCActionSheetComtroller messageTextColor]` | `nil` +**Action text color** | `actionTextColor` | `-[MDCActionSheetComtroller -setActionTextColor:]`
`-[MDCActionSheetComtroller actionTextColor]` | `nil` +**Action tint color** | `actionTintColor` | `-[MDCActionSheetComtroller -setActionTintColor:]`
`-[MDCActionSheetComtroller actionTintColor]` | `nil` - +#### Scrim properties -### Shape Theming +  | **Attribute** | **Related methods** | **Default value** +----------------------| --------------------------| ------------------------------------------------------ | ----------------- +**Color** | `scrimColor` | `-[MDCBottomSheetPresentationController setScrimColor:]`
`-[MDCBottomSheetPresentationController scrimColor]` | White at 40% opacity -You can theme a bottom sheet with your app's shape scheme using the ShapeThemer extension. +## Expanding bottom sheet -You must first add the ShapeThemer extension to your project: +An expanding bottom sheet is a surface anchored to the bottom of the screen that users can expand to access a feature or task. It can be used for: + +* **Persistently displaying a cross-app feature**, such as a shopping cart +* **Collecting and acting on user selections** from a set of items, such as photos in a gallery +* **Supporting tasks**, such as chat and comments +* **Indirect navigation between items**, such as videos in a playlist + +Expanding bottom sheets are recommended for use on mobile and tablet. + +### Expanding bottom sheet example + +To generate an expanding bottom sheet on iOS, set the `trackingScrollView` property on your `MDCBottomSheetController`. If the `contentSize` of the scroll view has a large enough height the bottom sheet will expand to the top. + + +#### Swift +```swift +bottomSheet.trackingScrollView = scrollView +``` + +#### Objective-C +```objc +bottomSheet.trackingScrollView = self.scrollView; +``` + + +## Theming + +Unlike most Material components on iOS, bottom sheets do not offer theming with a container scheme. However, `MDCBottomSheetController` does have a shape themer. In order to use the shape themer, first add the following to your `Podfile`: ```bash pod 'MaterialComponents/BottomSheet+ShapeThemer' ``` + + +Then run the installer: + +```bash +pod install +``` + +Next, import the relevant taret or file and call the correct theming method. + #### Swift ```swift @@ -174,5 +265,4 @@ id shapeScheme = [[MDCShapeScheme alloc] init]; [MDCBottomSheetControllerShapeThemer applyShapeScheme:shapeScheme toBottomSheetController:component]; ``` - - + \ No newline at end of file diff --git a/components/BottomSheet/docs/assets/bottom-sheet-hero.png b/components/BottomSheet/docs/assets/bottom-sheet-hero.png new file mode 100644 index 00000000000..2a1a4c43753 Binary files /dev/null and b/components/BottomSheet/docs/assets/bottom-sheet-hero.png differ diff --git a/components/BottomSheet/docs/assets/bottom-sheet-types.png b/components/BottomSheet/docs/assets/bottom-sheet-types.png new file mode 100644 index 00000000000..1c2a94b6044 Binary files /dev/null and b/components/BottomSheet/docs/assets/bottom-sheet-types.png differ diff --git a/components/BottomSheet/docs/assets/modal-bottom-sheet-anatomy.png b/components/BottomSheet/docs/assets/modal-bottom-sheet-anatomy.png new file mode 100644 index 00000000000..cab92e70d0c Binary files /dev/null and b/components/BottomSheet/docs/assets/modal-bottom-sheet-anatomy.png differ diff --git a/components/BottomSheet/docs/assets/modal-bottom-sheet.png b/components/BottomSheet/docs/assets/modal-bottom-sheet.png new file mode 100644 index 00000000000..a5ba28d1768 Binary files /dev/null and b/components/BottomSheet/docs/assets/modal-bottom-sheet.png differ diff --git a/components/Buttons/docs/buttons.md b/components/Buttons/docs/buttons.md index 57fdc4725e4..2ed4efc9ffc 100644 --- a/components/Buttons/docs/buttons.md +++ b/components/Buttons/docs/buttons.md @@ -14,7 +14,7 @@ api_doc_root: true !["Button on a screen"](assets/buttons_hero.png) -## Contents +**Contents** * [Using buttons](#using-buttons) * [Text button](#text-button) diff --git a/components/Buttons/docs/fabs.md b/components/Buttons/docs/fabs.md index 3ad58a468cd..499671b22c2 100644 --- a/components/Buttons/docs/fabs.md +++ b/components/Buttons/docs/fabs.md @@ -14,7 +14,7 @@ A floating action button (FAB) represents the primary action of a screen. !["A FAB on a screen"](assets/fab-hero.png) -## Contents +**Contents** * [Using FABs](#using-fabs) * [Regular FAB](#regular-fab) diff --git a/components/Cards/README.md b/components/Cards/README.md index 0073df83f79..e6239dd7d0d 100644 --- a/components/Cards/README.md +++ b/components/Cards/README.md @@ -17,7 +17,7 @@ a single subject. ![An arrangement of cards containing various charts and graphs](docs/assets/card-hero.png) -## Contents +**Contents** * [Using cards](#using-cards) * [Card](#card) diff --git a/components/Chips/README.md b/components/Chips/README.md index 47f5bbd4085..55a47d040c1 100644 --- a/components/Chips/README.md +++ b/components/Chips/README.md @@ -18,7 +18,7 @@ api_doc_root: true ![Chips hero image](docs/assets/chips-hero.png) -## Contents +**Contents** * [Using chips](#using-chips) * [Input chip](#input-chip) diff --git a/components/Dialogs/README.md b/components/Dialogs/README.md index e1f3a5e8b7e..f2f6523fe10 100644 --- a/components/Dialogs/README.md +++ b/components/Dialogs/README.md @@ -19,7 +19,7 @@ involve multiple tasks. !["Dialog confirming settings centered in a screen"](docs/assets/dialogs-hero.png) -## Contents +**Contents** * [Using dialogs](#using-dialogs) * [Alert dialog](#alert-dialog) diff --git a/components/LibraryInfo/src/MDCLibraryInfo.m b/components/LibraryInfo/src/MDCLibraryInfo.m index c419e8294f7..2c1afb58aff 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 = @"119.2.0"; +static NSString* const kMDCLibraryInfoVersionString = @"119.3.0"; @implementation MDCLibraryInfo diff --git a/components/LibraryInfo/tests/unit/LibraryInfoTests.m b/components/LibraryInfo/tests/unit/LibraryInfoTests.m index 805e3f42ca9..75e451b9fcd 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: "119.2.0", etc. + // Accept: "119.3.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/List/README.md b/components/List/README.md index e1d7108f823..dfe5dc252c5 100644 --- a/components/List/README.md +++ b/components/List/README.md @@ -14,7 +14,7 @@ api_doc_root: true [Lists](https://material.io/components/lists/) are continuous, vertical indexes of text or images. -## Contents +**Contents** * [Using lists](#using-lists) * [Single-line list](#single-line-list) diff --git a/components/NavigationDrawer/README.md b/components/NavigationDrawer/README.md index 59111489195..52c50795d89 100644 --- a/components/NavigationDrawer/README.md +++ b/components/NavigationDrawer/README.md @@ -15,7 +15,7 @@ api_doc_root: true ![Bottom navigation example](docs/assets/bottom-drawer-hero.png) -## Contents +**Contents** * [Using navigation drawers](#using-navigation-drawers) * [Bottom navigation drawer](#bottom-navigation-drawer) diff --git a/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m b/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m index f61bde81a7c..c22fa91f081 100644 --- a/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m +++ b/components/NavigationDrawer/src/private/MDCBottomDrawerContainerViewController.m @@ -1091,6 +1091,11 @@ - (void)cacheLayoutCalculationsWithAddedContentHeight:(CGFloat)addedContentHeigh CGFloat scrollingDistance = _contentHeaderTopInset + totalHeight; _contentHeightSurplus = scrollingDistance - containerHeight; + // readded `presentingViewYOffset` because offset is cancelled out + // by being included in both the `scrollingDistance` and `containerHeight`. + // Because `containerHeight` is used in independent calculations + // we can't remove the offset from there. + _contentHeightSurplus += self.presentingViewYOffset; if ([self shouldPresentFullScreen]) { self.drawerState = MDCBottomDrawerStateFullScreen; } else if (_contentHeightSurplus <= 0) { diff --git a/components/NavigationDrawer/tests/unit/MDCNavigationDrawerTest.m b/components/NavigationDrawer/tests/unit/MDCNavigationDrawerTest.m index feac2aa70af..2d799e16e83 100644 --- a/components/NavigationDrawer/tests/unit/MDCNavigationDrawerTest.m +++ b/components/NavigationDrawer/tests/unit/MDCNavigationDrawerTest.m @@ -20,6 +20,7 @@ #import "MDCNavigationDrawerFakes.h" @interface MDCBottomDrawerContainerViewController (MDCBottomDrawerHeaderTesting) +@property(nonatomic) CGFloat contentHeightSurplus; - (void)updateViewWithContentOffset:(CGPoint)contentOffset; @end @@ -288,4 +289,37 @@ - (void)testSettingShouldDismissOnAccessibilityPerformEscape { XCTAssertFalse([self.navigationDrawer accessibilityPerformEscape]); } +- (void)testContentHeightSurplusIsAddingYOffset { + // Given + self.navigationDrawer.headerViewController.preferredContentSize = CGSizeMake(100, 200); + self.navigationDrawer.contentViewController.preferredContentSize = CGSizeMake(100, 200); + [self.navigationDrawer.presentationController presentationTransitionWillBegin]; + // This will be used to assert the `contentHeightSurplus` The height surplus is calculated by + // adding the content height and the header height. We then add the `presentingViewYOffset`. + // The offset is calculated by subtracting the frame height with the presenting bounds height. + // The presenting view bounds here is 0 and the content and header height get cancelled out in + // the surplus calculation. So the surplus ends up being the frame height + CGFloat frameHeight = CGRectGetHeight(self.navigationDrawer.view.frame); + + // When + self.navigationDrawer.maximumDrawerHeight = 0; + + // Then + XCTAssertEqual(self.navigationDrawer.maximumDrawerHeight, 0); + if ([self.navigationDrawer.presentationController + isKindOfClass:[MDCBottomDrawerPresentationController class]]) { + MDCBottomDrawerPresentationController *presentationController = + (MDCBottomDrawerPresentationController *)self.navigationDrawer.presentationController; + XCTAssertEqual(presentationController.maximumDrawerHeight, 0); + XCTAssertEqual(presentationController.bottomDrawerContainerViewController.maximumDrawerHeight, + 0); + XCTAssertEqual(presentationController.bottomDrawerContainerViewController.contentHeightSurplus, + frameHeight); + } else { + XCTFail(@"The presentation controller should be class of kind " + @"MDCBottomDrawerPresentationController but is %@", + self.navigationDrawer.presentationController.class); + } +} + @end diff --git a/components/Snackbar/README.md b/components/Snackbar/README.md index d72a088599d..9937a0b8b0f 100644 --- a/components/Snackbar/README.md +++ b/components/Snackbar/README.md @@ -15,7 +15,7 @@ path: /catalog/Snackbars/ ![Snackbars hero image](docs/assets/snackbars-hero.png) -## Contents +**Contents** * [Using snackbars](#using-snackbars) * [Snackbar](#snackbar) @@ -153,4 +153,4 @@ The following is an anatomy diagram of a snackbar: ## Theming -Snacksbars on iOS do not support theming. \ No newline at end of file +Snacksbars on iOS do not support theming. diff --git a/components/Tabs/README.md b/components/Tabs/README.md index b6936869c28..64cd880c849 100644 --- a/components/Tabs/README.md +++ b/components/Tabs/README.md @@ -16,7 +16,7 @@ api_doc_root: true ![Generic tab bar with numbered tabs](docs/assets/tabs-generic-hero.png) -## Contents +**Contents** * [Using tabs](#using-tabs) * [Fixed tabs](#fixed-tabs) @@ -207,6 +207,7 @@ MDCContainerScheme *containerScheme = [[MDCContainerScheme alloc] init]; // Or Surface Theme [self.tabBar applySurfaceThemeWithScheme:containerScheme]; ``` + ## Migrating from legacy tabs @@ -229,7 +230,7 @@ and its APIs present equivalent information. To set the image tint colors, use `- setImageTintColor:forState:`. The `MDCTabBar` APIs, `- selectedItemTintColor` and `- unselectedItemTintColor` are -unavailable. +unavailable. To set the fonts of the labels, use `- setTitleFont:forState:`. The `MDCTabBar` APIs, `- selectedItemTitleFont` and `- unselectedItemTitleFont` diff --git a/components/Tabs/src/TabBarView/MDCTabBarView.h b/components/Tabs/src/TabBarView/MDCTabBarView.h index 0470dd2958d..91530e1d3f6 100644 --- a/components/Tabs/src/TabBarView/MDCTabBarView.h +++ b/components/Tabs/src/TabBarView/MDCTabBarView.h @@ -226,7 +226,7 @@ __attribute__((objc_subclassing_restricted)) @interface MDCTabBarView : UIScroll * {.top = 8, .right = 16, .bottom = 8, .left = 16} for text only, * {.top = 12, .right = 16, .bottom = 12, .left = 16} for image only, and * {.top = 12, .right = 16, .bottom = 12, .left = 16} for text and image. Setting this property - * overrides all three defualts. This property is only used when the tab bar view's items are @c + * overrides all three defaults. This property is only used when the tab bar view's items are @c * UITabBarItems and not @c MDCTabBarItems, or any other custom @c UITabBarItem subclasses that * conform to @c MDCTabBarItemCustomViewing. */ diff --git a/components/Tabs/src/TabBarView/MDCTabBarView.m b/components/Tabs/src/TabBarView/MDCTabBarView.m index 1c97f6556f5..33ed66e366e 100644 --- a/components/Tabs/src/TabBarView/MDCTabBarView.m +++ b/components/Tabs/src/TabBarView/MDCTabBarView.m @@ -331,8 +331,9 @@ - (void)setItems:(NSArray *)items { newSelectedItem = self.selectedItem; } - self.selectedItem = newSelectedItem; + [self setSelectedItem:newSelectedItem animated:NO]; [self addObserversToTabBarItems]; + [self updateTitleFontForAllViews]; [self invalidateIntrinsicContentSize]; [self setNeedsLayout]; @@ -768,7 +769,7 @@ - (void)layoutSubviews { } self.contentSize = [self calculatedContentSize]; - [self updateSelectionIndicatorToIndex:[self.items indexOfObject:self.selectedItem]]; + [self updateSelectionIndicatorToIndex:[self.items indexOfObject:self.selectedItem] animated:NO]; if (self.needsScrollToSelectedItem) { self.needsScrollToSelectedItem = NO; @@ -1315,9 +1316,8 @@ - (void)didReleaseTapOnTabBarItem:(UITabBarItem *)item { } } -/// Sets _selectionIndicator's bounds and center to display under the item at the given index with -/// no animation. May be called from an animation block to animate the transition. -- (void)updateSelectionIndicatorToIndex:(NSUInteger)index { +/// Sets _selectionIndicator's bounds and center to display under the item at the given index. +- (void)updateSelectionIndicatorToIndex:(NSUInteger)index animated:(BOOL)animated { if (index == NSNotFound || index >= self.items.count) { // Hide selection indicator. self.selectionIndicatorView.bounds = CGRectZero; @@ -1358,7 +1358,14 @@ - (void)updateSelectionIndicatorToIndex:(NSUInteger)index { [template indicatorAttributesForContext:context]; // Update the selection indicator. - [self.selectionIndicatorView applySelectionIndicatorAttributes:indicatorAttributes]; + if (animated) { + [self.selectionIndicatorView applySelectionIndicatorAttributes:indicatorAttributes]; + } else { + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [self.selectionIndicatorView applySelectionIndicatorAttributes:indicatorAttributes]; + [CATransaction commit]; + } } /** @@ -1372,7 +1379,7 @@ - (void)didSelectItemAtIndex:(NSUInteger)index animateTransition:(BOOL)animate { void (^animationBlock)(void) = ^{ [self updateImageTintColorForAllViews]; [self updateTitleFontForAllViews]; - [self updateSelectionIndicatorToIndex:index]; + [self updateSelectionIndicatorToIndex:index animated:animate]; // Force layout so any changes to the selection indicator are captured by the animation block. [self.selectionIndicatorView layoutIfNeeded]; diff --git a/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m b/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m index 5b82c39e1e2..a5570f77917 100644 --- a/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m +++ b/components/Tabs/tests/unit/TabBarView/MDCTabBarViewTests.m @@ -522,6 +522,36 @@ - (void)assertTitleFontForState:(UIControlState)state equalsFont:(UIFont *)font statefulTitleFont, font); } +- (void)testTitleFontSetsFontForItems { + // Given + UIFont *fakeFont = [UIFont systemFontOfSize:25]; + + // When + self.tabBarView.items = @[ self.itemA, self.itemB ]; + [self.tabBarView setTitleFont:fakeFont forState:UIControlStateNormal]; + + // Then + for (int i = 0; i < (int)[self.tabBarView.items count]; i++) { + MDCTabBarViewItemView *itemView = (MDCTabBarViewItemView *)self.tabBarView.itemViews[i]; + XCTAssertTrue([itemView.titleLabel.font isEqual:fakeFont]); + } +} + +- (void)testTitleFontSetsFontForItemsAddedAfterFontSet { + // Given + UIFont *fakeFont = [UIFont systemFontOfSize:25]; + + // When + [self.tabBarView setTitleFont:fakeFont forState:UIControlStateNormal]; + self.tabBarView.items = @[ self.itemA, self.itemB ]; + + // Then + for (int i = 0; i < (int)[self.tabBarView.items count]; i++) { + MDCTabBarViewItemView *itemView = (MDCTabBarViewItemView *)self.tabBarView.itemViews[i]; + XCTAssertTrue([itemView.titleLabel.font isEqual:fakeFont]); + } +} + - (void)testContentPaddingAddedToContentSizeForScrollableLayout { // Given self.tabBarView.preferredLayoutStyle = MDCTabBarViewLayoutStyleScrollable; diff --git a/components/TextControls/README.md b/components/TextControls/README.md index 0055140df25..5d12c51436e 100644 --- a/components/TextControls/README.md +++ b/components/TextControls/README.md @@ -14,7 +14,7 @@ edit text. !["A screen with several text fields arranged vertically."](docs/assets/textfields-hero.png) -## Contents +**Contents** * [Using text fields](#using-text-fields) * [Filled text field](#filled-text-field) diff --git a/components/TextControls/src/BaseTextFields/MDCBaseTextField.m b/components/TextControls/src/BaseTextFields/MDCBaseTextField.m index 7e434e587d5..6a65654bb3d 100644 --- a/components/TextControls/src/BaseTextFields/MDCBaseTextField.m +++ b/components/TextControls/src/BaseTextFields/MDCBaseTextField.m @@ -34,7 +34,8 @@ @interface MDCBaseTextField () @property(nonatomic, assign) MDCTextControlLabelPosition labelPosition; @property(nonatomic, assign) CGRect labelFrame; @property(nonatomic, assign) NSTimeInterval animationDuration; -@property(nonatomic, assign) CGSize cachedIntrinsicContentSize; +@property(nonatomic, assign) CGFloat lastRecordedWidth; +@property(nonatomic, assign) CGFloat lastCalculatedHeight; /** This property maps MDCTextControlStates as NSNumbers to @@ -146,8 +147,7 @@ - (CGSize)sizeThatFits:(CGSize)size { } - (CGSize)intrinsicContentSize { - self.cachedIntrinsicContentSize = [self preferredSizeWithWidth:CGRectGetWidth(self.bounds)]; - return self.cachedIntrinsicContentSize; + return [self preferredSizeWithWidth:CGRectGetWidth(self.bounds)]; } - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { @@ -160,6 +160,19 @@ - (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentA [self setNeedsLayout]; } +- (void)setBounds:(CGRect)bounds { + [super setBounds:bounds]; + + // I wouldn't think something like the following is necessary, but in light of b/173646979 I think + // it is. + CGFloat newWidth = CGRectGetWidth(bounds); + BOOL widthHasChanged = newWidth != self.lastRecordedWidth; + if (widthHasChanged) { + [self setNeedsLayout]; + } + self.lastRecordedWidth = CGRectGetWidth(bounds); +} + #pragma mark Layout /** @@ -171,9 +184,6 @@ - (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentA -layoutSubviews in the layout cycle. */ - (void)preLayoutSubviews { - if (![self validateWidth]) { - [self invalidateIntrinsicContentSize]; - } self.textControlState = [self determineCurrentTextControlState]; self.labelPosition = [self determineCurrentLabelPosition]; MDCTextControlColorViewModel *colorViewModel = @@ -192,9 +202,19 @@ - (void)postLayoutSubviews { [self animateLabel]; [self updateSideViews]; [self.containerStyle applyStyleToTextControl:self animationDuration:self.animationDuration]; - if (![self validateHeight]) { + [self updateCalculatedHeight]; +} + +- (void)updateCalculatedHeight { + if (self.layout.calculatedHeight != self.lastCalculatedHeight) { [self invalidateIntrinsicContentSize]; + if ([self.baseTextFieldDelegate respondsToSelector:@selector(baseTextField: + didUpdateIntrinsicHeight:)]) { + [self.baseTextFieldDelegate baseTextField:self + didUpdateIntrinsicHeight:self.layout.calculatedHeight]; + } } + self.lastCalculatedHeight = self.layout.calculatedHeight; } - (void)updateSideViews { @@ -332,14 +352,6 @@ - (CGSize)preferredSizeWithWidth:(CGFloat)width { return CGSizeMake(width, layout.calculatedHeight); } -- (BOOL)validateWidth { - return CGRectGetWidth(self.bounds) == self.cachedIntrinsicContentSize.width; -} - -- (BOOL)validateHeight { - return self.layout.calculatedHeight == self.cachedIntrinsicContentSize.height; -} - - (BOOL)shouldLayoutForRTL { if (self.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft) { return YES; @@ -463,7 +475,7 @@ - (void)setContainerStyle:(id)containerStyle { } - (CGRect)containerFrame { - return CGRectMake(0, 0, CGRectGetWidth(self.frame), self.layout.containerHeight); + return CGRectMake(0, 0, CGRectGetWidth(self.bounds), self.layout.containerHeight); } - (CGFloat)numberOfLinesOfVisibleText { @@ -791,7 +803,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath for (NSString *assistiveLabelKeyPath in [MDCBaseTextField assistiveLabelKVOKeyPaths]) { if ([assistiveLabelKeyPath isEqualToString:keyPath]) { - [self invalidateIntrinsicContentSize]; [self setNeedsLayout]; break; } diff --git a/components/TextControls/src/BaseTextFields/MDCBaseTextFieldDelegate.h b/components/TextControls/src/BaseTextFields/MDCBaseTextFieldDelegate.h index c08197cf77e..57d8bea864a 100644 --- a/components/TextControls/src/BaseTextFields/MDCBaseTextFieldDelegate.h +++ b/components/TextControls/src/BaseTextFields/MDCBaseTextFieldDelegate.h @@ -56,4 +56,15 @@ covered by @c UITextFieldDelegate. withSender:(id)sender canPerformAction:(BOOL)canPerformAction; +/** + At the end of every layout pass @c MDCBaseTextField checks to see if the intrinsic height it + calculates has changed. If it has, it calls this method. Setting assistive label text is an example + of something that results in this method getting called. If you have a height constraint set on the + text field, this method is a good place to update that constraint's constant. + + @param textField The MDCBaseTextField. + @param height The newly calculated height. + */ +- (void)baseTextField:(MDCBaseTextField *)textField didUpdateIntrinsicHeight:(CGFloat)height; + @end diff --git a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec index 6aad7e3d3c5..ad270c1879c 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 = "119.2.0" + s.version = "119.3.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"