Add a private API for passing a custom title icon view. It can be used for passing a custom animation into the title.
MODIFY
components/Dialogs/src/MDCAlertController.m
components/Dialogs/src/MDCAlertController.m
#import "MDCAlertController.h"
-#import <MDFInternationalization/MDFInternationalization.h>
-
+#import "MaterialButtons.h"
#import "MDCAlertControllerView.h"
#import "MDCDialogPresentationController.h"
#import "MDCDialogTransitionController.h"
-#import "MaterialButtons.h"
-#import "MaterialMath.h"
-#import "MaterialTypography.h"
#import "UIViewController+MaterialDialogs.h"
+#import "MaterialTypography.h"
+#import "MaterialMath.h"
+#import <MDFInternationalization/MDFInternationalization.h>
+
#import "private/MDCAlertActionManager.h"
+#import "private/MDCAlertController+Custom.h"
#import "private/MDCAlertControllerView+Private.h"
#import "private/MaterialDialogsStrings.h"
#import "private/MaterialDialogsStrings_table.h"
@@ -78,6 +79,7 @@ static NSString *const kMaterialDialogsBundle = @"MaterialDialogs.bundle";
@property(nonatomic, nullable, weak) MDCAlertControllerView *alertView;
@property(nonatomic, strong) MDCDialogTransitionController *transitionController;
@property(nonatomic, nonnull, strong) MDCAlertActionManager *actionManager;
+@property(nonatomic, nullable, strong) UIView *titleIconView;
- (nonnull instancetype)initWithTitle:(nullable NSString *)title
message:(nullable NSString *)message;
@@ -97,6 +99,7 @@ static NSString *const kMaterialDialogsBundle = @"MaterialDialogs.bundle";
@synthesize mdc_overrideBaseElevation = _mdc_overrideBaseElevation;
@synthesize mdc_elevationDidChangeBlock = _mdc_elevationDidChangeBlock;
@synthesize adjustsFontForContentSizeCategory = _adjustsFontForContentSizeCategory;
+@synthesize titleIconView = _titleIconView;
+ (instancetype)alertControllerWithTitle:(nullable NSString *)alertTitle
message:(nullable NSString *)message {
@@ -333,6 +336,13 @@ static NSString *const kMaterialDialogsBundle = @"MaterialDialogs.bundle";
}
}
+- (void)setTitleIconView:(UIView *)titleIconView {
+ _titleIconView = titleIconView;
+ if (self.alertView) {
+ self.alertView.titleIconView = titleIconView;
+ }
+}
+
- (void)setTitleIconTintColor:(UIColor *)titleIconTintColor {
_titleIconTintColor = titleIconTintColor;
if (self.alertView) {
@@ -516,6 +526,7 @@ static NSString *const kMaterialDialogsBundle = @"MaterialDialogs.bundle";
self.alertView.titleAlignment = self.titleAlignment;
self.alertView.titleIcon = self.titleIcon;
self.alertView.titleIconTintColor = self.titleIconTintColor;
+ self.alertView.titleIconView = self.titleIconView;
self.alertView.cornerRadius = self.cornerRadius;
self.alertView.enableRippleBehavior = self.enableRippleBehavior;

MODIFY
components/Dialogs/src/MDCAlertControllerView.h
components/Dialogs/src/MDCAlertControllerView.h
@property(nonatomic, strong, nullable) UIImage *titleIcon;
@property(nonatomic, strong, nullable) UIColor *titleIconTintColor;
+/**
+ An optional custom icon view above the title of the alert.
+
+ @note This property is intended to be used to provide a custom implementation of the title icon
+ view. If the intention is to just display a `UIImage`, use `setTitleIcon:` API instead. If
+ 'titleIcon' is set, 'titleIconView' is ignored.
+ */
+@property(nonatomic, strong, nullable) UIView *titleIconView;
+
@property(nonatomic, strong, nullable) UIFont *messageFont UI_APPEARANCE_SELECTOR;
@property(nonatomic, strong, nullable) UIColor *messageColor UI_APPEARANCE_SELECTOR;

ADD
components/Dialogs/src/private/MDCAlertController+Custom.h
+// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "MaterialDialogs.h"
+
+@interface MDCAlertController (Custom)
+
+/**
+ An optional custom icon view above the title of the alert.
+
+ @note This property is intended to be used to provide a custom implementation of the title icon
+ view. If the intention is to just display a `UIImage`, use `setTitleIcon:` API instead. If
+ 'titleIcon' is set, 'titleIconView' is ignored.
+ */
+@property(nonatomic, strong, nullable) UIView *titleIconView;
+
+@end

MODIFY
components/Dialogs/src/private/MDCAlertControllerView+Private.h
components/Dialogs/src/private/MDCAlertControllerView+Private.h
@property(nonatomic, nonnull, strong) UILabel *messageLabel;
@property(nonatomic, nullable, strong) UIView *accessoryView;
-@property(nonatomic, nullable, strong) UIImageView *titleIconImageView;
-
@property(nonatomic, nullable, weak) MDCAlertActionManager *actionManager;
/** The scroll view that holds the @c titleLabel. */

MODIFY
components/Dialogs/src/private/MDCAlertControllerView+Private.m
components/Dialogs/src/private/MDCAlertControllerView+Private.m
@@ -44,6 +44,7 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
@interface MDCAlertControllerView ()
@property(nonatomic, getter=isVerticalActionsLayout) BOOL verticalActionsLayout;
+@property(nonatomic, nullable, strong) UIImageView *titleIconImageView;
@end
@@ -205,7 +206,16 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
if (titleIcon == nil) {
[self.titleIconImageView removeFromSuperview];
self.titleIconImageView = nil;
- } else if (self.titleIconImageView == nil) {
+ [self setNeedsLayout];
+ return;
+ }
+
+ if (self.titleIconView != nil) {
+ [self.titleIconView removeFromSuperview];
+ self.titleIconView = nil;
+ }
+
+ if (self.titleIconImageView == nil) {
self.titleIconImageView = [[UIImageView alloc] initWithImage:titleIcon];
self.titleIconImageView.contentMode = UIViewContentModeScaleAspectFit;
[self.titleScrollView addSubview:self.titleIconImageView];
@@ -222,6 +232,25 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
self.titleIconImageView.tintColor = titleIconTintColor;
}
+- (void)setTitleIconView:(UIView *)titleIconView {
+ if (self.titleIconImageView != nil) {
+ NSLog(@"Warning: unintended use of the API. The following APIs are not expected to be used"
+ "together: 'setTitleIconView:' and `setTitleIcon:` API. Please set either, but not "
+ "both at the same time. If 'titleIcon' is set, 'titleIconView' is ignored.");
+ return;
+ }
+ if (_titleIconView == nil || ![_titleIconView isEqual:titleIconView]) {
+ if (_titleIconView != nil) {
+ [_titleIconView removeFromSuperview];
+ }
+ _titleIconView = titleIconView;
+ if (_titleIconView != nil) {
+ [self.titleScrollView addSubview:_titleIconView];
+ }
+ [self.titleScrollView setNeedsLayout];
+ }
+}
+
- (NSString *)message {
return self.messageLabel.text;
}
@@ -423,13 +452,19 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
: 0.0f;
}
-- (CGSize)titleIconSize {
- return (self.titleIconImageView != nil) ? self.titleIconImageView.image.size : CGSizeZero;
+- (CGSize)titleIconViewSize {
+ CGSize titleIconViewSize = CGSizeZero;
+ if (self.titleIconImageView != nil) {
+ titleIconViewSize = self.titleIconImageView.image.size;
+ } else if (self.titleIconView != nil) {
+ titleIconViewSize = self.titleIconView.frame.size;
+ }
+ return titleIconViewSize;
}
- (CGFloat)titleTop {
return MDCDialogContentInsets.top + [self contentTitleIconVerticalPadding] +
- [self titleIconSize].height;
+ [self titleIconViewSize].height;
}
- (CGRect)titleFrameWithTitleSize:(CGSize)titleSize {
@@ -444,21 +479,21 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
}
- (CGRect)titleIconFrameWithTitleSize:(CGSize)titleSize {
- CGSize titleIconSize = [self titleIconSize];
+ CGSize titleIconViewSize = [self titleIconViewSize];
CGRect titleFrame = [self titleFrameWithTitleSize:titleSize];
// match the titleIcon alignment to the title alignment
CGFloat titleIconLeftPadding = MDCDialogContentInsets.left;
if (self.titleAlignment == NSTextAlignmentCenter) {
titleIconLeftPadding =
- CGRectGetMinX(titleFrame) + (CGRectGetWidth(titleFrame) - titleIconSize.width) / 2.0f;
+ CGRectGetMinX(titleFrame) + (CGRectGetWidth(titleFrame) - titleIconViewSize.width) / 2.0f;
} else if (self.titleAlignment == NSTextAlignmentRight ||
(self.titleAlignment == NSTextAlignmentNatural &&
[self mdf_effectiveUserInterfaceLayoutDirection] ==
UIUserInterfaceLayoutDirectionRightToLeft)) {
- titleIconLeftPadding = CGRectGetMaxX(titleFrame) - titleIconSize.width;
+ titleIconLeftPadding = CGRectGetMaxX(titleFrame) - titleIconViewSize.width;
}
CGRect titleIconFrame = CGRectMake(titleIconLeftPadding, MDCDialogContentInsets.top,
- titleIconSize.width, titleIconSize.height);
+ titleIconViewSize.width, titleIconViewSize.height);
return titleIconFrame;
}
@@ -515,7 +550,7 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
CGFloat contentWidth = MAX(MAX(titleSize.width, messageSize.width), accessoryViewSize.width) +
MDCDialogContentInsets.left + MDCDialogContentInsets.right;
- CGFloat contentHeight = MDCDialogContentInsets.top + [self titleIconSize].height +
+ CGFloat contentHeight = MDCDialogContentInsets.top + [self titleIconViewSize].height +
[self contentTitleIconVerticalPadding] + titleSize.height +
[self contentInternalVerticalPadding];
@@ -620,9 +655,12 @@ static const CGFloat MDCDialogMessageOpacity = 0.54f;
MDCDialogContentInsets.left, CGRectGetMaxY(messageFrame) + contentAccessoryVerticalPadding,
accessoryViewSize.width, accessoryViewSize.height);
+ CGRect titleIconImageViewRect = [self titleIconFrameWithTitleSize:titleSize];
if (self.titleIconImageView != nil) {
// Match the title icon alignment to the title alignment.
- self.titleIconImageView.frame = [self titleIconFrameWithTitleSize:titleSize];
+ self.titleIconImageView.frame = titleIconImageViewRect;
+ } else if (self.titleIconView != nil) {
+ self.titleIconView.frame = titleIconImageViewRect;
}
self.titleLabel.frame = titleFrame;

MODIFY
components/Dialogs/tests/unit/MDCAlertControllerCustomizationTests.m
components/Dialogs/tests/unit/MDCAlertControllerCustomizationTests.m
#import <XCTest/XCTest.h>
+@interface MDCAlertControllerView (Testing)
+@property(nonatomic, nullable, strong) UIImageView *titleIconImageView;
+@end
+
@interface MDCAlertControllerCustomizationTests : XCTestCase
@property(nonatomic, nullable, strong) MDCAlertController *alert;
@property(nonatomic, nullable, weak) MDCAlertControllerView *alertView;

Add a private API for passing a custom title icon view. It can be used for passing a custom animation into the title.

PiperOrigin-RevId: 292824168