blob: 285772b74f9940a8abdbd9ff7726671dd0b8ab53 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h"
#include "base/check.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
#import "ios/chrome/common/ui/elements/gradient_view.h"
#import "ios/chrome/common/ui/util/button_util.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#include "ios/chrome/common/ui/util/dynamic_type_util.h"
#import "ios/chrome/common/ui/util/pointer_interaction_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
NSString* const kConfirmationAlertMoreInfoAccessibilityIdentifier =
@"kConfirmationAlertMoreInfoAccessibilityIdentifier";
NSString* const kConfirmationAlertTitleAccessibilityIdentifier =
@"kConfirmationAlertTitleAccessibilityIdentifier";
NSString* const kConfirmationAlertSecondaryTitleAccessibilityIdentifier =
@"kConfirmationAlertSecondaryTitleAccessibilityIdentifier";
NSString* const kConfirmationAlertSubtitleAccessibilityIdentifier =
@"kConfirmationAlertSubtitleAccessibilityIdentifier";
NSString* const kConfirmationAlertPrimaryActionAccessibilityIdentifier =
@"kConfirmationAlertPrimaryActionAccessibilityIdentifier";
NSString* const kConfirmationAlertSecondaryActionAccessibilityIdentifier =
@"kConfirmationAlertSecondaryActionAccessibilityIdentifier";
NSString* const kConfirmationAlertTertiaryActionAccessibilityIdentifier =
@"kConfirmationAlertTertiaryActionAccessibilityIdentifier";
namespace {
constexpr CGFloat kActionsBottomMargin = 10;
// Gradient height.
constexpr CGFloat kGradientHeight = 40.;
constexpr CGFloat kScrollViewBottomInsets = 20;
constexpr CGFloat kStackViewSpacing = 8;
constexpr CGFloat kStackViewSpacingAfterIllustration = 27;
// The multiplier used when in regular horizontal size class.
constexpr CGFloat kSafeAreaMultiplier = 0.65;
constexpr CGFloat kContentOptimalWidth = 327;
// The size of the symbol image.
constexpr NSInteger kSymbolBadgeImagePointSize = 13;
// The name of the checkmark symbol in filled circle.
NSString* const kCheckmarkSymbol = @"checkmark.circle.fill";
// Properties of the favicon.
constexpr CGFloat kFaviconCornerRadius = 13;
constexpr CGFloat kFaviconShadowOffsetX = 0;
constexpr CGFloat kFaviconShadowOffsetY = 0;
constexpr CGFloat kFaviconShadowRadius = 6;
constexpr CGFloat kFaviconShadowOpacity = 0.1;
// Length of each side of the favicon frame (which contains the favicon and the
// surrounding whitespace).
constexpr CGFloat kFaviconFrameSideLength = 60;
// Length of each side of the favicon.
constexpr CGFloat kFaviconSideLength = 30;
// Length of each side of the favicon badge.
constexpr CGFloat kFaviconBadgeSideLength = 24;
} // namespace
@interface ConfirmationAlertViewController () <UIToolbarDelegate>
// References to the UI properties that need to be updated when the trait
// collection changes.
@property(nonatomic, strong) UIButton* primaryActionButton;
@property(nonatomic, strong) UIButton* secondaryActionButton;
@property(nonatomic, strong) UIButton* tertiaryActionButton;
@property(nonatomic, strong) UIToolbar* topToolbar;
@property(nonatomic, strong) UIImageView* imageView;
@property(nonatomic, strong) UIView* imageContainerView;
@property(nonatomic, strong) NSLayoutConstraint* imageViewAspectRatioConstraint;
@end
@implementation ConfirmationAlertViewController
#pragma mark - Public
- (instancetype)init {
self = [super init];
if (self) {
_customSpacingAfterImage = kStackViewSpacingAfterIllustration;
_showDismissBarButton = YES;
_dismissBarButtonSystemItem = UIBarButtonSystemItemDone;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
if (self.hasTopToolbar) {
self.topToolbar = [self createTopToolbar];
[self.view addSubview:self.topToolbar];
}
if (self.imageEnclosedWithShadowAndBadge) {
// The image view is set within the helper method.
self.imageContainerView = [self createImageContainerViewWithShadowAndBadge];
} else {
// The image container and the image view are the same.
self.imageView = [self createImageView];
self.imageContainerView = self.imageView;
}
UILabel* title = [self createTitleLabel];
UILabel* subtitle = [self createSubtitleLabel];
NSArray* stackSubviews = nil;
if ([self.secondaryTitleString length] != 0) {
UILabel* secondaryTitle = [self createSecondaryTitleLabel];
stackSubviews =
@[ self.imageContainerView, title, secondaryTitle, subtitle ];
} else {
stackSubviews = @[ self.imageContainerView, title, subtitle ];
}
DCHECK(stackSubviews);
UIStackView* stackView =
[self createStackViewWithArrangedSubviews:stackSubviews];
UIScrollView* scrollView = [self createScrollView];
[scrollView addSubview:stackView];
[self.view addSubview:scrollView];
self.view.preservesSuperviewLayoutMargins = YES;
UILayoutGuide* margins = self.view.layoutMarginsGuide;
if (self.hasTopToolbar) {
// Toolbar constraints to the top.
AddSameConstraintsToSides(
self.topToolbar, self.view.safeAreaLayoutGuide,
LayoutSides::kTrailing | LayoutSides::kTop | LayoutSides::kLeading);
}
// Constraint top/bottom of the stack view to the scroll view. This defines
// the content area. No need to contraint horizontally as we don't want
// horizontal scroll.
[NSLayoutConstraint activateConstraints:@[
[stackView.topAnchor constraintEqualToAnchor:scrollView.topAnchor],
[stackView.bottomAnchor constraintEqualToAnchor:scrollView.bottomAnchor
constant:-kScrollViewBottomInsets]
]];
// Scroll View constraints to the height of its content. This allows to center
// the scroll view.
NSLayoutConstraint* heightConstraint = [scrollView.heightAnchor
constraintEqualToAnchor:scrollView.contentLayoutGuide.heightAnchor];
// UILayoutPriorityDefaultHigh is the default priority for content
// compression. Setting this lower avoids compressing the content of the
// scroll view.
heightConstraint.priority = UILayoutPriorityDefaultHigh - 1;
heightConstraint.active = YES;
[NSLayoutConstraint activateConstraints:@[
[stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
// Width Scroll View constraint for regular mode.
[stackView.widthAnchor
constraintGreaterThanOrEqualToAnchor:margins.widthAnchor
multiplier:kSafeAreaMultiplier],
// Disable horizontal scrolling.
[stackView.widthAnchor
constraintLessThanOrEqualToAnchor:margins.widthAnchor],
]];
// This constraint is added to enforce that the content width should be as
// close to the optimal width as possible, within the range already activated
// for "stackView.widthAnchor" previously, with a higher priority.
NSLayoutConstraint* contentLayoutGuideWidthConstraint =
[stackView.widthAnchor constraintEqualToConstant:kContentOptimalWidth];
contentLayoutGuideWidthConstraint.priority = UILayoutPriorityRequired - 1;
contentLayoutGuideWidthConstraint.active = YES;
// The bottom anchor for the scroll view.
NSLayoutYAxisAnchor* scrollViewBottomAnchor =
self.view.safeAreaLayoutGuide.bottomAnchor;
BOOL hasActionButton = self.primaryActionString ||
self.secondaryActionString ||
self.tertiaryActionString;
if (hasActionButton) {
UIView* actionStackView = [self createActionStackView];
[self.view addSubview:actionStackView];
// Add a low priority width constraints to make sure that the buttons are
// taking as much width as they can.
CGFloat extraBottomMargin =
self.secondaryActionString ? 0 : kActionsBottomMargin;
NSLayoutConstraint* lowPriorityWidthConstraint =
[actionStackView.widthAnchor
constraintEqualToConstant:kContentOptimalWidth];
lowPriorityWidthConstraint.priority = UILayoutPriorityDefaultHigh + 1;
// Also constrain the bottom of the action stack view to the bottom of the
// safe area, but with a lower priority, so that the action stack view is
// put as close to the bottom as possible.
NSLayoutConstraint* actionBottomConstraint = [actionStackView.bottomAnchor
constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
actionBottomConstraint.priority = UILayoutPriorityDefaultLow;
actionBottomConstraint.active = YES;
[NSLayoutConstraint activateConstraints:@[
[actionStackView.leadingAnchor
constraintGreaterThanOrEqualToAnchor:scrollView.leadingAnchor],
[actionStackView.trailingAnchor
constraintLessThanOrEqualToAnchor:scrollView.trailingAnchor],
[actionStackView.centerXAnchor
constraintEqualToAnchor:self.view.centerXAnchor],
[actionStackView.widthAnchor
constraintEqualToAnchor:stackView.widthAnchor],
[actionStackView.bottomAnchor
constraintLessThanOrEqualToAnchor:self.view.bottomAnchor
constant:-kActionsBottomMargin -
extraBottomMargin],
[actionStackView.bottomAnchor
constraintLessThanOrEqualToAnchor:self.view.safeAreaLayoutGuide
.bottomAnchor
constant:-extraBottomMargin],
lowPriorityWidthConstraint
]];
scrollViewBottomAnchor = actionStackView.topAnchor;
GradientView* gradientView = [self createGradientView];
[self.view addSubview:gradientView];
[NSLayoutConstraint activateConstraints:@[
[gradientView.bottomAnchor
constraintEqualToAnchor:actionStackView.topAnchor],
[gradientView.leadingAnchor
constraintEqualToAnchor:scrollView.leadingAnchor],
[gradientView.trailingAnchor
constraintEqualToAnchor:scrollView.trailingAnchor],
[gradientView.heightAnchor constraintEqualToConstant:kGradientHeight],
]];
}
[NSLayoutConstraint activateConstraints:@[
[scrollView.bottomAnchor
constraintLessThanOrEqualToAnchor:scrollViewBottomAnchor
constant:-kScrollViewBottomInsets],
[scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[scrollView.trailingAnchor
constraintEqualToAnchor:self.view.trailingAnchor],
]];
NSLayoutYAxisAnchor* scrollViewTopAnchor;
CGFloat scrollViewTopConstant = 0;
if (self.hasTopToolbar) {
scrollViewTopAnchor = self.topToolbar.bottomAnchor;
} else {
scrollViewTopAnchor = self.view.safeAreaLayoutGuide.topAnchor;
scrollViewTopConstant = self.customSpacingBeforeImageIfNoToolbar;
}
if (self.topAlignedLayout) {
[scrollView.topAnchor constraintEqualToAnchor:scrollViewTopAnchor
constant:scrollViewTopConstant]
.active = YES;
} else {
[scrollView.topAnchor
constraintGreaterThanOrEqualToAnchor:scrollViewTopAnchor
constant:scrollViewTopConstant]
.active = YES;
// Scroll View constraint to the vertical center.
NSLayoutConstraint* centerYConstraint = [scrollView.centerYAnchor
constraintEqualToAnchor:margins.centerYAnchor];
// This needs to be lower than the height constraint, so it's deprioritized.
// If this breaks, the scroll view is still constrained to the top toolbar
// and the bottom safe area or button.
centerYConstraint.priority = heightConstraint.priority - 1;
centerYConstraint.active = YES;
}
if (!self.imageHasFixedSize) {
// Constrain the image to the scroll view size and its aspect ratio.
[self.imageView
setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
forAxis:
UILayoutConstraintAxisHorizontal];
[self.imageView
setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisVertical];
CGFloat imageAspectRatio =
self.imageView.image.size.width / self.imageView.image.size.height;
self.imageViewAspectRatioConstraint = [self.imageView.widthAnchor
constraintEqualToAnchor:self.imageView.heightAnchor
multiplier:imageAspectRatio];
self.imageViewAspectRatioConstraint.active = YES;
}
}
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
// Update fonts for specific content sizes.
if (previousTraitCollection.preferredContentSizeCategory !=
self.traitCollection.preferredContentSizeCategory) {
self.primaryActionButton.titleLabel.font =
PreferredFontForTextStyleWithMaxCategory(
UIFontTextStyleHeadline,
self.traitCollection.preferredContentSizeCategory,
UIContentSizeCategoryExtraExtraExtraLarge);
}
// Update constraints for different size classes.
BOOL hasNewHorizontalSizeClass =
previousTraitCollection.horizontalSizeClass !=
self.traitCollection.horizontalSizeClass;
BOOL hasNewVerticalSizeClass = previousTraitCollection.verticalSizeClass !=
self.traitCollection.verticalSizeClass;
if (hasNewHorizontalSizeClass || hasNewVerticalSizeClass) {
[self.view setNeedsUpdateConstraints];
}
}
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
[self.view setNeedsUpdateConstraints];
}
- (void)viewLayoutMarginsDidChange {
[super viewLayoutMarginsDidChange];
[self.view setNeedsUpdateConstraints];
}
- (void)updateViewConstraints {
BOOL isVerticalCompact =
self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact;
// Hiding the image causes the UIStackView to change the image's height to 0.
// Because its width and height are related, if the aspect ratio constraint
// is active, the image's width also goes to 0, which causes the stack view
// width to become 0 too.
[self.imageView setHidden:isVerticalCompact];
[self.imageContainerView setHidden:isVerticalCompact];
self.imageViewAspectRatioConstraint.active = !isVerticalCompact;
// Allow toolbar to update its height based on new layout.
[self.topToolbar invalidateIntrinsicContentSize];
[super updateViewConstraints];
}
#pragma mark - UIToolbarDelegate
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
return UIBarPositionTopAttached;
}
#pragma mark - Private
// Handle taps on the dismiss button.
- (void)didTapDismissBarButton {
DCHECK(self.showDismissBarButton);
if ([self.actionHandler
respondsToSelector:@selector(confirmationAlertDismissAction)]) {
[self.actionHandler confirmationAlertDismissAction];
}
}
// Handle taps on the help button.
- (void)didTapHelpButton {
if ([self.actionHandler
respondsToSelector:@selector(confirmationAlertLearnMoreAction)]) {
[self.actionHandler confirmationAlertLearnMoreAction];
}
}
// Handle taps on the primary action button.
- (void)didTapPrimaryActionButton {
[self.actionHandler confirmationAlertPrimaryAction];
}
// Handle taps on the secondary action button
- (void)didTapSecondaryActionButton {
DCHECK(self.secondaryActionString);
if ([self.actionHandler
respondsToSelector:@selector(confirmationAlertSecondaryAction)]) {
[self.actionHandler confirmationAlertSecondaryAction];
}
}
- (void)didTapTertiaryActionButton {
DCHECK(self.tertiaryActionString);
if ([self.actionHandler
respondsToSelector:@selector(confirmationAlertTertiaryAction)]) {
[self.actionHandler confirmationAlertTertiaryAction];
}
}
// Helper to create the top toolbar.
- (UIToolbar*)createTopToolbar {
UIToolbar* topToolbar = [[UIToolbar alloc] init];
topToolbar.translucent = NO;
[topToolbar setShadowImage:[[UIImage alloc] init]
forToolbarPosition:UIBarPositionAny];
[topToolbar setBarTintColor:[UIColor colorNamed:kBackgroundColor]];
topToolbar.delegate = self;
NSMutableArray* toolbarItems = [[NSMutableArray alloc] init];
if (self.helpButtonAvailable) {
UIBarButtonItem* helpButton =
[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"help_icon"]
style:UIBarButtonItemStylePlain
target:self
action:@selector(didTapHelpButton)];
[toolbarItems addObject:helpButton];
if (self.helpButtonAccessibilityLabel) {
helpButton.isAccessibilityElement = YES;
helpButton.accessibilityLabel = self.helpButtonAccessibilityLabel;
}
helpButton.accessibilityIdentifier =
kConfirmationAlertMoreInfoAccessibilityIdentifier;
// Set the help button as the left button item so it can be used as a
// popover anchor.
_helpButton = helpButton;
}
UIBarButtonItem* spacer = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil];
[toolbarItems addObject:spacer];
if (self.showDismissBarButton) {
UIBarButtonItem* dismissButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:self.dismissBarButtonSystemItem
target:self
action:@selector(didTapDismissBarButton)];
[toolbarItems addObject:dismissButton];
}
topToolbar.translatesAutoresizingMaskIntoConstraints = NO;
[topToolbar setItems:toolbarItems];
return topToolbar;
}
- (void)setImage:(UIImage*)image {
_image = image;
_imageView.image = image;
}
// Helper to create the image view.
- (UIImageView*)createImageView {
UIImageView* imageView = [[UIImageView alloc] initWithImage:self.image];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
return imageView;
}
// Helper to create the image view enclosed in a frame with a shadow and a
// corner badge with a green checkmark. |self.imageView| is set in this method.
- (UIView*)createImageContainerViewWithShadowAndBadge {
UIImageView* faviconBadgeView = [[UIImageView alloc] init];
faviconBadgeView.translatesAutoresizingMaskIntoConstraints = NO;
UIImageSymbolConfiguration* configuration = [UIImageSymbolConfiguration
configurationWithPointSize:kSymbolBadgeImagePointSize
weight:UIImageSymbolWeightMedium
scale:UIImageSymbolScaleMedium];
faviconBadgeView.image = [UIImage systemImageNamed:kCheckmarkSymbol
withConfiguration:configuration];
faviconBadgeView.tintColor = [UIColor colorNamed:kGreenColor];
UIImageView* faviconView = [[UIImageView alloc] initWithImage:self.image];
faviconView.translatesAutoresizingMaskIntoConstraints = NO;
faviconView.contentMode = UIViewContentModeScaleAspectFit;
UIView* frameView = [[UIView alloc] init];
frameView.translatesAutoresizingMaskIntoConstraints = NO;
frameView.backgroundColor = [UIColor colorNamed:kBackgroundColor];
frameView.layer.cornerRadius = kFaviconCornerRadius;
frameView.layer.shadowOffset =
CGSizeMake(kFaviconShadowOffsetX, kFaviconShadowOffsetY);
frameView.layer.shadowRadius = kFaviconShadowRadius;
frameView.layer.shadowOpacity = kFaviconShadowOpacity;
[frameView addSubview:faviconView];
UIView* containerView = [[UIView alloc] init];
[containerView addSubview:frameView];
[containerView addSubview:faviconBadgeView];
[NSLayoutConstraint activateConstraints:@[
// Size constraints.
[frameView.widthAnchor constraintEqualToConstant:kFaviconFrameSideLength],
[frameView.heightAnchor constraintEqualToConstant:kFaviconFrameSideLength],
[faviconView.widthAnchor constraintEqualToConstant:kFaviconSideLength],
[faviconView.heightAnchor constraintEqualToConstant:kFaviconSideLength],
[faviconBadgeView.widthAnchor
constraintEqualToConstant:kFaviconBadgeSideLength],
[faviconBadgeView.heightAnchor
constraintEqualToConstant:kFaviconBadgeSideLength],
// Badge is on the upper right corner of the frame.
[frameView.topAnchor
constraintEqualToAnchor:faviconBadgeView.centerYAnchor],
[frameView.trailingAnchor
constraintEqualToAnchor:faviconBadgeView.centerXAnchor],
// Favicon is centered in the frame.
[frameView.centerXAnchor constraintEqualToAnchor:faviconView.centerXAnchor],
[frameView.centerYAnchor constraintEqualToAnchor:faviconView.centerYAnchor],
// Frame and badge define the whole view returned by this method.
[containerView.leadingAnchor
constraintEqualToAnchor:frameView.leadingAnchor
constant:-kFaviconBadgeSideLength / 2],
[containerView.bottomAnchor constraintEqualToAnchor:frameView.bottomAnchor],
[containerView.topAnchor
constraintEqualToAnchor:faviconBadgeView.topAnchor],
[containerView.trailingAnchor
constraintEqualToAnchor:faviconBadgeView.trailingAnchor],
]];
self.imageView = faviconView;
return containerView;
}
// Creates a label with subtitle label defaults.
- (UILabel*)createLabel {
UILabel* label = [[UILabel alloc] init];
label.numberOfLines = 0;
label.textAlignment = NSTextAlignmentCenter;
label.translatesAutoresizingMaskIntoConstraints = NO;
label.adjustsFontForContentSizeCategory = YES;
return label;
}
// Helper to create the title label.
- (UILabel*)createTitleLabel {
if (!self.titleTextStyle) {
self.titleTextStyle = UIFontTextStyleTitle1;
}
UILabel* title = [[UILabel alloc] init];
title.numberOfLines = 0;
UIFontDescriptor* descriptor = [UIFontDescriptor
preferredFontDescriptorWithTextStyle:self.titleTextStyle];
UIFont* font = [UIFont systemFontOfSize:descriptor.pointSize
weight:UIFontWeightBold];
UIFontMetrics* fontMetrics =
[UIFontMetrics metricsForTextStyle:self.titleTextStyle];
title.font = [fontMetrics scaledFontForFont:font];
title.textColor = [UIColor colorNamed:kTextPrimaryColor];
title.text = self.titleString;
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
title.adjustsFontForContentSizeCategory = YES;
title.accessibilityIdentifier =
kConfirmationAlertTitleAccessibilityIdentifier;
return title;
}
// Helper to create the title description label.
- (UILabel*)createSecondaryTitleLabel {
UILabel* secondaryTitle = [self createLabel];
secondaryTitle.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleTitle2];
secondaryTitle.text = self.secondaryTitleString;
secondaryTitle.textColor = [UIColor colorNamed:kTextPrimaryColor];
secondaryTitle.accessibilityIdentifier =
kConfirmationAlertSecondaryTitleAccessibilityIdentifier;
return secondaryTitle;
}
// Helper to create the subtitle label.
- (UILabel*)createSubtitleLabel {
UILabel* subtitle = [self createLabel];
subtitle.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
subtitle.text = self.subtitleString;
subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor];
subtitle.accessibilityIdentifier =
kConfirmationAlertSubtitleAccessibilityIdentifier;
return subtitle;
}
- (BOOL)hasTopToolbar {
return self.helpButtonAvailable || self.showDismissBarButton;
}
// Helper to create the scroll view.
- (UIScrollView*)createScrollView {
UIScrollView* scrollView = [[UIScrollView alloc] init];
scrollView.alwaysBounceVertical = NO;
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
return scrollView;
}
// Helper to create the gradient view.
- (GradientView*)createGradientView {
GradientView* gradientView = [[GradientView alloc] init];
gradientView.translatesAutoresizingMaskIntoConstraints = NO;
return gradientView;
}
// Helper to create the stack view.
- (UIStackView*)createStackViewWithArrangedSubviews:
(NSArray<UIView*>*)subviews {
UIStackView* stackView =
[[UIStackView alloc] initWithArrangedSubviews:subviews];
[stackView setCustomSpacing:self.customSpacingAfterImage
afterView:self.imageView];
if (self.imageHasFixedSize) {
stackView.alignment = UIStackViewAlignmentCenter;
} else {
stackView.alignment = UIStackViewAlignmentFill;
}
stackView.axis = UILayoutConstraintAxisVertical;
stackView.translatesAutoresizingMaskIntoConstraints = NO;
stackView.spacing = kStackViewSpacing;
return stackView;
}
- (UIView*)createActionStackView {
UIStackView* actionStackView = [[UIStackView alloc] init];
actionStackView.alignment = UIStackViewAlignmentFill;
actionStackView.axis = UILayoutConstraintAxisVertical;
actionStackView.translatesAutoresizingMaskIntoConstraints = NO;
if (self.primaryActionString) {
self.primaryActionButton = [self createPrimaryActionButton];
[actionStackView addArrangedSubview:self.primaryActionButton];
}
if (self.secondaryActionString) {
self.secondaryActionButton = [self createSecondaryActionButton];
[actionStackView addArrangedSubview:self.secondaryActionButton];
}
if (self.tertiaryActionString) {
self.tertiaryActionButton = [self createTertiaryButton];
[actionStackView addArrangedSubview:self.tertiaryActionButton];
}
return actionStackView;
}
// Helper to create the primary action button.
- (UIButton*)createPrimaryActionButton {
UIButton* primaryActionButton = PrimaryActionButton(YES);
[primaryActionButton addTarget:self
action:@selector(didTapPrimaryActionButton)
forControlEvents:UIControlEventTouchUpInside];
[primaryActionButton setTitle:self.primaryActionString
forState:UIControlStateNormal];
primaryActionButton.accessibilityIdentifier =
kConfirmationAlertPrimaryActionAccessibilityIdentifier;
primaryActionButton.titleLabel.adjustsFontSizeToFitWidth = YES;
return primaryActionButton;
}
// Helper to create the primary action button.
- (UIButton*)createSecondaryActionButton {
DCHECK(self.secondaryActionString);
UIButton* secondaryActionButton =
[UIButton buttonWithType:UIButtonTypeSystem];
[secondaryActionButton addTarget:self
action:@selector(didTapSecondaryActionButton)
forControlEvents:UIControlEventTouchUpInside];
[secondaryActionButton setTitle:self.secondaryActionString
forState:UIControlStateNormal];
secondaryActionButton.contentEdgeInsets =
UIEdgeInsetsMake(kButtonVerticalInsets, 0, kButtonVerticalInsets, 0);
[secondaryActionButton setBackgroundColor:[UIColor clearColor]];
UIColor* titleColor = [UIColor colorNamed:kBlueColor];
[secondaryActionButton setTitleColor:titleColor
forState:UIControlStateNormal];
secondaryActionButton.titleLabel.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleBody];
secondaryActionButton.titleLabel.adjustsFontForContentSizeCategory = NO;
secondaryActionButton.translatesAutoresizingMaskIntoConstraints = NO;
secondaryActionButton.accessibilityIdentifier =
kConfirmationAlertSecondaryActionAccessibilityIdentifier;
secondaryActionButton.titleLabel.adjustsFontSizeToFitWidth = YES;
secondaryActionButton.pointerInteractionEnabled = YES;
secondaryActionButton.pointerStyleProvider =
CreateOpaqueButtonPointerStyleProvider();
return secondaryActionButton;
}
- (UIButton*)createTertiaryButton {
DCHECK(self.tertiaryActionString);
UIButton* tertiaryActionButton = [UIButton buttonWithType:UIButtonTypeSystem];
[tertiaryActionButton addTarget:self
action:@selector(didTapTertiaryActionButton)
forControlEvents:UIControlEventTouchUpInside];
[tertiaryActionButton setTitle:self.tertiaryActionString
forState:UIControlStateNormal];
tertiaryActionButton.contentEdgeInsets =
UIEdgeInsetsMake(kButtonVerticalInsets, 0, kButtonVerticalInsets, 0);
[tertiaryActionButton setBackgroundColor:[UIColor clearColor]];
UIColor* titleColor = [UIColor colorNamed:kBlueColor];
[tertiaryActionButton setTitleColor:titleColor forState:UIControlStateNormal];
tertiaryActionButton.titleLabel.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleBody];
tertiaryActionButton.titleLabel.adjustsFontForContentSizeCategory = NO;
tertiaryActionButton.translatesAutoresizingMaskIntoConstraints = NO;
tertiaryActionButton.accessibilityIdentifier =
kConfirmationAlertTertiaryActionAccessibilityIdentifier;
tertiaryActionButton.pointerInteractionEnabled = YES;
tertiaryActionButton.pointerStyleProvider =
CreateOpaqueButtonPointerStyleProvider();
return tertiaryActionButton;
}
@end