// Copyright 2018 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/authentication/views/identity_view.h"
#import "base/check.h"
#import "base/check_op.h"
#import "base/notreached.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/util/constraints_ui_util.h"
namespace {
// List of constants related to the IdentityViewStyle.
typedef struct {
const CGFloat avatarLeadingMargin;
const CGFloat avatarSize;
const CGFloat titleOffset;
const CGFloat minimumTopMargin;
const CGFloat minimumBottomMargin;
} StyleValues;
const StyleValues kDefaultStyle = {
16., /* avatarLeadingMargin */
40., /* avatarSize */
4., /* titleOffset */
12., /* minimumTopMargin */
12., /* minimumBottomMargin */
};
const StyleValues kSignInChooseryStyle = {
24., /* avatarLeadingMargin */
40., /* avatarSize */
4., /* titleOffset */
7., /* minimumTopMargin */
7., /* minimumBottomMargin */
};
const StyleValues kConsistencyStyle = {
16., /* avatarLeadingMargin */
30., /* avatarSize */
0., /* titleOffset */
10., /* minimumTopMargin */
8., /* minimumBottomMargin */
};
// Distances/margins.
constexpr CGFloat kHorizontalAvatarLeadingMargin = 16.;
} // namespace
@interface IdentityView ()
// Avatar.
@property(nonatomic, strong) UIImageView* avatarView;
// Contains the name if it exists, otherwise it contains the email.
@property(nonatomic, strong) UILabel* title;
// Contains the email if the name exists, otherwise it is hidden.
@property(nonatomic, strong) UILabel* subtitle;
// Constraints if the name exists.
@property(nonatomic, strong) NSLayoutConstraint* titleConstraintForNameAndEmail;
// Constraints if the name doesn't exist.
@property(nonatomic, strong) NSLayoutConstraint* titleConstraintForEmailOnly;
// Constraints to update when `self.minimumTopMargin` is updated.
@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* topConstraints;
// Constraints to update when `self.minimumBottomMargin` is updated.
@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* bottomConstraints;
// Constraints for the avatar size.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* avatarSizeConstraints;
// Leading margin constraint for the avatar.
@property(nonatomic, strong) NSLayoutConstraint* avatarLeadingMarginConstraint;
@end
@implementation IdentityView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = NO;
// Avatar view.
_avatarView = [[UIImageView alloc] init];
_avatarView.translatesAutoresizingMaskIntoConstraints = NO;
_avatarView.clipsToBounds = YES;
[self addSubview:_avatarView];
// Title.
_title = [[UILabel alloc] init];
_title.adjustsFontForContentSizeCategory = YES;
_title.translatesAutoresizingMaskIntoConstraints = NO;
_title.numberOfLines = 1;
_title.textColor = [UIColor colorNamed:kTextPrimaryColor];
_title.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
_title.adjustsFontSizeToFitWidth = NO;
_title.lineBreakMode = NSLineBreakByTruncatingTail;
// Subtitle.
_subtitle = [[UILabel alloc] init];
_subtitle.adjustsFontForContentSizeCategory = YES;
_subtitle.translatesAutoresizingMaskIntoConstraints = NO;
_subtitle.numberOfLines = 1;
_subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor];
_subtitle.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
_subtitle.adjustsFontSizeToFitWidth = NO;
_subtitle.lineBreakMode = NSLineBreakByTruncatingTail;
// Text container.
UIStackView* contentView =
[[UIStackView alloc] initWithArrangedSubviews:@[ _title, _subtitle ]];
contentView.axis = UILayoutConstraintAxisVertical;
contentView.distribution = UIStackViewDistributionEqualSpacing;
contentView.alignment = UIStackViewAlignmentLeading;
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:contentView];
// Layout constraints.
_avatarLeadingMarginConstraint = [_avatarView.leadingAnchor
constraintEqualToAnchor:self.leadingAnchor
constant:kDefaultStyle.avatarLeadingMargin];
[NSLayoutConstraint activateConstraints:@[
[contentView.leadingAnchor
constraintEqualToAnchor:_avatarView.trailingAnchor
constant:kHorizontalAvatarLeadingMargin],
_avatarLeadingMarginConstraint,
]];
_avatarSizeConstraints = @[
[_avatarView.heightAnchor
constraintEqualToConstant:kDefaultStyle.avatarSize],
[_avatarView.widthAnchor
constraintEqualToConstant:kDefaultStyle.avatarSize],
];
[NSLayoutConstraint activateConstraints:_avatarSizeConstraints];
AddSameCenterYConstraint(self, _avatarView);
AddSameConstraintsToSides(_title, _subtitle,
LayoutSides::kLeading | LayoutSides::kTrailing);
_titleConstraintForNameAndEmail =
[_subtitle.topAnchor constraintEqualToAnchor:_title.bottomAnchor
constant:kDefaultStyle.titleOffset];
_titleConstraintForEmailOnly =
[contentView.bottomAnchor constraintEqualToAnchor:_title.bottomAnchor];
[NSLayoutConstraint activateConstraints:@[
[self.centerYAnchor constraintEqualToAnchor:contentView.centerYAnchor],
[contentView.leadingAnchor constraintEqualToAnchor:_title.leadingAnchor],
[contentView.leadingAnchor
constraintEqualToAnchor:_subtitle.leadingAnchor],
[contentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[contentView.topAnchor constraintEqualToAnchor:_title.topAnchor],
[contentView.bottomAnchor constraintEqualToAnchor:_subtitle.bottomAnchor],
]];
_topConstraints = @[
[_avatarView.topAnchor
constraintGreaterThanOrEqualToAnchor:self.topAnchor
constant:kDefaultStyle.minimumTopMargin],
[_title.topAnchor
constraintGreaterThanOrEqualToAnchor:self.topAnchor
constant:kDefaultStyle.minimumTopMargin],
];
[NSLayoutConstraint activateConstraints:_topConstraints];
_bottomConstraints = @[
[self.bottomAnchor
constraintGreaterThanOrEqualToAnchor:_avatarView.bottomAnchor
constant:kDefaultStyle
.minimumBottomMargin],
[self.bottomAnchor
constraintGreaterThanOrEqualToAnchor:_subtitle.bottomAnchor
constant:kDefaultStyle
.minimumBottomMargin],
];
[NSLayoutConstraint activateConstraints:_bottomConstraints];
// Initialize the style.
[self updateStyle];
}
return self;
}
#pragma mark - Setter
- (void)setAvatar:(UIImage*)avatarImage {
if (!avatarImage) {
self.avatarView.image = nil;
} else {
const StyleValues* style = [self styleValues];
DCHECK_EQ(avatarImage.size.width, style->avatarSize);
DCHECK_EQ(avatarImage.size.height, style->avatarSize);
self.avatarView.image = avatarImage;
self.avatarView.layer.cornerRadius = style->avatarSize / 2.0;
}
}
- (void)setTitle:(NSString*)title subtitle:(NSString*)subtitle {
DCHECK(title);
self.title.text = title;
if (!subtitle.length) {
self.titleConstraintForNameAndEmail.active = NO;
self.titleConstraintForEmailOnly.active = YES;
self.subtitle.hidden = YES;
} else {
self.titleConstraintForEmailOnly.active = NO;
self.titleConstraintForNameAndEmail.active = YES;
self.subtitle.hidden = NO;
self.subtitle.text = subtitle;
}
}
- (void)setTitleColor:(UIColor*)color {
self.title.textColor = color;
}
#pragma mark - properties
- (void)setTitleFont:(UIFont*)titleFont {
self.title.font = titleFont;
}
- (UIFont*)titleFont {
return self.title.font;
}
- (void)setSubtitleFont:(UIFont*)subtitleFont {
self.subtitle.font = subtitleFont;
}
- (UIFont*)subtitleFont {
return self.subtitleFont;
}
- (void)setStyle:(IdentityViewStyle)style {
if (_style == style)
return;
_style = style;
[self updateStyle];
}
#pragma mark - private
// Returns the default style values according to `self.style`.
- (const StyleValues*)styleValues {
switch (self.style) {
case IdentityViewStyleDefault:
return &kDefaultStyle;
case IdentityViewStyleIdentityChooser:
return &kSignInChooseryStyle;
case IdentityViewStyleConsistency:
return &kConsistencyStyle;
}
NOTREACHED_IN_MIGRATION();
return nullptr;
}
// Updates the current view according the style.
- (void)updateStyle {
const StyleValues* style = [self styleValues];
switch (self.style) {
case IdentityViewStyleDefault:
self.titleFont =
[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
self.subtitleFont =
[UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
break;
case IdentityViewStyleIdentityChooser:
self.titleFont =
[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
self.subtitleFont =
[UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
break;
case IdentityViewStyleConsistency:
self.titleFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
self.subtitleFont =
[UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
break;
}
DCHECK(style);
self.avatarLeadingMarginConstraint.constant = style->avatarLeadingMargin;
for (NSLayoutConstraint* constraint in self.avatarSizeConstraints) {
constraint.constant = style->avatarSize;
}
self.avatarView.layer.cornerRadius = style->avatarSize / 2.;
for (NSLayoutConstraint* constraint in self.topConstraints) {
constraint.constant = style->minimumTopMargin;
}
for (NSLayoutConstraint* constraint in self.bottomConstraints) {
constraint.constant = style->minimumBottomMargin;
}
self.titleConstraintForNameAndEmail.constant = style->titleOffset;
}
@end