// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/settings/cells/settings_check_cell.h"
#import "base/check.h"
#import "base/ios/ios_util.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"
namespace {
const CGFloat kSymbolSize = 22;
}
@interface SettingsCheckCell ()
// The image view for the trailing image.
@property(nonatomic, strong) UIImageView* trailingImageView;
// UIActivityIndicatorView spinning while check is running.
@property(nonatomic, readonly, strong)
UIActivityIndicatorView* activityIndicator;
// Image view for the leading image.
@property(nonatomic, strong) UIImageView* leadingImageView;
// Constraint that is used to define trailing text constraint without
// `trailingImageView` `activityIndicator` and `infoButton`.
@property(nonatomic, strong)
NSLayoutConstraint* textNoTrailingContentsConstraint;
// Constraint that is used to define trailing text constraint with either
// `trailingImageView` or `activityIndicator` or `infoButton` showing.
@property(nonatomic, strong)
NSLayoutConstraint* textWithTrailingContentsConstraint;
// Constraint used for leading text constraint without `leadingImage`.
@property(nonatomic, strong) NSLayoutConstraint* textNoLeadingImageConstraint;
// Constraint used for leading text constraint with `leadingImage` showing.
@property(nonatomic, strong) NSLayoutConstraint* textWithLeadingImageConstraint;
@end
@implementation SettingsCheckCell {
UIView* _leadingIconBackground;
}
@synthesize textLabel = _textLabel;
@synthesize detailTextLabel = _detailTextLabel;
- (instancetype)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString*)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UIView* contentView = self.contentView;
// Attributes of row contents in order or appearance (if present).
// `_leadingImageView` attributes
_leadingIconBackground = [[UIView alloc] init];
_leadingIconBackground.translatesAutoresizingMaskIntoConstraints = NO;
_leadingIconBackground.hidden = NO;
[contentView addSubview:_leadingIconBackground];
_leadingImageView = [[UIImageView alloc] init];
_leadingImageView.translatesAutoresizingMaskIntoConstraints = NO;
_leadingImageView.tintColor = [UIColor colorNamed:kTextPrimaryColor];
_leadingImageView.contentMode = UIViewContentModeCenter;
[contentView addSubview:_leadingImageView];
AddSameCenterConstraints(_leadingImageView, _leadingIconBackground);
// Text attributes.
// `_textLabel` attributes.
_textLabel = [[UILabel alloc] init];
_textLabel.translatesAutoresizingMaskIntoConstraints = NO;
_textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
_textLabel.adjustsFontForContentSizeCategory = YES;
_textLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
[contentView addSubview:_textLabel];
// `detailText` attributes.
_detailTextLabel = [[UILabel alloc] init];
_detailTextLabel.numberOfLines = 0;
_detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
_detailTextLabel.font =
[UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
_detailTextLabel.adjustsFontForContentSizeCategory = YES;
_detailTextLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
[contentView addSubview:_detailTextLabel];
// Only `_trailingImageView` or `_activityIndicator` or `_infoButton` is
// shown, not all at once. `trailingImage` attributes.
_trailingImageView = [[UIImageView alloc] init];
_trailingImageView.translatesAutoresizingMaskIntoConstraints = NO;
_trailingImageView.tintColor = [UIColor colorNamed:kTextPrimaryColor];
_trailingImageView.hidden = YES;
[contentView addSubview:_trailingImageView];
// `activityIndictor` attributes.
// Creates default activity indicator. Color depends on appearance.
_activityIndicator = [[UIActivityIndicatorView alloc] init];
_activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
_activityIndicator.hidden = YES;
[contentView addSubview:_activityIndicator];
// `_infoButton` attribues.
_infoButton = [UIButton buttonWithType:UIButtonTypeSystem];
_infoButton.translatesAutoresizingMaskIntoConstraints = NO;
_infoButton.hidden = YES;
UIImage* image = DefaultSymbolWithPointSize(kInfoCircleSymbol, kSymbolSize);
[_infoButton setImage:image forState:UIControlStateNormal];
[_infoButton setTintColor:[UIColor colorNamed:kBlueColor]];
[contentView addSubview:_infoButton];
// Constraints.
UILayoutGuide* textLayoutGuide = [[UILayoutGuide alloc] init];
[self.contentView addLayoutGuide:textLayoutGuide];
_textNoTrailingContentsConstraint = [textLayoutGuide.trailingAnchor
constraintEqualToAnchor:contentView.trailingAnchor
constant:-kTableViewHorizontalSpacing];
_textWithTrailingContentsConstraint = [textLayoutGuide.trailingAnchor
constraintEqualToAnchor:_trailingImageView.leadingAnchor
constant:-kTableViewImagePadding];
_textNoLeadingImageConstraint = [textLayoutGuide.leadingAnchor
constraintEqualToAnchor:contentView.leadingAnchor
constant:kTableViewHorizontalSpacing];
_textWithLeadingImageConstraint = [textLayoutGuide.leadingAnchor
constraintEqualToAnchor:_leadingIconBackground.trailingAnchor
constant:kTableViewImagePadding];
NSLayoutConstraint* heightConstraint = [self.contentView.heightAnchor
constraintGreaterThanOrEqualToConstant:kChromeTableViewCellHeight];
// Set up the constraints assuming that the icon image and activity
// indicator are hidden.
[NSLayoutConstraint activateConstraints:@[
heightConstraint,
_textNoTrailingContentsConstraint,
// Constraints for `_trailingImageView` (same position as
// `_activityIndictor`).
[_trailingImageView.trailingAnchor
constraintEqualToAnchor:self.contentView.trailingAnchor
constant:-kTableViewHorizontalSpacing],
[_trailingImageView.widthAnchor
constraintEqualToConstant:kTableViewIconImageSize],
[_trailingImageView.heightAnchor
constraintEqualToAnchor:_trailingImageView.widthAnchor],
[_trailingImageView.centerYAnchor
constraintEqualToAnchor:textLayoutGuide.centerYAnchor],
[_trailingImageView.leadingAnchor
constraintEqualToAnchor:_activityIndicator.leadingAnchor],
// Constraints for `_infoButton` (same position as
// `_trailingImageView`).
[_infoButton.trailingAnchor
constraintEqualToAnchor:self.contentView.trailingAnchor
constant:-kTableViewHorizontalSpacing],
[_infoButton.widthAnchor
constraintEqualToConstant:kTableViewIconImageSize],
[_infoButton.heightAnchor
constraintEqualToAnchor:_infoButton.widthAnchor],
[_infoButton.centerYAnchor
constraintEqualToAnchor:textLayoutGuide.centerYAnchor],
[_infoButton.leadingAnchor
constraintEqualToAnchor:_activityIndicator.leadingAnchor],
// Constraints for `_activityIndictor` (same position as
// `_trailingImageView`).
[_activityIndicator.trailingAnchor
constraintEqualToAnchor:self.contentView.trailingAnchor
constant:-kTableViewHorizontalSpacing],
[_activityIndicator.widthAnchor
constraintEqualToConstant:kTableViewIconImageSize],
[_activityIndicator.heightAnchor
constraintEqualToAnchor:_activityIndicator.widthAnchor],
[_activityIndicator.centerYAnchor
constraintEqualToAnchor:textLayoutGuide.centerYAnchor],
// Constraints for `_leadingIconBackground`.
[_leadingIconBackground.leadingAnchor
constraintEqualToAnchor:self.contentView.leadingAnchor
constant:kTableViewHorizontalSpacing],
[_leadingIconBackground.widthAnchor
constraintEqualToConstant:kTableViewIconImageSize],
[_leadingIconBackground.heightAnchor
constraintEqualToAnchor:_leadingIconBackground.widthAnchor],
[_leadingIconBackground.centerYAnchor
constraintEqualToAnchor:textLayoutGuide.centerYAnchor],
// Constraints for `_textLabel` and `_detailTextLabel`.
[textLayoutGuide.centerYAnchor
constraintEqualToAnchor:self.contentView.centerYAnchor],
[textLayoutGuide.leadingAnchor
constraintEqualToAnchor:_textLabel.leadingAnchor],
[textLayoutGuide.leadingAnchor
constraintEqualToAnchor:_detailTextLabel.leadingAnchor],
[textLayoutGuide.trailingAnchor
constraintEqualToAnchor:_textLabel.trailingAnchor],
[textLayoutGuide.trailingAnchor
constraintEqualToAnchor:_detailTextLabel.trailingAnchor],
[textLayoutGuide.topAnchor constraintEqualToAnchor:_textLabel.topAnchor],
[textLayoutGuide.bottomAnchor
constraintEqualToAnchor:_detailTextLabel.bottomAnchor],
[_textLabel.bottomAnchor
constraintEqualToAnchor:_detailTextLabel.topAnchor],
]];
// Make sure there are top and bottom margins of at least `margin`.
AddOptionalVerticalPadding(self.contentView, textLayoutGuide,
kTableViewTwoLabelsCellVerticalSpacing);
}
return self;
}
- (void)showActivityIndicator {
if (!self.activityIndicator.hidden)
return;
self.trailingImageView.hidden = YES;
self.infoButton.hidden = YES;
self.activityIndicator.hidden = NO;
[self.activityIndicator startAnimating];
[self updateTrailingImageTextConstraints];
}
- (void)hideActivityIndicator {
if (self.activityIndicator.hidden)
return;
[self.activityIndicator stopAnimating];
self.activityIndicator.hidden = YES;
[self updateTrailingImageTextConstraints];
}
- (void)setTrailingImage:(UIImage*)trailingImage
withTintColor:(UIColor*)trailingImageColor {
self.trailingImageView.tintColor = trailingImageColor;
BOOL hidden = !trailingImage;
self.trailingImageView.image = trailingImage;
if (hidden == self.trailingImageView.hidden)
return;
self.trailingImageView.hidden = hidden;
if (!hidden) {
self.activityIndicator.hidden = YES;
self.infoButton.hidden = YES;
}
[self updateTrailingImageTextConstraints];
}
- (void)setLeadingIconImage:(UIImage*)image
tintColor:(UIColor*)tintColor
backgroundColor:(UIColor*)backgroundColor
cornerRadius:(CGFloat)cornerRadius {
self.leadingImageView.image = image;
self.leadingImageView.tintColor = tintColor;
_leadingIconBackground.backgroundColor = backgroundColor;
_leadingIconBackground.layer.cornerRadius = cornerRadius;
BOOL hidden = !image;
_leadingIconBackground.hidden = hidden;
// Update the leading text constraint based on `image` being provided.
if (hidden) {
_textWithLeadingImageConstraint.active = NO;
_textNoLeadingImageConstraint.active = YES;
} else {
_textNoLeadingImageConstraint.active = NO;
_textWithLeadingImageConstraint.active = YES;
}
}
- (void)setInfoButtonHidden:(BOOL)hidden {
if (hidden == self.infoButton.hidden)
return;
self.infoButton.hidden = hidden;
if (hidden) {
self.accessibilityCustomActions = nil;
} else {
self.accessibilityCustomActions = [self createAccessibilityActions];
self.trailingImageView.hidden = YES;
self.activityIndicator.hidden = YES;
}
[self updateTrailingImageTextConstraints];
}
- (void)setInfoButtonEnabled:(BOOL)enabled {
self.infoButton.enabled = enabled;
if (enabled) {
[self.infoButton setTintColor:[UIColor colorNamed:kBlueColor]];
} else {
[self.infoButton setTintColor:[UIColor colorNamed:kTextSecondaryColor]];
}
}
#pragma mark - Private Methods
// Updates the constraints around the trailing image for when `trailingImage` or
// `activityIndicator` or `infoButton` is shown or hidden.
- (void)updateTrailingImageTextConstraints {
// Active proper `textLayoutGuide` trailing constraint to show
// `trailingImageView` or `activityIndicator` or `infoButton`.
if (self.activityIndicator.hidden && self.trailingImageView.hidden &&
self.infoButton.hidden) {
_textWithTrailingContentsConstraint.active = NO;
_textNoTrailingContentsConstraint.active = YES;
} else {
_textNoTrailingContentsConstraint.active = NO;
_textWithTrailingContentsConstraint.active = YES;
}
}
#pragma mark - UITableViewCell
- (void)prepareForReuse {
[super prepareForReuse];
self.textLabel.text = nil;
self.accessibilityCustomActions = nil;
[self setInfoButtonEnabled:YES];
[self.infoButton removeTarget:nil
action:nil
forControlEvents:UIControlEventAllEvents];
self.detailTextLabel.text = nil;
self.accessibilityTraits = UIAccessibilityTraitNone;
[self setTrailingImage:nil withTintColor:nil];
[self setLeadingIconImage:nil
tintColor:nil
backgroundColor:nil
cornerRadius:0];
[self hideActivityIndicator];
}
#pragma mark - Accessibility
// Creates custom accessibility actions.
- (NSArray*)createAccessibilityActions {
UIAccessibilityCustomAction* tapButtonAction =
[[UIAccessibilityCustomAction alloc]
initWithName:l10n_util::GetNSString(
IDS_IOS_INFO_BUTTON_ACCESSIBILITY_HINT)
target:self
selector:@selector(handleInfoButtonTapForCell)];
return @[ tapButtonAction ];
}
// Handles accessibility action for tapping outside the info button.
- (void)handleInfoButtonTapForCell {
[self.infoButton sendActionsForControlEvents:UIControlEventTouchUpInside];
}
@end