diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c75e6e096d..3f330c2e1c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,42 @@
+# 119.4.0
+
+In this minor release, we added a new contentEdgeInsets API to Banner, added support for vertically centered image views in MDCSelfSizingStereoCell, and improved support for tvOS.
+
+## API changes
+
+### Banner
+
+*new* property: `contentEdgeInsets` in `MDCBannerView`.
+
+### Lists
+
+*new* enum: `MDCSelfSizingStereoCellImageViewVerticalPosition`.
+*new* property: `leadingImageViewVerticalPosition` in `MDCSelfSizingStereoCell`.
+*new* property: `trailingImageViewVerticalPosition` in `MDCSelfSizingStereoCell`.
+
+## Component changes
+
+### Banner
+
+* [ Add contentEdgeInsets support to replace layoutMargins usage in sizeThatFits:.](https://github.com/material-components/material-components-ios/commit/8bd8b295e227310a8c835ec9d3f58ca3b651319a) (Wenyu Zhang)
+* [setNeedsLayout before showing Banner in BannerAutolayoutSwiftExampleViewController](https://github.com/material-components/material-components-ios/commit/1faf60e6f5fee51470fc2b21b26304879d6405da) (Wenyu Zhang)
+
+### Cards
+
+* [Update setting corner radius to match team style guide](https://github.com/material-components/material-components-ios/commit/4b7c446e7e14b43202421311894e388b18df6650) (Cody Weaver)
+
+### List
+
+* [Support vertically centered image views in MDCSelfSizingStereoCell](https://github.com/material-components/material-components-ios/commit/bdeb1f7e9d23708ab01440cb3f7e9d681c195038) (Andrew Overton)
+
+## Multi-component changes
+
+* [Additional requested docs changes](https://github.com/material-components/material-components-ios/commit/b42de42c876dada1437193a2c0b793b5cef41e70) (Andrew Overton)
+* [Ensure swift snippets always come before objc](https://github.com/material-components/material-components-ios/commit/08ca10b07876de6f363c7f6baf6b265f3980fea1) (Andrew Overton)
+* [Improve support for tvOS.](https://github.com/material-components/material-components-ios/commit/3bc7339b7a8e834a661de78fe3ae9805468f5259) (Jeff Verkoeyen)
+
+---
+
# 119.3.0
In this minor release we added a new method to the `MDCBaseTextFieldDelegate` protocol and fixed bugs in NavigationDrawer, TextControls, and Tabs.
diff --git a/MaterialComponents.podspec b/MaterialComponents.podspec
index 156cd5b580c..efa09b47ebf 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.3.0"
+ mdc.version = "119.4.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 2b3bd60ae6e..81b8b6ac9fd 100644
--- a/MaterialComponentsEarlGreyTests.podspec
+++ b/MaterialComponentsEarlGreyTests.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialComponentsEarlGreyTests"
- s.version = "119.3.0"
+ s.version = "119.4.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 ca3dbc5158a..470084e93f6 100644
--- a/MaterialComponentsExamples.podspec
+++ b/MaterialComponentsExamples.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "MaterialComponentsExamples"
- s.version = "119.3.0"
+ s.version = "119.4.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 b170974b749..9c8f2c202c4 100644
--- a/MaterialComponentsSnapshotTests.podspec
+++ b/MaterialComponentsSnapshotTests.podspec
@@ -53,7 +53,7 @@ end
Pod::Spec.new do |s|
s.name = "MaterialComponentsSnapshotTests"
- s.version = "119.3.0"
+ s.version = "119.4.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 c2b04563bda..d8b28152970 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-119.3.0
+119.4.0
diff --git a/catalog/MDCCatalog/Info.plist b/catalog/MDCCatalog/Info.plist
index bc10c76c81f..cd2753764d6 100644
--- a/catalog/MDCCatalog/Info.plist
+++ b/catalog/MDCCatalog/Info.plist
@@ -15,11 +15,11 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 119.3.0
+ 119.4.0
CFBundleSignature
????
CFBundleVersion
- 119.3.0
+ 119.4.0
LSRequiresIPhoneOS
UIAppFonts
diff --git a/catalog/MDCDragons/Info.plist b/catalog/MDCDragons/Info.plist
index a804cabf07c..6b9bbd85eee 100644
--- a/catalog/MDCDragons/Info.plist
+++ b/catalog/MDCDragons/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 119.3.0
+ 119.4.0
CFBundleVersion
- 119.3.0
+ 119.4.0
LSRequiresIPhoneOS
UILaunchStoryboardName
diff --git a/catalog/MaterialCatalog/MaterialCatalog.podspec b/catalog/MaterialCatalog/MaterialCatalog.podspec
index 2bfe0fcdec7..fe68d8d3781 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.3.0"
+ s.version = "119.4.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 d95838cedb5..b6b0fd75f50 100644
--- a/components/AppBar/README.md
+++ b/components/AppBar/README.md
@@ -21,7 +21,6 @@ information and actions relating to the current screen.
* [Using top app bars](#using-top-app-bars)
* [Regular top app bar](#regular-top-app-bar)
-* [Contextual action bar](#contextual-action-bar)
* [Theming](#theming)
* [Migration guides](#migration-guides)
* [Unsupported](#unsupported)
@@ -104,16 +103,10 @@ NSLog(@"accessibilityLabel: %@",self.navigationItem.rightBarButtonItem.accessibi
```
-## Types
-
-There are two types of top app bar:
-1. [Regular top app bar](#regular-top-app-bar)
-1. [Contextual action bar](#contextual-action-bar)
-
-![Regular top app bar and contextual action bars](docs/assets/appbar-types.png)
-
## Regular top app bar
+![Example of an iOS regular top app bar](docs/assets/app-bar-example.png)
+
Regular top app bars are the only top app bars supported on iOS.
### Top app bar examples
@@ -641,10 +634,6 @@ See the [FlexibleHeader](../FlexibleHeader) documentation for additional usage g
-------------------------------- | -------------------- | ------------------------------------------ | -------------
**Icons** | `-[UIViewController navigationItem]` | `-setLeftBarButtonItems:`
`-leftBarButtonItems`
`-setRightBarButtonItems:`
`-rightBarButtonItems` | `nil`
-## Contextual action bar
-
-Contextual action bars are not implemented on iOS.
-
## Theming
`MDCAppBarViewController` supports Material Theming using a Container Scheme. To theme your app bar, add the `AppBar+Theming` subspec to your `Podfile`:
diff --git a/components/AppBar/docs/assets/app-bar-example.png b/components/AppBar/docs/assets/app-bar-example.png
new file mode 100644
index 00000000000..30b0040c9e9
Binary files /dev/null and b/components/AppBar/docs/assets/app-bar-example.png differ
diff --git a/components/AppBar/docs/assets/appbar-types.png b/components/AppBar/docs/assets/appbar-types.png
deleted file mode 100644
index e754eb3766a..00000000000
Binary files a/components/AppBar/docs/assets/appbar-types.png and /dev/null differ
diff --git a/components/Banner/examples/AppBarBannerExample.m b/components/Banner/examples/AppBarBannerExample.m
index 9103aa3bf94..a7a93e418fa 100644
--- a/components/Banner/examples/AppBarBannerExample.m
+++ b/components/Banner/examples/AppBarBannerExample.m
@@ -86,7 +86,7 @@ - (void)dismissBanner {
- (void)showBanner {
self.banner = [[MDCBannerView alloc] init];
[self.banner applyThemeWithScheme:_containerScheme];
- self.banner.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8);
+ self.banner.contentEdgeInsets = UIEdgeInsetsMake(0, 8, 0, 8);
self.banner.textView.text = @"This banner has been set as bottomBar of this AppBar.";
[self.banner.leadingButton setTitle:@"Dismiss" forState:UIControlStateNormal];
[self.banner.leadingButton addTarget:self
diff --git a/components/Banner/examples/BannerAutolayoutSwiftExampleViewController.swift b/components/Banner/examples/BannerAutolayoutSwiftExampleViewController.swift
index 73724aab2fd..87625118ba0 100644
--- a/components/Banner/examples/BannerAutolayoutSwiftExampleViewController.swift
+++ b/components/Banner/examples/BannerAutolayoutSwiftExampleViewController.swift
@@ -13,11 +13,10 @@
// limitations under the License.
import UIKit
-
import MaterialComponents.MaterialBanner
-import MaterialComponents.MaterialBanner_Theming
+import MaterialComponents.MaterialBanner_Theming
import MaterialComponents.MaterialButtons
-import MaterialComponents.MaterialButtons_Theming
+import MaterialComponents.MaterialButtons_Theming
import MaterialComponents.MaterialColorScheme
import MaterialComponents.MaterialContainerScheme
@@ -33,7 +32,8 @@ class BannerAutoLayoutSwiftExampleViewController: UIViewController {
showBannerButton.translatesAutoresizingMaskIntoConstraints = false
showBannerButton.applyTextTheme(withScheme: containerScheme)
showBannerButton.setTitle("Material Banner", for: .normal)
- showBannerButton.addTarget(self, action: #selector(self.didTapShowBannerButton), for: .touchUpInside)
+ showBannerButton.addTarget(
+ self, action: #selector(self.didTapShowBannerButton), for: .touchUpInside)
view.addSubview(showBannerButton)
showBannerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
showBannerButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
@@ -45,7 +45,8 @@ class BannerAutoLayoutSwiftExampleViewController: UIViewController {
bannerView.layoutMargins = .zero
let actionButton = bannerView.leadingButton
actionButton.setTitle("Dismiss", for: .normal)
- actionButton.addTarget(self, action: #selector(self.didTapDismissOnBannerView), for: .touchUpInside)
+ actionButton.addTarget(
+ self, action: #selector(self.didTapDismissOnBannerView), for: .touchUpInside)
bannerView.applyTheme(withScheme: containerScheme)
view.addSubview(bannerView)
bannerView.isHidden = true
@@ -60,7 +61,8 @@ class BannerAutoLayoutSwiftExampleViewController: UIViewController {
@objc func didTapShowBannerButton() {
bannerView.isHidden = false
- UIAccessibility.post(notification:.layoutChanged, argument: bannerView);
+ bannerView.setNeedsLayout()
+ UIAccessibility.post(notification: .layoutChanged, argument: bannerView)
}
@objc func didTapDismissOnBannerView() {
diff --git a/components/Banner/src/MDCBannerView.h b/components/Banner/src/MDCBannerView.h
index 6bf72d377e3..e0f9722e430 100644
--- a/components/Banner/src/MDCBannerView.h
+++ b/components/Banner/src/MDCBannerView.h
@@ -86,6 +86,15 @@ __attribute__((objc_subclassing_restricted)) @interface MDCBannerView
*/
@property(nonatomic, readwrite, strong, nonnull) UIColor *dividerColor;
+/**
+ The insets applied to the banner for all its content.
+
+ The banner uses this property to determine @c intrinsicContentSize and @c sizeThatFits:.
+
+ The default value is @c UIEdgeInsetsZero.
+ */
+@property(nonatomic, readwrite, assign) UIEdgeInsets contentEdgeInsets;
+
/*
Indicates whether the banner should automatically update its font when the device’s
UIContentSizeCategory is changed.
diff --git a/components/Banner/src/MDCBannerView.m b/components/Banner/src/MDCBannerView.m
index 5b2159ccbd1..5fa0fb5669e 100644
--- a/components/Banner/src/MDCBannerView.m
+++ b/components/Banner/src/MDCBannerView.m
@@ -36,6 +36,8 @@
@interface MDCBannerView ()
+@property(nonatomic, readwrite, strong) UIView *contentView;
+
@property(nonatomic, readwrite, strong) UITextView *textView;
@property(nonatomic, readwrite, strong) UIImageView *imageView;
@@ -47,6 +49,12 @@ @interface MDCBannerView ()
@property(nonatomic, readwrite, strong) UIView *divider;
@property(nonatomic, readwrite, assign) CGFloat dividerHeight;
+// Content constraints
+@property(nonatomic, readwrite, strong) NSLayoutConstraint *contentViewConstraintTop;
+@property(nonatomic, readwrite, strong) NSLayoutConstraint *contentViewConstraintBottom;
+@property(nonatomic, readwrite, strong) NSLayoutConstraint *contentViewConstraintLeft;
+@property(nonatomic, readwrite, strong) NSLayoutConstraint *contentViewConstraintRight;
+
// Image constraints
@property(nonatomic, readwrite, strong) NSLayoutConstraint *imageViewConstraintLeading;
@property(nonatomic, readwrite, strong) NSLayoutConstraint *imageViewConstraintCenterY;
@@ -123,6 +131,12 @@ - (void)commonBannerViewInit {
self.backgroundColor = UIColor.whiteColor;
_bannerViewLayoutStyle = MDCBannerViewLayoutStyleAutomatic;
self.layoutMargins = UIEdgeInsetsZero;
+ self.contentEdgeInsets = UIEdgeInsetsZero;
+
+ UIView *contentView = [[UIView alloc] init];
+ contentView.translatesAutoresizingMaskIntoConstraints = NO;
+ _contentView = contentView;
+ [self addSubview:contentView];
// Create textView
UITextView *textView = [[UITextView alloc] init];
@@ -138,7 +152,7 @@ - (void)commonBannerViewInit {
textView.textAlignment = NSTextAlignmentNatural;
textView.textContainerInset = UIEdgeInsetsZero;
textView.backgroundColor = UIColor.clearColor;
- [self addSubview:textView];
+ [contentView addSubview:textView];
_textView = textView;
// Create imageView
@@ -149,13 +163,13 @@ - (void)commonBannerViewInit {
imageView.contentMode = UIViewContentModeCenter;
imageView.clipsToBounds = YES;
imageView.hidden = YES;
- [self addSubview:imageView];
+ [contentView addSubview:imageView];
_imageView = imageView;
// Create a button container to organize buttons
UIView *buttonContainerView = [[UIView alloc] init];
buttonContainerView.translatesAutoresizingMaskIntoConstraints = NO;
- [self addSubview:buttonContainerView];
+ [contentView addSubview:buttonContainerView];
self.buttonContainerView = buttonContainerView;
// Create leadingButton and trailingButton
@@ -194,6 +208,15 @@ - (UIColor *)dividerColor {
return self.divider.backgroundColor;
}
+- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets {
+ _contentEdgeInsets = contentEdgeInsets;
+
+ self.contentViewConstraintBottom.constant = -contentEdgeInsets.bottom;
+ self.contentViewConstraintTop.constant = contentEdgeInsets.top;
+ self.contentViewConstraintLeft.constant = contentEdgeInsets.left;
+ self.contentViewConstraintRight.constant = -contentEdgeInsets.right;
+}
+
- (CGFloat)mdc_currentElevation {
return 0;
}
@@ -201,6 +224,7 @@ - (CGFloat)mdc_currentElevation {
#pragma mark - Constraints Helpers
- (void)setupConstraints {
+ [self setUpContentConstraints];
[self setUpImageViewConstraints];
[self setUpTextViewConstraints];
[self setUpButtonContainerConstraints];
@@ -208,12 +232,27 @@ - (void)setupConstraints {
[self setUpDividerConstraints];
}
+- (void)setUpContentConstraints {
+ self.contentViewConstraintLeft =
+ [self.contentView.leftAnchor constraintEqualToAnchor:self.layoutMarginsGuide.leftAnchor
+ constant:self.contentEdgeInsets.left];
+ self.contentViewConstraintRight =
+ [self.contentView.rightAnchor constraintEqualToAnchor:self.layoutMarginsGuide.rightAnchor
+ constant:-self.contentEdgeInsets.right];
+ self.contentViewConstraintTop =
+ [self.contentView.topAnchor constraintEqualToAnchor:self.layoutMarginsGuide.topAnchor
+ constant:self.contentEdgeInsets.top];
+ self.contentViewConstraintBottom =
+ [self.contentView.bottomAnchor constraintEqualToAnchor:self.layoutMarginsGuide.bottomAnchor
+ constant:-self.contentEdgeInsets.bottom];
+}
+
- (void)setUpImageViewConstraints {
self.imageViewConstraintLeading =
- [self.imageView.leadingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.leadingAnchor
+ [self.imageView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor
constant:kLeadingPadding];
self.imageViewConstraintTopLarge =
- [self.imageView.topAnchor constraintEqualToAnchor:self.layoutMarginsGuide.topAnchor
+ [self.imageView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor
constant:kTopPaddingLarge];
self.imageViewConstraintCenterY =
[self.imageView.centerYAnchor constraintEqualToAnchor:self.buttonContainerView.centerYAnchor];
@@ -221,38 +260,38 @@ - (void)setUpImageViewConstraints {
- (void)setUpTextViewConstraints {
self.textViewConstraintTop =
- [self.textView.topAnchor constraintEqualToAnchor:self.layoutMarginsGuide.topAnchor
+ [self.textView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor
constant:kTopPaddingLarge];
self.textViewConstraintCenterY =
[self.textView.centerYAnchor constraintEqualToAnchor:self.buttonContainerView.centerYAnchor];
self.textViewConstraintTrailing =
- [self.textView.trailingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.trailingAnchor
+ [self.textView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor
constant:-kTextTrailingPadding];
self.textViewConstraintLeadingWithImage =
[self.textView.leadingAnchor constraintEqualToAnchor:self.imageView.trailingAnchor
constant:kSpaceBetweenIconImageAndTextView];
self.textViewConstraintLeadingWithMargin =
- [self.textView.leadingAnchor constraintEqualToAnchor:self.layoutMarginsGuide.leadingAnchor
+ [self.textView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor
constant:kLeadingPadding];
}
- (void)setUpButtonContainerConstraints {
- self.buttonContainerConstraintLeading = [self.buttonContainerView.leadingAnchor
- constraintEqualToAnchor:self.layoutMarginsGuide.leadingAnchor
- constant:kLeadingPadding];
+ self.buttonContainerConstraintLeading =
+ [self.buttonContainerView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor
+ constant:kLeadingPadding];
self.buttonContainerConstraintWidthWithLeadingButton =
[self.buttonContainerView.widthAnchor constraintEqualToAnchor:self.leadingButton.widthAnchor];
self.buttonContainerConstraintTrailing = [self.buttonContainerView.trailingAnchor
- constraintEqualToAnchor:self.layoutMarginsGuide.trailingAnchor
+ constraintEqualToAnchor:self.contentView.trailingAnchor
constant:-kButtonContainerTrailingPadding];
- self.buttonContainerConstraintBottom = [self.buttonContainerView.bottomAnchor
- constraintEqualToAnchor:self.layoutMarginsGuide.bottomAnchor
- constant:-kBottomPadding];
+ self.buttonContainerConstraintBottom =
+ [self.buttonContainerView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor
+ constant:-kBottomPadding];
self.buttonContainerConstraintLeadingWithTextView = [self.buttonContainerView.leadingAnchor
constraintEqualToAnchor:self.textView.trailingAnchor
constant:kHorizontalSpaceBetweenTextViewAndButton];
self.buttonContainerConstraintTopWithMargin =
- [self.buttonContainerView.topAnchor constraintEqualToAnchor:self.layoutMarginsGuide.topAnchor
+ [self.buttonContainerView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor
constant:kTopPaddingSmall];
self.buttonContainerConstraintTopWithImageViewGreater = [self.buttonContainerView.topAnchor
constraintGreaterThanOrEqualToAnchor:self.imageView.bottomAnchor
@@ -315,6 +354,10 @@ - (void)setUpDividerConstraints {
}
- (void)deactivateAllConstraints {
+ self.contentViewConstraintBottom.active = NO;
+ self.contentViewConstraintTop.active = NO;
+ self.contentViewConstraintLeft.active = NO;
+ self.contentViewConstraintRight.active = NO;
self.imageViewConstraintLeading.active = NO;
self.imageViewConstraintTopLarge.active = NO;
self.imageViewConstraintCenterY.active = NO;
@@ -361,7 +404,7 @@ - (void)setFrame:(CGRect)frame {
- (CGSize)sizeThatFits:(CGSize)size {
MDCBannerViewLayoutStyle layoutStyle = [self layoutStyleForSizeToFit:size];
- CGFloat frameHeight = self.layoutMargins.top + self.layoutMargins.bottom;
+ CGFloat frameHeight = self.contentEdgeInsets.top + self.contentEdgeInsets.bottom;
CGSize contentSize = [self contentSizeForLayoutSize:size];
switch (layoutStyle) {
case MDCBannerViewLayoutStyleSingleRow: {
@@ -444,6 +487,10 @@ - (void)layoutSubviews {
- (void)updateConstraintsWithLayoutStyle:(MDCBannerViewLayoutStyle)layoutStyle {
[self deactivateAllConstraints];
+ self.contentViewConstraintBottom.active = YES;
+ self.contentViewConstraintTop.active = YES;
+ self.contentViewConstraintLeft.active = YES;
+ self.contentViewConstraintRight.active = YES;
self.imageViewConstraintLeading.active = YES;
if (!self.imageView.hidden) {
@@ -561,6 +608,7 @@ - (CGFloat)getFrameHeightOfImageViewAndTextViewWithSizeToFit:(CGSize)sizeToFit {
- (CGSize)contentSizeForLayoutSize:(CGSize)layoutSize {
CGFloat remainingWidth = layoutSize.width;
CGFloat marginsPadding = self.layoutMargins.left + self.layoutMargins.right;
+ marginsPadding += self.contentEdgeInsets.left + self.contentEdgeInsets.right;
remainingWidth -= marginsPadding;
remainingWidth -= (kLeadingPadding + kButtonContainerTrailingPadding);
return CGSizeMake(remainingWidth, layoutSize.height);
diff --git a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m
index 1ddff3f17d6..18dbd8f9d97 100644
--- a/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m
+++ b/components/Banner/tests/snapshot/MDCBannerSnapshotTests.m
@@ -572,4 +572,62 @@ - (void)testPreferredFontForAXSContentSizeCategory {
andVerifyForView:self.bannerView];
}
}
+
+#pragma mark - contentEdgeInsets
+
+- (void)testBannerWithContentEdgeInsets {
+ // When
+ self.bannerView.textView.text = kBannerLongText;
+ MDCButton *button1 = self.bannerView.leadingButton;
+ [button1 setTitle:@"Action1" forState:UIControlStateNormal];
+ [button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button1.uppercaseTitle = YES;
+ MDCButton *button2 = self.bannerView.trailingButton;
+ [button2 setTitle:@"Action2" forState:UIControlStateNormal];
+ [button2 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button2.uppercaseTitle = YES;
+ self.bannerView.imageView.hidden = YES;
+ self.bannerView.contentEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.bannerView];
+}
+
+- (void)testBannerWithContentEdgeInsetsLTR {
+ // When
+ self.bannerView.textView.text = kBannerLongText;
+ MDCButton *button1 = self.bannerView.leadingButton;
+ [button1 setTitle:@"Action1" forState:UIControlStateNormal];
+ [button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button1.uppercaseTitle = YES;
+ MDCButton *button2 = self.bannerView.trailingButton;
+ [button2 setTitle:@"Action2" forState:UIControlStateNormal];
+ [button2 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button2.uppercaseTitle = YES;
+ self.bannerView.imageView.hidden = YES;
+ self.bannerView.contentEdgeInsets = UIEdgeInsetsMake(20, 10, 20, 50);
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.bannerView];
+}
+
+- (void)testBannerWithContentEdgeInsetsRTL {
+ // When
+ self.bannerView.textView.text = kBannerLongText;
+ MDCButton *button1 = self.bannerView.leadingButton;
+ [button1 setTitle:@"Action1" forState:UIControlStateNormal];
+ [button1 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button1.uppercaseTitle = YES;
+ MDCButton *button2 = self.bannerView.trailingButton;
+ [button2 setTitle:@"Action2" forState:UIControlStateNormal];
+ [button2 setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
+ button2.uppercaseTitle = YES;
+ self.bannerView.imageView.hidden = YES;
+ self.bannerView.contentEdgeInsets = UIEdgeInsetsMake(20, 10, 20, 50);
+ [self changeViewToRTL:self.bannerView];
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.bannerView];
+}
+
@end
diff --git a/components/BottomAppBar/README.md b/components/BottomAppBar/README.md
index 132ace98bf0..5d0aaf896f5 100644
--- a/components/BottomAppBar/README.md
+++ b/components/BottomAppBar/README.md
@@ -121,10 +121,6 @@ trailingButton.accessibilityHint = @"Purchase the item";
```
-## Types
-
-There is only one type of bottom app bar.
-
## Bottom app bar
Bottom app bars group primary and secondary actions at the bottom of the screen, where they are easily reachable by the user's thumb.
diff --git a/components/BottomNavigation/README.md b/components/BottomNavigation/README.md
index 1e5a6d0965f..9f262f883e2 100644
--- a/components/BottomNavigation/README.md
+++ b/components/BottomNavigation/README.md
@@ -135,10 +135,6 @@ override func viewWillLayoutSubviews() {
```
-## Types
-
-There is only one type of bottom navigation bar.
-
## Bottom navigation bar
![A bottom navigation item with home, mail, favorites, reader, and birthday sections](docs/assets/bottom-nav-example.png)
diff --git a/components/Buttons/docs/buttons.md b/components/Buttons/docs/buttons.md
index 2ed4efc9ffc..7c65002393b 100644
--- a/components/Buttons/docs/buttons.md
+++ b/components/Buttons/docs/buttons.md
@@ -20,7 +20,6 @@ api_doc_root: true
* [Text button](#text-button)
* [Outlined button](#outlined-button)
* [Contained button](#contained-button)
-* [Toggle button](#toggle-button)
* [Theming](#theming)
- - -
@@ -163,12 +162,7 @@ this day."
## Types
-There are four types of buttons:
-
-1. [Text button](#text-button)
-2. [Outlined button](#outlined-button)
-3. [Contained button](#contained-button)
-4. [Toggle button](#toggle-button) (*not supported in iOS*)
+There are four types of buttons: 1. [Text button](#text-button) 2. [Outlined button](#outlined-button) 3. [Contained button](#contained-button) 4. Toggle button (*not supported in iOS*)"
![Example of the four button types](assets/buttons_types.png)
@@ -186,16 +180,16 @@ All Material buttons are implemented by `MDCButton`, a subclass of [`UIButton`](
To use a text button use the text button theming method on the `MDCButton` theming extension. For more information on theming extensions see the [Theming section](#theming).
-#### Objective-C
-```objc
-[self.button applyTextThemeWithScheme:self.containerScheme];
-```
-
#### Swift
```swift
button.applyTextTheme(withScheme: containerScheme)
```
+
+#### Objective-C
+```objc
+[self.button applyTextThemeWithScheme:self.containerScheme];
+```
### Anatomy and key properties
@@ -243,16 +237,15 @@ A text button has a text label, a transparent container and an optional icon.
To achieve an outlined button use the outlined button theming method on the `MDCButton` theming extension. To access the theming extension see the [Theming section](#theming).
-#### Objective-C
-```objc
-[self.button applyOutlinedThemeWithScheme:self.containerScheme];
-```
-
#### Swift
-
```swift
button.applyOutlinedTheme(withScheme: containerScheme)
```
+
+#### Objective-C
+```objc
+[self.button applyOutlinedThemeWithScheme:self.containerScheme];
+```
### Anatomy and Key properties
@@ -300,16 +293,15 @@ An outlined button has a text label, a container, and an optional icon.
Contained buttons are implemented by `MDCButton`. 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
-```objc
-[self.button applyContainedThemeWithScheme:self.containerScheme];
-```
-
#### Swift
-
```swift
button.applyContainedTheme(withScheme: containerScheme)
```
+
+#### Objective-C
+```objc
+[self.button applyContainedThemeWithScheme:self.containerScheme];
+```
### Anatomy and Key properties
@@ -346,10 +338,6 @@ A contained button has a text label, a container, and an optional icon.
**Icon** | `imageView` | `setImage:forState:`
`imageForState:` | `nil`
**Color** | `imageView.tintColor` | `setImageViewTintColor:forState:`
`imageViewTintColorForState:` | `nil`
-## Toggle button
-
-[Toggle buttons](https://material.io/components/buttons/#toggle-button) can be used to select from a group of choices. They are not supported on iOS.
-
## Theming
You can theme an `MDCButton` to match any of the Material Button styles using theming
diff --git a/components/Buttons/docs/fabs.md b/components/Buttons/docs/fabs.md
index 499671b22c2..7829a0f8e2d 100644
--- a/components/Buttons/docs/fabs.md
+++ b/components/Buttons/docs/fabs.md
@@ -142,11 +142,7 @@ this day."
## Types
-There are three types of FABs:
-
-1. [Regular FABs](#regular-fabs)
-2. [Mini FABs](#mini-fabs)
-3. [Extended FABs](#extended-fabs)
+There are three types of FABs: 1\. [Regular FABs](#regular-fab) 2\. [Mini FABs](#mini-fab) 3\. [Extended FABs](#extended-fab)
![Three FABs, one of each type.](assets/fab-types.png)
@@ -218,16 +214,16 @@ To create a mini FAB use the `+floatingButtonWithShape:` constructor with a valu
For more information on theming FABs see the [Theming section](#theming).
+#### Swift
+```swift
+let fab = MDCFloatingButton(shape: mini)
+```
+
#### Objective-C
```objc
MDCFloatingButton *fab =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini];
```
-
-#### Swift
-```swift
-let fab = MDCFloatingButton(shape: mini)
-```
### Anatomy and key properties
@@ -269,18 +265,18 @@ To create an extended FAB use the `+floatingButtonWithShape:` constructor with a
For more information on theming FABs see the [Theming section](#theming).
+#### Swift
+```swift
+let fab = MDCFloatingButton(shape: .default)
+fab.mode = .expanded
+```
+
#### Objective-C
```objc
MDCFloatingButton *fab =
[MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeDefault];
fab.mode = MDCFloatingButtonModeExpanded;
```
-
-#### Swift
-```swift
-let fab = MDCFloatingButton(shape: .default)
-fab.mode = .expanded
-```
### Anatomy and key properties
diff --git a/components/Buttons/src/MDCButton.m b/components/Buttons/src/MDCButton.m
index 296b421c7c6..d263d5205cd 100644
--- a/components/Buttons/src/MDCButton.m
+++ b/components/Buttons/src/MDCButton.m
@@ -262,6 +262,7 @@ - (void)commonMDCButtonInit {
}
#ifdef __IPHONE_13_4
+#if !TARGET_OS_TV
if (@available(iOS 13.4, *)) {
if ([self respondsToSelector:@selector(pointerStyleProvider)]) {
__weak __typeof__(self) weakSelf = self;
@@ -282,7 +283,8 @@ - (void)commonMDCButtonInit {
self.pointerInteractionEnabled = NO;
}
}
-#endif
+#endif // !TARGET_OS_TV
+#endif // __IPHONE_13_4
}
- (void)dealloc {
diff --git a/components/Buttons/src/MDCFloatingButton+Animation.m b/components/Buttons/src/MDCFloatingButton+Animation.m
index a680315473e..0aedcdf59e9 100644
--- a/components/Buttons/src/MDCFloatingButton+Animation.m
+++ b/components/Buttons/src/MDCFloatingButton+Animation.m
@@ -95,6 +95,7 @@ - (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion {
// Because of this, we instead temporarily disable pointer interaction for the button while it
// animates and reenable (if previously enabled) once the animation has ended.
#ifdef __IPHONE_13_4
+#if !TARGET_OS_TV
BOOL wasPointerInteractionEnabled = NO;
if (@available(iOS 13.4, *)) {
if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) {
@@ -102,7 +103,8 @@ - (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion {
self.pointerInteractionEnabled = NO;
}
}
-#endif
+#endif // !TARGET_OS_TV
+#endif // __IPHONE_13_4
void (^expandActions)(void) = ^{
self.layer.transform =
CATransform3DConcat(self.layer.transform, [MDCFloatingButton expandTransform]);
@@ -113,12 +115,14 @@ - (void)expand:(BOOL)animated completion:(void (^_Nullable)(void))completion {
[self.layer removeAnimationForKey:kMDCFloatingButtonOpacityKey];
[self.imageView.layer removeAnimationForKey:kMDCFloatingButtonTransformKey];
#ifdef __IPHONE_13_4
+#if !TARGET_OS_TV
if (@available(iOS 13.4, *)) {
if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) {
self.pointerInteractionEnabled = wasPointerInteractionEnabled;
}
}
-#endif
+#endif // !TARGET_OS_TV
+#endif // __IPHONE_13_4
if (completion) {
completion();
}
@@ -191,6 +195,7 @@ - (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion {
// Because of this, we instead temporarily disable pointer interaction for the button while it
// animates and reenable (if previously enabled) once the animation has ended.
#ifdef __IPHONE_13_4
+#if !TARGET_OS_TV
BOOL wasPointerInteractionEnabled = NO;
if (@available(iOS 13.4, *)) {
if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) {
@@ -198,7 +203,8 @@ - (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion {
self.pointerInteractionEnabled = NO;
}
}
-#endif
+#endif // !TARGET_OS_TV
+#endif // __IPHONE_13_4
void (^collapseActions)(void) = ^{
self.layer.transform =
@@ -210,12 +216,14 @@ - (void)collapse:(BOOL)animated completion:(void (^_Nullable)(void))completion {
[self.layer removeAnimationForKey:kMDCFloatingButtonOpacityKey];
[self.imageView.layer removeAnimationForKey:kMDCFloatingButtonTransformKey];
#ifdef __IPHONE_13_4
+#if !TARGET_OS_TV
if (@available(iOS 13.4, *)) {
if ([self respondsToSelector:@selector(isPointerInteractionEnabled)]) {
self.pointerInteractionEnabled = wasPointerInteractionEnabled;
}
}
-#endif
+#endif // !TARGET_OS_TV
+#endif // __IPHONE_13_4
if (completion) {
completion();
}
diff --git a/components/Cards/README.md b/components/Cards/README.md
index e6239dd7d0d..bf65a612160 100644
--- a/components/Cards/README.md
+++ b/components/Cards/README.md
@@ -175,8 +175,6 @@ card.accessibilityLabel = [NSString
```
-## Types
-
## Card
![Card with sample image and buttons](docs/assets/custom-card.png)
@@ -299,7 +297,7 @@ _**Note:** All the optional elements of a card's content are implemented through
**Elevation** | N/A | `-setShadowElevation:forState:`
`-shadowElevationForState:` | 1
**Ripple color** | `rippleView.rippleColor` | N/A | `nil`
-### Theming
+## Theming
Cards supports Material Theming using a Container Scheme. `MDCCard` and `MDCCardCollectionCell` have both default and outlined theming methods. Learn more about theming extensions [here](../../docs/theming.md). Below is a screenshot of an `MDCCard` with the Material Design Shrine theme:
diff --git a/components/Cards/src/Theming/MDCCard+MaterialTheming.m b/components/Cards/src/Theming/MDCCard+MaterialTheming.m
index 0f2d78825eb..d95864f6c58 100644
--- a/components/Cards/src/Theming/MDCCard+MaterialTheming.m
+++ b/components/Cards/src/Theming/MDCCard+MaterialTheming.m
@@ -14,6 +14,8 @@
#import "MDCCard+MaterialTheming.h"
+#import "MaterialCards.h"
+
static const MDCShadowElevation kNormalElevation = 1;
static const MDCShadowElevation kHighlightedElevation = 1;
static const CGFloat kBorderWidth = 1;
@@ -35,7 +37,7 @@ - (void)applyThemeWithScheme:(id)scheme {
if (shapeScheme) {
[self applyThemeWithShapeScheme:shapeScheme];
} else {
- self.layer.cornerRadius = (CGFloat)4;
+ self.layer.cornerRadius = 4;
}
[self setShadowElevation:kNormalElevation forState:UIControlStateNormal];
@@ -66,7 +68,7 @@ - (void)applyOutlinedThemeWithScheme:(nonnull id)scheme {
if (shapeScheme) {
[self applyThemeWithShapeScheme:shapeScheme];
} else {
- self.layer.cornerRadius = (CGFloat)4;
+ self.layer.cornerRadius = 4;
}
NSUInteger maximumStateValue = UIControlStateNormal | UIControlStateSelected |
diff --git a/components/Dialogs/README.md b/components/Dialogs/README.md
index f2f6523fe10..229ea068ffb 100644
--- a/components/Dialogs/README.md
+++ b/components/Dialogs/README.md
@@ -86,20 +86,11 @@ the `-performAccessibilityEscape` method in your custom dialog view controller c
}
```
-## Types
-
-There are four types of dialogs:
-
-1. [Alert](#alert-dialog)
-1. Simple
-1. Confirmation
-1. Full-screen
-
-![Examples of the four types of dialogs.](docs/assets/dialogs-types.png)
-
## Alert dialog
-The alert style is the only style supported on iOS. Consider using the [ActionSheet](https://github.com/material-components/material-components-ios/blob/develop/components/ActionSheet/README.md) component in situations where one of the unsupported dialog types would have been appropriate.
+![Example alert dialog with lorem ipsum sample text.](docs/assets/alert-example.png)
+
+Alerts are the only type of dialog supported on iOS. Consider using the [ActionSheet](https://github.com/material-components/material-components-ios/blob/develop/components/ActionSheet/README.md) component in situations where one of the unsupported dialog types would have been appropriate.
To present either an `MDCAlertController` or a custom dialog view controller, set its `modalPresentationStyle`
property to `UIModalPresentationCustom` and its `transitioningDelegate` property to an
diff --git a/components/Dialogs/docs/assets/alert-example.png b/components/Dialogs/docs/assets/alert-example.png
new file mode 100644
index 00000000000..b41ecefa4b7
Binary files /dev/null and b/components/Dialogs/docs/assets/alert-example.png differ
diff --git a/components/LibraryInfo/src/MDCLibraryInfo.m b/components/LibraryInfo/src/MDCLibraryInfo.m
index 2c1afb58aff..179c27ac002 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.3.0";
+static NSString* const kMDCLibraryInfoVersionString = @"119.4.0";
@implementation MDCLibraryInfo
diff --git a/components/LibraryInfo/tests/unit/LibraryInfoTests.m b/components/LibraryInfo/tests/unit/LibraryInfoTests.m
index 75e451b9fcd..af47b1c34af 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.3.0", etc.
+ // Accept: "119.4.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 dfe5dc252c5..4739ee207b5 100644
--- a/components/List/README.md
+++ b/components/List/README.md
@@ -173,19 +173,6 @@ Single-line list items contain a maximum of one line of text.
![Image of three single-line list items with sample text](docs/assets/single-line-list-example.png)
-#### Objective-C
-
-```objc
-- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
- cellForItemAtIndexPath:(NSIndexPath *)indexPath {
- MDCSelfSizingStereoCell *cell =
- (MDCSelfSizingStereoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
- forIndexPath:indexPath];
- cell.titleLabel.text = @"This is a single-line list";
- return cell;
-}
-```
-
#### Swift
```swift
@@ -200,31 +187,30 @@ func collectionView(_ collectionView: UICollectionView,
return cell
}
```
-
-
-### Two-line list
-
-Two-line list items contain a maximum of two lines of text.
-
-### Two-line list example
-
-![Image of three two-line list items with sample text](docs/assets/two-line-list-example.png)
-
#### Objective-C
```objc
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MDCSelfSizingStereoCell *cell =
- [collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
+ (MDCSelfSizingStereoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
forIndexPath:indexPath];
- cell.titleLabel.text = @"This is a two-line list";
- cell.detailLabel.text = @"This is secondary text that occupies one line.";
+ cell.titleLabel.text = @"This is a single-line list";
return cell;
}
```
+
+
+### Two-line list
+
+Two-line list items contain a maximum of two lines of text.
+### Two-line list example
+
+![Image of three two-line list items with sample text](docs/assets/two-line-list-example.png)
+
+
#### Swift
```swift
@@ -240,17 +226,7 @@ func collectionView(_ collectionView: UICollectionView,
return cell
}
```
-
-
-### Three-line list
-
-Three-line list items contains a maximum of three lines of text.
-### Three-line list example
-
-![Image of three three-line list items with sample text](docs/assets/three-line-list-example.png)
-
-
#### Objective-C
```objc
@@ -259,12 +235,22 @@ Three-line list items contains a maximum of three lines of text.
MDCSelfSizingStereoCell *cell =
[collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
forIndexPath:indexPath];
- cell.titleLabel.text = @"This is a three-line list";
- cell.detailLabel.text = @"This is secondary text\nthat occupies two lines.";
+ cell.titleLabel.text = @"This is a two-line list";
+ cell.detailLabel.text = @"This is secondary text that occupies one line.";
return cell;
}
```
+
+
+### Three-line list
+
+Three-line list items contains a maximum of three lines of text.
+### Three-line list example
+
+![Image of three three-line list items with sample text](docs/assets/three-line-list-example.png)
+
+
#### Swift
```swift
@@ -280,6 +266,20 @@ func collectionView(_ collectionView: UICollectionView,
return cell
}
```
+
+#### Objective-C
+
+```objc
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
+ cellForItemAtIndexPath:(NSIndexPath *)indexPath {
+ MDCSelfSizingStereoCell *cell =
+ [collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
+ forIndexPath:indexPath];
+ cell.titleLabel.text = @"This is a three-line list";
+ cell.detailLabel.text = @"This is secondary text\nthat occupies two lines.";
+ return cell;
+}
+```
## Theming
@@ -375,14 +375,6 @@ to take:
1. Initialize `MDCInkView` on init and add it as a subview:
- #### Objective-C
-
- ```objc
- _inkView = [[MDCInkView alloc] initWithFrame:self.bounds];
- _inkView.usesLegacyInkRipple = NO;
- [self addSubview:_inkView];
- ```
-
#### Swift
```swift
@@ -390,6 +382,14 @@ to take:
inkView.usesLegacyInkRipple = false
addSubview(inkView)
```
+
+ #### Objective-C
+
+ ```objc
+ _inkView = [[MDCInkView alloc] initWithFrame:self.bounds];
+ _inkView.usesLegacyInkRipple = NO;
+ [self addSubview:_inkView];
+ ```
1. Initialize a `CGPoint` property in your cell (`CGPoint _lastTouch;`) to
@@ -400,6 +400,16 @@ and save where the touches were so we can then start the ripple animation from
that point:
+ #### Swift
+
+ ```swift
+ override func touchesBegan(_ touches: Set, with event: UIEvent?) {
+ let touch = touches.first
+ let location = touch?.location(in: self)
+ lastTouch = location
+ }
+ ```
+
#### Objective-C
```objc
@@ -411,35 +421,12 @@ that point:
[super touchesBegan:touches withEvent:event];
}
```
-
- #### Swift
-
- ```swift
- override func touchesBegan(_ touches: Set, with event: UIEvent?) {
- let touch = touches.first
- let location = touch?.location(in: self)
- lastTouch = location
- }
- ```
1. Override the `setHighlighted` method for your cell and apply the start and
stop ripple animations:
- #### Objective-C
-
- ```objc
- - (void)setHighlighted:(BOOL)highlighted {
- [super setHighlighted:highlighted];
- if (highlighted) {
- [_inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil];
- } else {
- [_inkView startTouchEndedAnimationAtPoint:_lastTouch completion:nil];
- }
- }
- ```
-
#### Swift
```swift
@@ -455,21 +442,25 @@ stop ripple animations:
// get...
}
```
-
-
-1. When the cell is reused we must make sure no outstanding ripple animations
-stay on the cell so we need to clear the ink before:
-
#### Objective-C
```objc
- - (void)prepareForReuse {
- [_inkView cancelAllAnimationsAnimated:NO];
- [super prepareForReuse];
+ - (void)setHighlighted:(BOOL)highlighted {
+ [super setHighlighted:highlighted];
+ if (highlighted) {
+ [_inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil];
+ } else {
+ [_inkView startTouchEndedAnimationAtPoint:_lastTouch completion:nil];
+ }
}
```
+
+1. When the cell is reused we must make sure no outstanding ripple animations
+stay on the cell so we need to clear the ink before:
+
+
#### Swift
```swift
@@ -478,6 +469,15 @@ stay on the cell so we need to clear the ink before:
super.prepareForReuse()
}
```
+
+ #### Objective-C
+
+ ```objc
+ - (void)prepareForReuse {
+ [_inkView cancelAllAnimationsAnimated:NO];
+ [super prepareForReuse];
+ }
+ ```
Now there is ink in our cells!
@@ -497,6 +497,12 @@ and their superview (the cell's `contentView`).
constraints that are set up to be accessible throughout the file:
+ #### Swift
+ ```swift
+ var imageLeftPaddingConstraint: NSLayoutConstraint
+ var imageRightPaddingConstraint: NSLayoutConstraint
+ var imageWidthConstraint: NSLayoutConstraint
+ ```
#### Objective-C
```objc
@@ -504,13 +510,6 @@ and their superview (the cell's `contentView`).
NSLayoutConstraint *_imageRightPaddingConstraint;
NSLayoutConstraint *_imageWidthConstraint;
```
-
- #### Swift
- ```swift
- var imageLeftPaddingConstraint: NSLayoutConstraint
- var imageRightPaddingConstraint: NSLayoutConstraint
- var imageWidthConstraint: NSLayoutConstraint
- ```
This is in order to support the changing layout if an image is set or not.
@@ -521,6 +520,13 @@ when the cell is set up. For that we expose a `setCellWidth` method that sets
the width constraint of the `contentView`:
+ #### Swift
+ ```swift
+ func set(cellWidth: CGFloat) {
+ cellWidthConstraint.constant = cellWidth
+ cellWidthConstraint.isActive = true
+ }
+ ```
#### Objective-C
```objc
@@ -529,20 +535,20 @@ the width constraint of the `contentView`:
_cellWidthConstraint.active = YES;
}
```
-
- #### Swift
- ```swift
- func set(cellWidth: CGFloat) {
- cellWidthConstraint.constant = cellWidth
- cellWidthConstraint.isActive = true
- }
- ```
and then in the collection view's `cellForItemAtIndexPath` delegate method we
set the width:
+ #### Swift
+ ```swift
+ var cellWidth = collectionView.bounds.width
+ if #available(iOS 11.0, *) {
+ cellWidth -= collectionView.adjustedContentInset.left + collectionView.adjustedContentInset.right
+ }
+ set(cellWidth: cellWidth)
+ ```
#### Objective-C
```objc
@@ -553,15 +559,6 @@ the width constraint of the `contentView`:
}
[cell setCellWidth:cellWidth];
```
-
- #### Swift
- ```swift
- var cellWidth = collectionView.bounds.width
- if #available(iOS 11.0, *) {
- cellWidth -= collectionView.adjustedContentInset.left + collectionView.adjustedContentInset.right
- }
- set(cellWidth: cellWidth)
- ```
1. In our collection view's flow layout we must set an `estimatedItemSize` so
@@ -571,17 +568,16 @@ the collection view will defer the size calculations to its content.
might break in runtime.
-
- #### Objective-C
- ```objc
- _flowLayout.estimatedItemSize = CGSizeMake(kSmallArbitraryCellWidth, kSmallestCellHeight);
- ```
-
#### Swift
```swift
flowLayout.estimatedItemSize = CGSize(width: kSmallArbitraryCellWidth,
height: kSmallestCellHeight)
```
+
+ #### Objective-C
+ ```objc
+ _flowLayout.estimatedItemSize = CGSizeMake(kSmallArbitraryCellWidth, kSmallestCellHeight);
+ ```
### Typography
@@ -601,6 +597,17 @@ support it in our cells we need to follow these steps:
set/update methods:
+ #### Swift
+ ```swift
+ func updateTitleFont() {
+ if (_titleFont == nil) {
+ _titleFont = defaultTitleFont
+ }
+ _titleLabel.font =
+ _titleFont.mdc_fontSized(forMaterialTextStyle: .subheadline,
+ scaledForDynamicType: mdc_adjustsFontForContentSizeCategory)
+ }
+ ```
#### Objective-C
```objc
@@ -614,18 +621,6 @@ set/update methods:
[self setNeedsLayout];
}
```
-
- #### Swift
- ```swift
- func updateTitleFont() {
- if (_titleFont == nil) {
- _titleFont = defaultTitleFont
- }
- _titleLabel.font =
- _titleFont.mdc_fontSized(forMaterialTextStyle: .subheadline,
- scaledForDynamicType: mdc_adjustsFontForContentSizeCategory)
- }
- ```
1. Add an observer in the cell to check for the
@@ -633,6 +628,13 @@ set/update methods:
text size has been changed.
+ #### Swift
+ ```swift
+ NotificationCenter.default.addObserver(self,
+ selector: #selector(contentSizeCategoryDidChange(notification:)),
+ name: UIContentSizeCategory.didChangeNotification,
+ object: nil)
+ ```
#### Objective-C
```objc
@@ -642,20 +644,19 @@ text size has been changed.
name:UIContentSizeCategoryDidChangeNotification
object:nil];
```
-
- #### Swift
- ```swift
- NotificationCenter.default.addObserver(self,
- selector: #selector(contentSizeCategoryDidChange(notification:)),
- name: UIContentSizeCategory.didChangeNotification,
- object: nil)
- ```
In the selector update the font sizes to reflect the change:
+ #### Swift
+ ```swift
+ func contentSizeCategoryDidChange(_: NSNotification) {
+ updateTitleFont()
+ updateDetailsFont()
+ }
+ ```
#### Objective-C
```objc
@@ -664,20 +665,18 @@ text size has been changed.
[self updateDetailsFont];
}
```
-
- #### Swift
- ```swift
- func contentSizeCategoryDidChange(_: NSNotification) {
- updateTitleFont()
- updateDetailsFont()
- }
- ```
1. Add an observer also in the `UIViewController` so we can reload the
collection view once there is a change:
+ #### Swift
+ ```swift
+ func contentSizeCategoryDidChange(_: NSNotification) {
+ collectionView.reloadData()
+ }
+ ```
#### Objective-C
```objc
@@ -685,13 +684,6 @@ text size has been changed.
[self.collectionView reloadData];
}
```
-
- #### Swift
- ```swift
- func contentSizeCategoryDidChange(_: NSNotification) {
- collectionView.reloadData()
- }
- ```
### iPhone X safe area support
@@ -701,6 +693,12 @@ text size has been changed.
aware of the safe area:
+ #### Swift
+ ```swift
+ if #available(iOS 11.0, *) {
+ collectionView.contentInsetAdjustmentBehavior = .always
+ }
+ ```
#### Objective-C
```objc
@@ -710,13 +708,6 @@ text size has been changed.
}
#endif
```
-
- #### Swift
- ```swift
- if #available(iOS 11.0, *) {
- collectionView.contentInsetAdjustmentBehavior = .always
- }
- ```
Lastly, as seen in the self-sizing section on step 2, when setting the width
@@ -730,6 +721,22 @@ text size has been changed.
code changes to achieve that:
+ #### Swift
+ ```swift
+ override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+ super.traitCollectionDidChange(previousTraitCollection)
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ self.collectionView.reloadData()
+ }
+
+ override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+ super.viewWillTransition(to: size, with: coordinator)
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ coordinator.animate(alongsideTransition: nil) { (_) in
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ }
+ }
+ ```
#### Objective-C
```objc
@@ -750,23 +757,6 @@ text size has been changed.
}];
}
```
-
- #### Swift
- ```swift
- override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
- super.traitCollectionDidChange(previousTraitCollection)
- self.collectionView.collectionViewLayout.invalidateLayout()
- self.collectionView.reloadData()
- }
-
- override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
- super.viewWillTransition(to: size, with: coordinator)
- self.collectionView.collectionViewLayout.invalidateLayout()
- coordinator.animate(alongsideTransition: nil) { (_) in
- self.collectionView.collectionViewLayout.invalidateLayout()
- }
- }
- ```
### Right to left text support
@@ -774,31 +764,29 @@ text size has been changed.
To support right to left text we need to import `MDFInternationalization`:
+#### Swift
+```swift
+import MDFInternationalization
+```
#### Objective-C
```objc
#import
```
-
-#### Swift
-```swift
-import MDFInternationalization
-```
and for each of our cell's subviews me need to update the `autoResizingMask`:
+#### Swift
+```swift
+_titleLabel.autoresizingMask =
+ MDFTrailingMarginAutoresizingMaskForLayoutDirection(mdf_effectiveUserInterfaceLayoutDirection)
+```
#### Objective-C
```objc
_titleLabel.autoresizingMask =
MDFTrailingMarginAutoresizingMaskForLayoutDirection(self.mdf_effectiveUserInterfaceLayoutDirection);
```
-
-#### Swift
-```swift
-_titleLabel.autoresizingMask =
- MDFTrailingMarginAutoresizingMaskForLayoutDirection(mdf_effectiveUserInterfaceLayoutDirection)
-```
diff --git a/components/List/src/MDCSelfSizingStereoCell.h b/components/List/src/MDCSelfSizingStereoCell.h
index 1e1cda02df9..0c3ea99d1e7 100644
--- a/components/List/src/MDCSelfSizingStereoCell.h
+++ b/components/List/src/MDCSelfSizingStereoCell.h
@@ -15,6 +15,7 @@
#import
#import "MDCBaseCell.h"
+#import "MDCSelfSizingStereoCellImageViewVerticalPosition.h"
/**
MDCSelfSizingStereoCell is intended to be an easy to use readymade implementation of a basic
@@ -42,11 +43,23 @@ __attribute__((objc_subclassing_restricted)) @interface MDCSelfSizingStereoCell
*/
@property(nonatomic, strong, readonly) UIImageView *leadingImageView;
+/**
+ This property influences the positioning of @c leadingImageView. The default value is @c .top.
+ */
+@property(nonatomic, assign)
+ MDCSelfSizingStereoCellImageViewVerticalPosition leadingImageViewVerticalPosition;
+
/**
The UIImageView responsible for displaying the trailing image.
*/
@property(nonatomic, strong, readonly) UIImageView *trailingImageView;
+/**
+ This property influences the positioning of @c trailingImageView. The default value is @c .top.
+ */
+@property(nonatomic, assign)
+ MDCSelfSizingStereoCellImageViewVerticalPosition trailingImageViewVerticalPosition;
+
/**
The UILabel responsible for displaying the title text. By default, `numberOfLines` is set to 0 so
the label wraps and the self-sizing capabilities of the cell are best utilized.
diff --git a/components/List/src/MDCSelfSizingStereoCell.m b/components/List/src/MDCSelfSizingStereoCell.m
index 8082447a282..ceff848fa11 100644
--- a/components/List/src/MDCSelfSizingStereoCell.m
+++ b/components/List/src/MDCSelfSizingStereoCell.m
@@ -172,12 +172,15 @@ - (MDCSelfSizingStereoCellLayout *)layoutForCellWidth:(CGFloat)cellWidth {
CGFloat flooredCellWidth = floor(cellWidth);
MDCSelfSizingStereoCellLayout *layout = self.cachedLayouts[@(flooredCellWidth)];
if (!layout) {
- layout = [[MDCSelfSizingStereoCellLayout alloc] initWithLeadingImageView:self.leadingImageView
- trailingImageView:self.trailingImageView
- textContainer:self.textContainer
- titleLabel:self.titleLabel
- detailLabel:self.detailLabel
- cellWidth:flooredCellWidth];
+ layout = [[MDCSelfSizingStereoCellLayout alloc]
+ initWithLeadingImageView:self.leadingImageView
+ leadingImageViewVerticalPosition:self.leadingImageViewVerticalPosition
+ trailingImageView:self.trailingImageView
+ trailingImageViewVerticalPosition:self.trailingImageViewVerticalPosition
+ textContainer:self.textContainer
+ titleLabel:self.titleLabel
+ detailLabel:self.detailLabel
+ cellWidth:flooredCellWidth];
self.cachedLayouts[@(flooredCellWidth)] = layout;
}
return layout;
diff --git a/components/List/src/MDCSelfSizingStereoCellImageViewVerticalPosition.h b/components/List/src/MDCSelfSizingStereoCellImageViewVerticalPosition.h
new file mode 100644
index 00000000000..2b1bea5862f
--- /dev/null
+++ b/components/List/src/MDCSelfSizingStereoCellImageViewVerticalPosition.h
@@ -0,0 +1,31 @@
+// 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
+
+/**
+ This enum represents different positions for the side image views in an @c
+ MDCSelfSizingStereoCell.
+ */
+typedef NS_ENUM(NSUInteger, MDCSelfSizingStereoCellImageViewVerticalPosition) {
+ /**
+ This value results in the image view being positioned at the top of the cell.
+ */
+ MDCSelfSizingStereoCellImageViewVerticalPositionTop,
+ /**
+ This value results in the image view being positioned in the vertical center of the
+ cell.
+ */
+ MDCSelfSizingStereoCellImageViewVerticalPositionCenter,
+};
diff --git a/components/List/src/MaterialList.h b/components/List/src/MaterialList.h
index a2bc924868d..25807cf8da4 100644
--- a/components/List/src/MaterialList.h
+++ b/components/List/src/MaterialList.h
@@ -15,3 +15,4 @@
#import "MDCBaseCell.h"
#import "MDCSelfSizingLayoutAttributes.h"
#import "MDCSelfSizingStereoCell.h"
+#import "MDCSelfSizingStereoCellImageViewVerticalPosition.h"
diff --git a/components/List/src/private/MDCSelfSizingStereoCellLayout.h b/components/List/src/private/MDCSelfSizingStereoCellLayout.h
index b017b0c0c9d..b23e7b1ecdb 100644
--- a/components/List/src/private/MDCSelfSizingStereoCellLayout.h
+++ b/components/List/src/private/MDCSelfSizingStereoCellLayout.h
@@ -17,6 +17,8 @@
#import
#import
+#import "MDCSelfSizingStereoCellImageViewVerticalPosition.h"
+
@interface MDCSelfSizingStereoCellLayout : NSObject
@property(nonatomic, assign, readonly) CGFloat cellWidth;
@@ -28,7 +30,11 @@
@property(nonatomic, assign, readonly) CGRect trailingImageViewFrame;
- (instancetype)initWithLeadingImageView:(UIImageView *)leadingImageView
+ leadingImageViewVerticalPosition:
+ (MDCSelfSizingStereoCellImageViewVerticalPosition)leadingImageViewVerticalPosition
trailingImageView:(UIImageView *)trailingImageView
+ trailingImageViewVerticalPosition:
+ (MDCSelfSizingStereoCellImageViewVerticalPosition)trailingImageViewVerticalPosition
textContainer:(UIView *)textContainer
titleLabel:(UILabel *)titleLabel
detailLabel:(UILabel *)detailLabel
diff --git a/components/List/src/private/MDCSelfSizingStereoCellLayout.m b/components/List/src/private/MDCSelfSizingStereoCellLayout.m
index 5551de034bf..e2e7f5ab67f 100644
--- a/components/List/src/private/MDCSelfSizingStereoCellLayout.m
+++ b/components/List/src/private/MDCSelfSizingStereoCellLayout.m
@@ -15,6 +15,7 @@
*/
#import "MDCSelfSizingStereoCellLayout.h"
+#import "MDCSelfSizingStereoCellImageViewVerticalPosition.h"
static const CGFloat kVerticalMarginMin = 8.0;
static const CGFloat kVerticalMarginMax = 16.0;
@@ -39,146 +40,203 @@ @interface MDCSelfSizingStereoCellLayout ()
@implementation MDCSelfSizingStereoCellLayout
- (instancetype)initWithLeadingImageView:(UIImageView *)leadingImageView
+ leadingImageViewVerticalPosition:
+ (MDCSelfSizingStereoCellImageViewVerticalPosition)leadingImageViewVerticalPosition
trailingImageView:(UIImageView *)trailingImageView
+ trailingImageViewVerticalPosition:
+ (MDCSelfSizingStereoCellImageViewVerticalPosition)trailingImageViewVerticalPosition
textContainer:(UIView *)textContainer
titleLabel:(UILabel *)titleLabel
detailLabel:(UILabel *)detailLabel
cellWidth:(CGFloat)cellWidth {
self = [super init];
if (self) {
- self.cellWidth = cellWidth;
- [self assignFrameForLeadingImageView:leadingImageView];
- [self assignFrameForTrailingImageView:trailingImageView];
- [self assignFramesForTextContainer:textContainer titleLabel:titleLabel detailLabel:detailLabel];
- self.calculatedHeight = [self calculateHeight];
+ [self calculateLayoutWithLeadingImageView:leadingImageView
+ leadingImageViewVerticalPosition:leadingImageViewVerticalPosition
+ trailingImageView:trailingImageView
+ trailingImageViewVerticalPosition:trailingImageViewVerticalPosition
+ textContainer:textContainer
+ titleLabel:titleLabel
+ detailLabel:detailLabel
+ cellWidth:cellWidth];
}
return self;
}
-- (void)assignFrameForLeadingImageView:(UIImageView *)leadingImageView {
- CGSize size = [self sizeForImage:leadingImageView.image];
- CGFloat leadingPadding = 0;
- CGFloat topPadding = 0;
- if (!CGSizeEqualToSize(size, CGSizeZero)) {
- leadingPadding = kHorizontalMargin;
- topPadding = [self verticalMarginForImageViewOfSize:size];
+/**
+ This method calculates the frames for the subviews in the cell. It starts by determining the
+ leading and trailing image view frames. Then it uses those frames to determine the frame of the
+ title label, detail label, and the view that contains them. Then, it calculates the height of the
+ cell. Finally, it vertically centers the iamge views if necessary.
+ */
+- (void)calculateLayoutWithLeadingImageView:(UIImageView *)leadingImageView
+ leadingImageViewVerticalPosition:
+ (MDCSelfSizingStereoCellImageViewVerticalPosition)leadingImageViewVerticalPosition
+ trailingImageView:(UIImageView *)trailingImageView
+ trailingImageViewVerticalPosition:
+ (MDCSelfSizingStereoCellImageViewVerticalPosition)trailingImageViewVerticalPosition
+ textContainer:(UIView *)textContainer
+ titleLabel:(UILabel *)titleLabel
+ detailLabel:(UILabel *)detailLabel
+ cellWidth:(CGFloat)cellWidth {
+ // Initially assume an image view frame of .zero.
+ CGRect leadingImageViewFrame = CGRectZero;
+ CGSize leadingImageViewSize = [self sizeForImage:leadingImageView.image];
+ BOOL displaysLeadingImageView = !CGSizeEqualToSize(leadingImageViewSize, CGSizeZero);
+ if (displaysLeadingImageView) {
+ // Calculate non-zero image view frame because image exists and has a valid size.
+ CGFloat leadingImageViewMinX = kHorizontalMargin;
+ CGFloat leadingImageViewMinY = [self verticalMarginForImageViewOfSize:leadingImageViewSize];
+ leadingImageViewFrame = CGRectMake(leadingImageViewMinX, leadingImageViewMinY,
+ leadingImageViewSize.width, leadingImageViewSize.height);
}
- CGRect rect = CGRectZero;
- rect.origin = CGPointMake(leadingPadding, topPadding);
- rect.size = size;
- self.leadingImageViewFrame = rect;
-}
-- (void)assignFrameForTrailingImageView:(UIImageView *)trailingImageView {
- CGSize size = [self sizeForImage:trailingImageView.image];
- CGFloat trailingPadding = 0;
- CGFloat topPadding = 0;
- if (!CGSizeEqualToSize(size, CGSizeZero)) {
- trailingPadding = kHorizontalMargin;
- topPadding = [self verticalMarginForImageViewOfSize:size];
+ // Initially assume an image view frame of .zero.
+ CGRect trailingImageViewFrame = CGRectZero;
+ CGSize trailingImageViewSize = [self sizeForImage:trailingImageView.image];
+ BOOL displaysTrailingImageView = !CGSizeEqualToSize(trailingImageViewSize, CGSizeZero);
+ if (displaysTrailingImageView) {
+ // Calculate non-zero image view frame because image exists and has a valid size.
+ CGFloat trailingImageViewMinX = cellWidth - kHorizontalMargin - trailingImageViewSize.width;
+ CGFloat trailingImageViewMinY = [self verticalMarginForImageViewOfSize:trailingImageViewSize];
+ trailingImageViewFrame = CGRectMake(trailingImageViewMinX, trailingImageViewMinY,
+ trailingImageViewSize.width, trailingImageViewSize.height);
}
- CGFloat originX = self.cellWidth - trailingPadding - size.width;
- CGRect rect = CGRectZero;
- rect.origin = CGPointMake(originX, topPadding);
- rect.size = size;
- self.trailingImageViewFrame = rect;
-}
-- (void)assignFramesForTextContainer:(UIView *)textContainer
- titleLabel:(UILabel *)titleLabel
- detailLabel:(UILabel *)detailLabel {
+ // Initialize text-related view frame as .zero.
+ CGRect titleLabelFrame = CGRectZero;
+ CGRect detailLabelFrame = CGRectZero;
+ CGRect textContainerFrame = CGRectZero;
+
BOOL containsTitleText = titleLabel.text.length > 0;
BOOL containsDetailText = detailLabel.text.length > 0;
- if (!containsTitleText && !containsDetailText) {
- self.titleLabelFrame = CGRectZero;
- self.detailLabelFrame = CGRectZero;
- self.textContainerFrame = CGRectZero;
- return;
- }
+ if (containsTitleText || containsDetailText) {
+ // Determine min x of text region
+ CGFloat textContainerMinX = kHorizontalMargin;
+ if (displaysLeadingImageView) {
+ textContainerMinX = CGRectGetMaxX(leadingImageViewFrame) + kHorizontalMargin;
+ }
- BOOL hasLeadingImage = !CGRectEqualToRect(self.leadingImageViewFrame, CGRectZero);
- BOOL hasTrailingImage = !CGRectEqualToRect(self.trailingImageViewFrame, CGRectZero);
- CGFloat leadingImageViewMaxX = (hasLeadingImage ? CGRectGetMaxX(self.leadingImageViewFrame) : 0);
- CGFloat textContainerMinX = leadingImageViewMaxX + kHorizontalMargin;
- CGFloat trailingImageViewMinX =
- (hasTrailingImage ? CGRectGetMinX(self.trailingImageViewFrame) : self.cellWidth);
- CGFloat textContainerMaxX = trailingImageViewMinX - kHorizontalMargin;
- CGFloat textContainerMinY = kVerticalMarginMax;
- CGFloat textContainerWidth = textContainerMaxX - textContainerMinX;
- CGFloat textContainerHeight = 0;
-
- const CGSize fittingSize = CGSizeMake(textContainerWidth, CGFLOAT_MAX);
-
- CGSize titleSize = [titleLabel sizeThatFits:fittingSize];
- titleSize.width = textContainerWidth;
- const CGFloat titleLabelMinX = 0;
- CGFloat titleLabelMinY = 0;
- CGPoint titleOrigin = CGPointMake(titleLabelMinX, titleLabelMinY);
- CGRect titleFrame = CGRectZero;
- titleFrame.origin = titleOrigin;
- titleFrame.size = titleSize;
- self.titleLabelFrame = titleFrame;
-
- CGSize detailSize = [detailLabel sizeThatFits:fittingSize];
- detailSize.width = textContainerWidth;
- const CGFloat detailLabelMinX = 0;
- CGFloat detailLabelMinY = CGRectGetMaxY(titleFrame);
- if (titleLabel.text.length > 0 && detailLabel.text.length > 0) {
- detailLabelMinY += kInterLabelVerticalPadding;
- }
- CGPoint detailOrigin = CGPointMake(detailLabelMinX, detailLabelMinY);
- CGRect detailFrame = CGRectZero;
- detailFrame.origin = detailOrigin;
- detailFrame.size = detailSize;
- self.detailLabelFrame = detailFrame;
+ // Determine max x of text region
+ CGFloat textContainerMaxX = cellWidth - kHorizontalMargin;
+ if (displaysTrailingImageView) {
+ textContainerMaxX = CGRectGetMinX(trailingImageViewFrame) - kHorizontalMargin;
+ }
- textContainerHeight = CGRectGetMaxY(self.detailLabelFrame);
+ // Begin determining the frame of the view that contains the title and detail labels.
+ // The final frame of this view must be calculated further down because it depends on the frames
+ // of the labels it contains.
+ CGFloat textContainerMinY = kVerticalMarginMax;
+ CGFloat textContainerWidth = textContainerMaxX - textContainerMinX;
+ CGFloat textContainerHeight = 0;
- CGRect textContainerFrame = CGRectZero;
- CGPoint textContainerOrigin = CGPointMake(textContainerMinX, textContainerMinY);
- CGSize textContainerSize = CGSizeMake(textContainerWidth, textContainerHeight);
- textContainerFrame.origin = textContainerOrigin;
- textContainerFrame.size = textContainerSize;
- self.textContainerFrame = textContainerFrame;
+ CGSize fittingSize = CGSizeMake(textContainerWidth, CGFLOAT_MAX);
+
+ // Determine title label size and then frame within container view
+ CGSize titleSize = [titleLabel sizeThatFits:fittingSize];
+ titleSize.width = textContainerWidth;
+ CGFloat titleLabelMinX = 0;
+ CGFloat titleLabelMinY = 0;
+ CGPoint titleOrigin = CGPointMake(titleLabelMinX, titleLabelMinY);
+ titleLabelFrame.origin = titleOrigin;
+ titleLabelFrame.size = titleSize;
- BOOL hasOnlyTitleText = containsTitleText && !containsDetailText;
- BOOL shouldVerticallyCenterTitleText = hasOnlyTitleText && hasLeadingImage;
- if (shouldVerticallyCenterTitleText) {
- CGFloat leadingImageViewCenterY = CGRectGetMidY(self.leadingImageViewFrame);
- CGFloat textContainerCenterY = CGRectGetMidY(self.textContainerFrame);
- CGFloat difference = textContainerCenterY - leadingImageViewCenterY;
- CGRect offsetTextContainerRect = CGRectOffset(self.textContainerFrame, 0, -difference);
- BOOL willExtendPastMargin = offsetTextContainerRect.origin.y < kVerticalMarginMax;
- if (!willExtendPastMargin) {
- self.textContainerFrame = offsetTextContainerRect;
+ // Determine detail label size and then frame within container view
+ CGSize detailSize = [detailLabel sizeThatFits:fittingSize];
+ detailSize.width = textContainerWidth;
+ CGFloat detailLabelMinX = 0;
+ CGFloat detailLabelMinY = CGRectGetMaxY(titleLabelFrame);
+ if (titleLabel.text.length > 0 && detailLabel.text.length > 0) {
+ detailLabelMinY += kInterLabelVerticalPadding;
+ }
+ CGPoint detailOrigin = CGPointMake(detailLabelMinX, detailLabelMinY);
+ detailLabelFrame.origin = detailOrigin;
+ detailLabelFrame.size = detailSize;
+
+ // Determine title and detail label container view height and then frame
+ textContainerHeight = CGRectGetMaxY(detailLabelFrame);
+
+ CGPoint textContainerOrigin = CGPointMake(textContainerMinX, textContainerMinY);
+ CGSize textContainerSize = CGSizeMake(textContainerWidth, textContainerHeight);
+ textContainerFrame.origin = textContainerOrigin;
+ textContainerFrame.size = textContainerSize;
+
+ // Usually the image views are positioned at the top. However, this looks funny if there is only
+ // one line of text. If there is only one line of text, make it have the same center Y as the
+ // image views.
+ BOOL hasOnlyTitleText = containsTitleText && !containsDetailText;
+ BOOL shouldVerticallyCenterTitleText = hasOnlyTitleText && displaysLeadingImageView;
+ if (shouldVerticallyCenterTitleText) {
+ CGFloat leadingImageViewCenterY = CGRectGetMidY(leadingImageViewFrame);
+ CGFloat textContainerCenterY = CGRectGetMidY(textContainerFrame);
+ CGFloat difference = textContainerCenterY - leadingImageViewCenterY;
+ CGRect offsetTextContainerRect = CGRectOffset(textContainerFrame, 0, -difference);
+ BOOL willExtendPastMargin = offsetTextContainerRect.origin.y < kVerticalMarginMax;
+ if (!willExtendPastMargin) {
+ textContainerFrame = offsetTextContainerRect;
+ }
}
}
+
+ // Calculate the height of the cell.
+ CGFloat calculatedHeight = [self calculateHeightWithLeadingImageViewFrame:leadingImageViewFrame
+ trailingImageViewFrame:trailingImageViewFrame
+ textContainerFrame:textContainerFrame];
+
+ // Center the image views if the user has specified that they want the image views centered.
+ if (displaysLeadingImageView &&
+ leadingImageViewVerticalPosition == MDCSelfSizingStereoCellImageViewVerticalPositionCenter) {
+ CGFloat verticallyCenteredLeadingImageViewMinY =
+ (0.5f * calculatedHeight) - (0.5f * leadingImageViewSize.height);
+ leadingImageViewFrame =
+ CGRectMake(CGRectGetMinX(leadingImageViewFrame), verticallyCenteredLeadingImageViewMinY,
+ leadingImageViewSize.width, leadingImageViewSize.height);
+ }
+
+ if (displaysTrailingImageView &&
+ trailingImageViewVerticalPosition == MDCSelfSizingStereoCellImageViewVerticalPositionCenter) {
+ CGFloat verticallyCenteredTrailingImageViewMinY =
+ (0.5f * calculatedHeight) - (0.5f * trailingImageViewSize.height);
+ trailingImageViewFrame =
+ CGRectMake(CGRectGetMinX(trailingImageViewFrame), verticallyCenteredTrailingImageViewMinY,
+ trailingImageViewSize.width, trailingImageViewSize.height);
+ }
+
+ // Set the properties to be read by the cell.
+ self.textContainerFrame = textContainerFrame;
+ self.titleLabelFrame = titleLabelFrame;
+ self.detailLabelFrame = detailLabelFrame;
+ self.cellWidth = cellWidth;
+ self.leadingImageViewFrame = leadingImageViewFrame;
+ self.trailingImageViewFrame = trailingImageViewFrame;
+ self.calculatedHeight = calculatedHeight;
}
-- (CGFloat)calculateHeight {
+- (CGFloat)calculateHeightWithLeadingImageViewFrame:(CGRect)leadingImageViewFrame
+ trailingImageViewFrame:(CGRect)trailingImageViewFrame
+ textContainerFrame:(CGRect)textContainerFrame {
CGFloat maxHeight = 0;
CGFloat leadingImageViewRequiredVerticalSpace = 0;
CGFloat trailingImageViewRequiredVerticalSpace = 0;
CGFloat textContainerRequiredVerticalSpace = 0;
- if (!CGRectEqualToRect(self.leadingImageViewFrame, CGRectZero)) {
+ if (!CGRectEqualToRect(leadingImageViewFrame, CGRectZero)) {
leadingImageViewRequiredVerticalSpace =
- CGRectGetMaxY(self.leadingImageViewFrame) +
- [self verticalMarginForImageViewOfSize:self.leadingImageViewFrame.size];
+ CGRectGetMaxY(leadingImageViewFrame) +
+ [self verticalMarginForImageViewOfSize:leadingImageViewFrame.size];
if (leadingImageViewRequiredVerticalSpace > maxHeight) {
maxHeight = leadingImageViewRequiredVerticalSpace;
}
}
- if (!CGRectEqualToRect(self.trailingImageViewFrame, CGRectZero)) {
+ if (!CGRectEqualToRect(trailingImageViewFrame, CGRectZero)) {
trailingImageViewRequiredVerticalSpace =
- CGRectGetMaxY(self.trailingImageViewFrame) +
- [self verticalMarginForImageViewOfSize:self.trailingImageViewFrame.size];
+ CGRectGetMaxY(trailingImageViewFrame) +
+ [self verticalMarginForImageViewOfSize:trailingImageViewFrame.size];
if (trailingImageViewRequiredVerticalSpace > maxHeight) {
maxHeight = trailingImageViewRequiredVerticalSpace;
}
}
- if (!CGRectEqualToRect(self.textContainerFrame, CGRectZero)) {
- textContainerRequiredVerticalSpace =
- CGRectGetMaxY(self.textContainerFrame) + kVerticalMarginMax;
+ if (!CGRectEqualToRect(textContainerFrame, CGRectZero)) {
+ textContainerRequiredVerticalSpace = CGRectGetMaxY(textContainerFrame) + kVerticalMarginMax;
if (textContainerRequiredVerticalSpace > maxHeight) {
maxHeight = textContainerRequiredVerticalSpace;
}
@@ -202,8 +260,9 @@ - (CGSize)sizeForImage:(UIImage *)image {
}
- (CGFloat)verticalMarginForImageViewOfSize:(CGSize)size {
- CGFloat leadingImageHeight = size.height;
- if (leadingImageHeight > 0 && leadingImageHeight <= kImageSideLengthMedium) {
+ if (size.height == 0) {
+ return 0;
+ } else if (size.height > 0 && size.height <= kImageSideLengthMedium) {
return kVerticalMarginMax;
} else {
return kVerticalMarginMin;
diff --git a/components/List/tests/snapshot/MDCSelfSizingStereoCellSnapshotTests.m b/components/List/tests/snapshot/MDCSelfSizingStereoCellSnapshotTests.m
index 5a40df7253c..a643909469b 100644
--- a/components/List/tests/snapshot/MDCSelfSizingStereoCellSnapshotTests.m
+++ b/components/List/tests/snapshot/MDCSelfSizingStereoCellSnapshotTests.m
@@ -156,6 +156,29 @@ - (void)testCellWithTitleAndDetailAndImage {
[self generateSnapshotAndVerifyForView:self.collectionView];
}
+- (void)testCellWithTitleAndDetailAndVerticallyCenteredImage {
+ // When
+ MDCSelfSizingStereoCell *cell = [[MDCSelfSizingStereoCell alloc] init];
+ cell.titleLabel.text = @"This is a title label. This is a title label. This is a title label. "
+ @"This is a title label. This is a title label.";
+ cell.detailLabel.text = @"This is a detail label. This is a detail label. This is a detail "
+ @"label. This is a detail label. This is a detail label.";
+ cell.leadingImageView.image = [UIImage mdc_testImageOfSize:CGSizeMake(24, 24)
+ withStyle:MDCSnapshotTestImageStyleCheckerboard];
+ cell.trailingImageView.image = [UIImage mdc_testImageOfSize:CGSizeMake(24, 24)
+ withStyle:MDCSnapshotTestImageStyleRectangles];
+ cell.leadingImageViewVerticalPosition = MDCSelfSizingStereoCellImageViewVerticalPositionCenter;
+ cell.trailingImageViewVerticalPosition = MDCSelfSizingStereoCellImageViewVerticalPositionCenter;
+ self.arrayOfCells = @[ cell ];
+
+ CGSize cellSize = [cell systemLayoutSizeFittingSize:CGSizeMake(170, CGFLOAT_MAX)];
+ self.collectionView.frame = CGRectMake(0, 0, cellSize.width, cellSize.height);
+ self.collectionViewLayout.estimatedItemSize = cellSize;
+
+ // Then
+ [self generateSnapshotAndVerifyForView:self.collectionView];
+}
+
- (void)testCellWithDynamicTypeForContentSizeCategoryExtraSmallEnabledForTitleAndDetail {
if (@available(iOS 10.0, *)) {
// Given
@@ -280,6 +303,8 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
dequeuedCell.leadingImageView.image = cell.leadingImageView.image;
dequeuedCell.trailingImageView.image = cell.trailingImageView.image;
dequeuedCell.mdc_adjustsFontForContentSizeCategory = cell.mdc_adjustsFontForContentSizeCategory;
+ dequeuedCell.leadingImageViewVerticalPosition = cell.leadingImageViewVerticalPosition;
+ dequeuedCell.trailingImageViewVerticalPosition = cell.trailingImageViewVerticalPosition;
[dequeuedCell removeFromSuperview];
return dequeuedCell;
}
diff --git a/components/ScalableFontDescriptor/src/MDCScalableFontDescriptor.m b/components/ScalableFontDescriptor/src/MDCScalableFontDescriptor.m
index 10e692d70c3..29aeb77f9cf 100644
--- a/components/ScalableFontDescriptor/src/MDCScalableFontDescriptor.m
+++ b/components/ScalableFontDescriptor/src/MDCScalableFontDescriptor.m
@@ -54,7 +54,9 @@ - (NSString *)description {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
textStyles = @[
+#if !TARGET_OS_TV
UIFontTextStyleLargeTitle,
+#endif
UIFontTextStyleTitle1,
UIFontTextStyleTitle2,
UIFontTextStyleTitle3,
diff --git a/components/TextControls/README.md b/components/TextControls/README.md
index 5d12c51436e..6e7d3aada96 100644
--- a/components/TextControls/README.md
+++ b/components/TextControls/README.md
@@ -412,14 +412,6 @@ import MaterialComponents.MaterialTextControls_OutlinedTextFieldsTheming
From there, call either the default or the error theming method.
-#### Objective-C
-
-```objc
-MDCContainerScheme *containerScheme = ... // Set up a container scheme;
-[textField applyThemeWithScheme:self.containerScheme]; // Default theming method
-[textField applyErrorThemeWithScheme:self.containerScheme]; // Error theming method
-```
-
#### Swift
```swift
@@ -427,4 +419,12 @@ let containerScheme = ... // Set up a container scheme
textField.applyTheme(withScheme: self.containerScheme) // Default theming method
textField.applyErrorTheme(withScheme: self.containerScheme) // Error theming method
```
+
+#### Objective-C
+
+```objc
+MDCContainerScheme *containerScheme = ... // Set up a container scheme;
+[textField applyThemeWithScheme:self.containerScheme]; // Default theming method
+[textField applyErrorThemeWithScheme:self.containerScheme]; // Error theming method
+```
diff --git a/demos/supplemental/RemoteImageServiceForMDCDemos.podspec b/demos/supplemental/RemoteImageServiceForMDCDemos.podspec
index ad270c1879c..647d91e5b50 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.3.0"
+ s.version = "119.4.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"
diff --git a/scripts/release b/scripts/release
index 492669c1e30..1f9df689c7d 100755
--- a/scripts/release
+++ b/scripts/release
@@ -351,18 +351,24 @@ publish_release() {
enforce_changelog_version
- ghtoken=$(cat ~/.gh.json | grep "github_token" | cut -d'"' -f4)
+ ghtoken="$(cut -d " " -f 2 <<< $(cat ~/.config/gh/hosts.yml | grep "oauth_token: "))"
if [ -z "$ghtoken" ]; then
- echo "Must be authenticated with gh."
+ echo "Error: Unable to find a token in ~/.config/gh/hosts.yml."
echo
- echo "Install gh by running:"
+ echo "You must authenticate with the official GitHub command line interface tool:"
+ echo "https://github.com/cli/cli"
echo
- echo " npm install -g gh"
+ echo "Not to be confused with the following unofficial (and no longer maintained) GitHub command line tool that this script used to use:"
+ echo "https://github.com/node-gh/gh"
echo
- echo "And then get an auth token by running:"
- echo
- echo " gh user"
+ echo "To proceed, do the following:"
+ echo "1. Install the official command line tool"
+ echo "2. Authenticate using 'gh auth login'"
+ echo " a. When it asks 'What account do you want to log into?', choose 'GitHub.com'"
+ echo " b. When it asks 'How would you like to authenticate?', choose 'Login with a web browser'"
+ echo "3. Re-run the scripts/release command"
echo
+ echo "You will probably have to delete or move the old gh to make way for the new (official) one."
exit 1
fi